Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions entity-framework/core/providers/cosmos/modeling.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,28 @@ Limitations:
* Only dictionaries with string keys are supported.
* Support for querying into primitive collections was added in EF Core 9.0.

## Database triggers
Comment thread
AndriySvyryd marked this conversation as resolved.
Comment thread
AndriySvyryd marked this conversation as resolved.

> [!NOTE]
> Database trigger execution support was introduced in EF Core 10.0.

Azure Cosmos DB supports pre- and post-triggers that run before or after database operations. EF Core can be configured to execute these triggers when performing save operations.

> [!IMPORTANT]
> Triggers are executed server-side by Azure Cosmos DB when EF Core performs operations, but they are not enforced - operations can be performed without running triggers if accessing the database directly. This means triggers should not be used for security-related functionality such as authentication or auditing, as they can be bypassed by applications that access the database directly without using EF Core.

To configure triggers on an entity type, use the `HasTrigger` method:

[!code-csharp[TriggerConfiguration](../../../../samples/core/Cosmos/ModelBuilding/TriggerSample.cs?name=TriggerConfiguration)]

The `HasTrigger` method requires:

* **modelName**: The name of the trigger in Azure Cosmos DB
* **triggerType**: Either `TriggerType.Pre` (executed before the operation) or `TriggerType.Post` (executed after the operation)
* **triggerOperation**: The operation that should execute the trigger - `Create`, `Replace`, `Delete`, or `All`

Before triggers can be executed, they must be created in Azure Cosmos DB using the Cosmos SDK or Azure portal. The trigger name configured in EF Core must match the trigger name in Azure Cosmos DB.

## Optimistic concurrency with eTags

To configure an entity type to use [optimistic concurrency](xref:core/saving/concurrency) call <xref:Microsoft.EntityFrameworkCore.CosmosEntityTypeBuilderExtensions.UseETagConcurrency*>. This call will create an `_etag` property in [shadow state](xref:core/modeling/shadow-properties) and set it as the concurrency token.
Expand Down
4 changes: 2 additions & 2 deletions samples/core/Cosmos/Cosmos.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="10.0.0-rc.1.25451.107" />
</ItemGroup>

</Project>
73 changes: 73 additions & 0 deletions samples/core/Cosmos/ModelBuilding/TriggerSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Microsoft.Azure.Cosmos.Scripts;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace Cosmos.ModelBuilding;

public static class TriggerSample
{
public static async Task ConfigureTriggers()
{
var contextOptions = new DbContextOptionsBuilder<TriggerContext>()
.UseCosmos("https://localhost:8081", "account-key", "sample");

using var context = new TriggerContext(contextOptions.Options);

// Ensure database is created
await context.Database.EnsureCreatedAsync();

// Create a new product - this will trigger the PreInsertTrigger
var product = new Product
{
Id = 1,
Name = "Sample Product",
Price = 19.99m,
Category = "Electronics"
};

context.Products.Add(product);
await context.SaveChangesAsync();

// Update the product - this will trigger the UpdateTrigger
product.Price = 24.99m;
await context.SaveChangesAsync();

// Delete the product - this will trigger the PostDeleteTrigger
context.Products.Remove(product);
await context.SaveChangesAsync();
}
}

public class TriggerContext : DbContext
{
public TriggerContext(DbContextOptions options) : base(options) { }

public DbSet<Product> Products { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>(entity =>
{
entity.HasPartitionKey(p => p.Category);

#region TriggerConfiguration
// Configure pre-trigger for create operations
entity.HasTrigger("PreInsertTrigger", TriggerType.Pre, TriggerOperation.Create);

// Configure post-trigger for delete operations
entity.HasTrigger("PostDeleteTrigger", TriggerType.Post, TriggerOperation.Delete);

// Configure trigger for replace operations
entity.HasTrigger("UpdateTrigger", TriggerType.Pre, TriggerOperation.Replace);
#endregion
});
}
}

public class Product
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public decimal Price { get; set; }
public string Category { get; set; } = null!;
}
5 changes: 5 additions & 0 deletions samples/global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
Comment thread
AndriySvyryd marked this conversation as resolved.
"sdk": {
"version": "10.0.100-rc.1.25451.107"
}
}
Loading