## https://sploitus.com/exploit?id=8A0223F9-3B1C-51F4-B8B7-19DA055D0753
# Multi Cosmos DB API
A .NET 8 RESTful API demonstrating how to connect to multiple Azure Cosmos DB databases using Entity Framework Core with a generic, reusable interface pattern.
## Features
- **Multiple Cosmos DB Connections**: Connects to two separate Cosmos databases (ProductsDb and OrdersDb)
- **Generic Repository Pattern**: Reusable repository interface that can be extended for additional databases
- **Entity Framework Core**: Uses EF Core's Cosmos DB provider for data access
- **RESTful API**: Clean REST endpoints with proper HTTP status codes
- **Comprehensive Testing**: Unit tests and controller tests using xUnit and Moq
- **Swagger Integration**: Built-in API documentation with Swagger/OpenAPI
## Architecture
### Project Structure
```
MultiCosmosApi/
โโโ Controllers/ # API Controllers
โ โโโ ProductsController.cs
โ โโโ OrdersController.cs
โโโ Data/ # Database Contexts
โ โโโ BaseCosmosDbContext.cs
โ โโโ ProductDbContext.cs
โ โโโ OrderDbContext.cs
โโโ DTOs/ # Data Transfer Objects
โ โโโ ProductDto.cs
โ โโโ OrderDto.cs
โโโ Interfaces/ # Generic Interfaces
โ โโโ ICosmosDbContext.cs
โ โโโ IRepository.cs
โโโ Models/ # Domain Models
โ โโโ BaseEntity.cs
โ โโโ Product.cs
โ โโโ Order.cs
โโโ Repositories/ # Repository Implementations
โโโ GenericRepository.cs
MultiCosmosApi.Tests/
โโโ ControllerTests/ # Controller Unit Tests
โ โโโ ProductsControllerTests.cs
โ โโโ OrdersControllerTests.cs
โโโ RepositoryTests/ # Repository Unit Tests
โโโ GenericRepositoryTests.cs
```
### Key Components
#### 1. Generic Interface Pattern
**ICosmosDbContext**: Base interface for all Cosmos DB contexts
```csharp
public interface ICosmosDbContext : IDisposable
{
string DatabaseName { get; }
Task SaveChangesAsync(CancellationToken cancellationToken = default);
DbSet Set() where TEntity : class;
}
```
**IRepository**: Generic repository interface for CRUD operations
```csharp
public interface IRepository where T : class
{
Task> GetAllAsync();
Task GetByIdAsync(string id);
Task> FindAsync(Expression> predicate);
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(string id);
Task ExistsAsync(Expression> predicate);
}
```
#### 2. Base Context Class
`BaseCosmosDbContext` provides common functionality for all Cosmos DB contexts:
- Abstract `DatabaseName` property for identification
- Template method pattern with `ConfigureModel()` for entity-specific configuration
- Implements `ICosmosDbContext` for dependency injection
#### 3. Dependency Injection
The `Program.cs` configures multiple database contexts:
```csharp
// Register ProductDbContext (Database 1)
builder.Services.AddDbContext(options =>
{
options.UseCosmos(cosmosEndpoint, cosmosKey, "ProductsDb");
});
// Register OrderDbContext (Database 2)
builder.Services.AddDbContext(options =>
{
options.UseCosmos(cosmosEndpoint, cosmosKey, "OrdersDb");
});
// Register repositories with specific contexts
builder.Services.AddScoped>(provider =>
{
var context = provider.GetRequiredService();
return new GenericRepository(context);
});
builder.Services.AddScoped>(provider =>
{
var context = provider.GetRequiredService();
return new GenericRepository(context);
});
```
## Prerequisites
- .NET 8.0 SDK or later
- Azure Cosmos DB account (or Cosmos DB Emulator for local development)
- Visual Studio 2022 / VS Code / JetBrains Rider (optional)
## Configuration
Update `appsettings.json` with your Cosmos DB connection details:
```json
{
"CosmosDb": {
"Endpoint": "https://your-cosmos-account.documents.azure.com:443/",
"Key": "your-primary-key-here",
"ProductsDatabase": "ProductsDb",
"OrdersDatabase": "OrdersDb"
}
}
```
**For local development**, the default configuration uses the Cosmos DB Emulator:
- Endpoint: `https://localhost:8081`
- Key: `C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==`
## Running the Application
### Using .NET CLI
```bash
# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run the API
cd MultiCosmosApi
dotnet run
```
The API will be available at:
- HTTPS: `https://localhost:5001`
- HTTP: `http://localhost:5000`
- Swagger UI: `https://localhost:5001/swagger`
### Using Visual Studio
1. Open `MultiCosmosApi.sln`
2. Set `MultiCosmosApi` as the startup project
3. Press F5 to run
## API Endpoints
### Products API (Database 1)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/products` | Get all products |
| GET | `/api/products/{id}` | Get product by ID |
| GET | `/api/products/category/{category}` | Get products by category |
| POST | `/api/products` | Create a new product |
| PUT | `/api/products/{id}` | Update a product |
| DELETE | `/api/products/{id}` | Delete a product |
### Orders API (Database 2)
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/orders` | Get all orders |
| GET | `/api/orders/{id}` | Get order by ID |
| GET | `/api/orders/customer/{email}` | Get orders by customer email |
| POST | `/api/orders` | Create a new order |
| PATCH | `/api/orders/{id}/status` | Update order status |
| DELETE | `/api/orders/{id}` | Delete an order |
### Example Requests
**Create a Product:**
```json
POST /api/products
{
"name": "Laptop",
"description": "High-performance laptop",
"price": 1299.99,
"category": "Electronics",
"stockQuantity": 50
}
```
**Create an Order:**
```json
POST /api/orders
{
"customerName": "John Doe",
"customerEmail": "john.doe@example.com",
"items": [
{
"productId": "prod-123",
"productName": "Laptop",
"quantity": 1,
"unitPrice": 1299.99
}
]
}
```
## Running Tests
### Run all tests
```bash
dotnet test
```
### Run tests with coverage
```bash
dotnet test /p:CollectCoverage=true
```
### Test Coverage
The test suite includes:
- **Repository Tests**: 13 test cases covering all CRUD operations, edge cases, and error scenarios
- **Controller Tests**: 18 test cases covering all API endpoints, validation, and error handling
## Extending to Additional Databases
To add a new Cosmos database:
### 1. Create a new Entity Model
```csharp
public class Customer : BaseEntity
{
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
// Additional properties...
}
```
### 2. Create a new DbContext
```csharp
public class CustomerDbContext : BaseCosmosDbContext
{
public override string DatabaseName => "CustomersDb";
public DbSet Customers { get; set; }
public CustomerDbContext(DbContextOptions options)
: base(options) { }
protected override void ConfigureModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity.ToContainer("Customers");
entity.HasPartitionKey(c => c.Email);
entity.HasKey(c => c.Id);
});
}
}
```
### 3. Register in Program.cs
```csharp
builder.Services.AddDbContext(options =>
{
options.UseCosmos(cosmosEndpoint, cosmosKey, "CustomersDb");
});
builder.Services.AddScoped>(provider =>
{
var context = provider.GetRequiredService();
return new GenericRepository(context);
});
```
### 4. Create Controller and DTOs
Follow the same pattern as `ProductsController` and `OrdersController`.
## Design Patterns Used
1. **Repository Pattern**: Abstracts data access logic
2. **Generic Repository**: Reusable repository for all entities
3. **Dependency Injection**: Loose coupling and testability
4. **Template Method**: Base context with overridable configuration
5. **DTO Pattern**: Separation of domain models and API contracts
## Best Practices Implemented
- โ Separation of concerns with layered architecture
- โ Generic interfaces for code reusability
- โ Comprehensive error handling and logging
- โ Input validation using Data Annotations
- โ Proper HTTP status codes
- โ Async/await throughout for better performance
- โ Unit testing with high coverage
- โ API documentation with Swagger
- โ Configuration externalization
## Cosmos DB Partition Keys
The application uses the following partition key strategies:
- **Products**: Partitioned by `Category` (optimal for category-based queries)
- **Orders**: Partitioned by `CustomerEmail` (optimal for customer-specific queries)
## Troubleshooting
### Cosmos DB Emulator Issues
If you're using the Cosmos DB Emulator:
1. Ensure the emulator is running
2. Trust the SSL certificate (for HTTPS)
3. Verify the endpoint is `https://localhost:8081`
### Database Creation
The application automatically creates databases and containers on startup using `EnsureCreatedAsync()`. For production, consider using migrations or Infrastructure as Code.
## Production Considerations
For production deployment:
1. Store Cosmos DB credentials in Azure Key Vault
2. Use managed identities instead of connection strings
3. Implement retry policies for transient failures
4. Add proper monitoring and logging (Application Insights)
5. Configure appropriate throughput (RU/s) for containers
6. Implement caching strategies for frequently accessed data
7. Use database migrations instead of `EnsureCreatedAsync()`
## License
This is a proof-of-concept project for demonstration purposes.
## Contributing
This is a sample project. Feel free to fork and modify for your needs.