{"id":30325904,"url":"https://github.com/win7user10/laraue.efcoretriggers","last_synced_at":"2025-08-17T23:08:41.011Z","repository":{"id":41509399,"uuid":"309297489","full_name":"win7user10/Laraue.EfCoreTriggers","owner":"win7user10","description":"Library to write triggers in C# with EF.Core","archived":false,"fork":false,"pushed_at":"2024-11-20T18:50:36.000Z","size":691,"stargazers_count":123,"open_issues_count":26,"forks_count":22,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-07-26T22:53:03.819Z","etag":null,"topics":["csharp","database","efcore","orm","triggers","writing-triggers"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/win7user10.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-02T08:01:27.000Z","updated_at":"2025-06-28T10:04:02.000Z","dependencies_parsed_at":"2023-11-15T19:26:21.229Z","dependency_job_id":"61f1322b-5640-448e-a80c-1f5f3218f01f","html_url":"https://github.com/win7user10/Laraue.EfCoreTriggers","commit_stats":{"total_commits":115,"total_committers":14,"mean_commits":8.214285714285714,"dds":0.6347826086956522,"last_synced_commit":"a44cdc9d8cbe0b730a1025918e0554756e3086a0"},"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"purl":"pkg:github/win7user10/Laraue.EfCoreTriggers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.EfCoreTriggers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.EfCoreTriggers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.EfCoreTriggers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.EfCoreTriggers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/win7user10","download_url":"https://codeload.github.com/win7user10/Laraue.EfCoreTriggers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/win7user10%2FLaraue.EfCoreTriggers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270918404,"owners_count":24667679,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","database","efcore","orm","triggers","writing-triggers"],"created_at":"2025-08-17T23:08:38.882Z","updated_at":"2025-08-17T23:08:41.002Z","avatar_url":"https://github.com/win7user10.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Entity Framework Core Triggers\n\nEfCoreTriggers is the library to write native SQL triggers using EFCore model builder. Triggers are automatically translating into sql and adding to migrations.\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.EfCoreTriggers.Common)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.Common)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.EfCoreTriggers.Common)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.Common)\n\n#### Versions compatability\n| Package version | .NET version     | EF Core version |\n|-----------------|------------------|-----------------|\n| 9.x.x           | NET 9.0          | 9               |\n| 8.x.x           | NET 8.0          | 8               |\n| 7.x.x           | NET 6.0          | 7               |\n| 5.x.x           | NET standard 2.1 | 5               |\n\nInstall the provider package corresponding to your target database.\n\n```sh\ndotnet add package Laraue.EfCoreTriggers.PostgreSql\ndotnet add package Laraue.EfCoreTriggers.MySql\ndotnet add package Laraue.EfCoreTriggers.SqlServer\ndotnet add package Laraue.EfCoreTriggers.SqlLite\n```\n\n### Basic usage\n\nThe library has extensions for EntityBuilder to configure DbContext.\n\nAfter update Transaction entity, update records in the table with UserBalance entities.\n\n```cs\nmodelBuilder.Entity\u003cTransaction\u003e()\n    .AfterUpdate(trigger =\u003e trigger\n        .Action(action =\u003e action\n            .Condition(tableRefs =\u003e tableRefs.Old.IsVeryfied \u0026\u0026 tableRefs.New.IsVeryfied) // Executes only if condition met \n            .Update\u003cUserBalance\u003e(\n                (tableRefs, userBalances) =\u003e userBalances.UserId == tableRefs.Old.UserId, // Will be updated entities with matched condition\n                (tableRefs, oldBalance) =\u003e new UserBalance { Balance = oldBalance.Balance + tableRefs.New.Value - tableRefs.Old.Value }))); // New values for matched entities.\n```\n\nAfter insert Transaction entity, upsert record in the table with UserBalance entities.\n\n```cs\nmodelBuilder.Entity\u003cTransaction\u003e()\n    .AfterDelete(trigger =\u003e trigger\n        .Action(action =\u003e action\n            .Condition(tableRefs =\u003e tableRefs.Old.IsVeryfied)\n            .Upsert(\n                (tableRefs, balances) =\u003e tableRefs.Old.UserId == balances.UserId, // If this field will match more than 0 rows, will be executed update operation for these rows else insert\n                tableRefs =\u003e new UserBalance { UserId = tableRefs.Old.UserId, Balance = tableRefs.Old.Value }, // Insert, if value didn't exist\n                (tableRefs, oldUserBalance) =\u003e new UserBalance { Balance = oldUserBalance.Balance + tableRefs.Old.Value }))); // Update all matched values\n```\n\nAfter delete Transaction entity, execute raw SQL. Pass deleted entity fields as arguments. \n\n```cs\nmodelBuilder.Entity\u003cTransaction\u003e()\n    .AfterDelete(trigger =\u003e trigger\n        .Action(action =\u003e action\n            .ExecuteRawSql(\"PERFORM recalc_balance({0}, {1})\"), tableRefs =\u003e tableRefs.Old.UserId, tableRefs =\u003e tableRefs.Old.Amount)));\n```\n\nAlso, different trigger functions can be used to generate the SQL.  \n```\nTriggerFunctions.GetTableName\u003cTransaction\u003e();\nTriggerFunctions.GetColumnName\u003cTransaction\u003e(transaction =\u003e transaction.Value);\n```\n\n### All available triggers\n\n| Trigger | PostgreSql | SQL Server | SQLite | MySQL |\n| --- | --- | --- | --- | --- |\n| Before Insert | + | - | + | + |\n| After Insert | + | + | + | + |\n| Instead Of Insert | + | + | + | - |\n| Before Update | + | - | + | + |\n| After Update | + | + | + | + |\n| Instead Of Update | + | + | + | - |\n| Before Delete | + | - | + | + |\n| After Delete | + | + | + | + |\n| Instead Of Delete | + | + | + | - |\n\n### Available actions after trigger has worked\n\n- Insert\n- InsertIfNotExists\n- Update\n- Upsert\n- Delete\n- ExecuteRawSql\n\n## Laraue.EfCoreTriggers.PostgreSql\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.EfCoreTriggers.PostgreSql)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.PostgreSql)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.EfCoreTriggers.PostgreSql)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.PostgreSql)\n\n### Basic usage\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseNpgsql(\"User ID=test;Password=test;Host=localhost;Port=5432;Database=test;\")\n    .UsePostgreSqlTriggers()\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n\n## Laraue.EfCoreTriggers.MySql\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.EfCoreTriggers.MySql)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.MySql)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.EfCoreTriggers.MySql)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.MySql)\n\n### Basic usage\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseMySql(\"server=localhost;user=test;password=test;database=test;\", new MySqlServerVersion(new Version(8, 0, 22))))\n    .UseMySqlTriggers()\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n\n## Laraue.EfCoreTriggers.SqlServer\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.EfCoreTriggers.SqlServer)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.SqlServer)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.EfCoreTriggers.SqlServer)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.SqlServer)\n\n### Basic usage\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseSqlServer(\"Data Source=(LocalDb)\\\\v15.0;Database=test;Integrated Security=SSPI;\")\n    .UseSqlServerTriggers()\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n## Laraue.EfCoreTriggers.SqlLite\n\n[![latest version](https://img.shields.io/nuget/v/Laraue.EfCoreTriggers.SqlLite)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.SqlLite)\n[![latest version](https://img.shields.io/nuget/dt/Laraue.EfCoreTriggers.SqlLite)](https://www.nuget.org/packages/Laraue.EfCoreTriggers.SqlLite)\n\n### Basic usage\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseSqlite(\"Filename=D://test.db\")\n    .UseSqlLiteTriggers()\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n\n## Customization\n\nAny service using for generation SQL can be replaced.\n\n```cs\nprivate class CustomDbSchemaRetriever : EfCoreDbSchemaRetriever\n{\n    public CustomDbSchemaRetriever(IModel model) : base(model)\n    {\n    }\n\n    protected override string GetColumnName(Type type, MemberInfo memberInfo)\n    {\n        // Change strategy of naming some column\n        return 'c_' + base.GetColumnName(type, memberInfo);\n    }\n}\n```\n\nAdding this service to the container\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseNpgsql(\"User ID=test;Password=test;Host=localhost;Port=5432;Database=test;\")\n    .UsePostgreSqlTriggers(services =\u003e services.AddSingleton\u003cIDbSchemaRetriever, CustomDbSchemaRetriever\u003e)\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n\n### Adding translation of some custom function into sql code\n\nTo do this thing a custom function converter should be added to a provider\n\nLet's image that we have an extension like\n\n```cs\npublic static class StringExtensions\n{\n    public static bool Like(this string str, string pattern)\n    {\n        throw new InvalidOperationException();\n    }\n} \n```\n\nNow a custom converter should be written to translate this function into SQL\n\n```cs\npublic abstract class StringExtensionsLikeConverter : MethodCallConverter\n{\n    public override bool IsApplicable(MethodCallExpression expression)\n    {\n        return expression.Method.ReflectedType == typeof(StringExtensions) \u0026\u0026 MethodName == nameof(StringExtensions.Like);\n    }\n    \n    public override SqlBuilder BuildSql(BaseExpressionProvider provider, MethodCallExpression expression)\n    {\n        // Generate SQL for arguments, they can be SQL expressions\n        var argumentSql = provider.GetMethodCallArgumentsSql(expression)[0];\n\n        // Generate SQL for this context, it also can be a SQL expression\n        var sqlBuilder = provider.GetExpressionSql(expression.Object);\n        \n        // Combine SQL for object and SQL for arguments\n        // Output will be like \"thisValueSql LIKE 'passedArgumentValueSql'\"\n        return new(sqlBuilder.AffectedColumns, $\"{sqlBuilder} LIKE {argumentSql}\");\n    }\n}\n```\n\nAll custom converters should be added while setup a database\n\n```cs\nvar options = new DbContextOptionsBuilder\u003cTestDbContext\u003e()\n    .UseSqlite(\"Filename=D://test.db\")\n    .UseSqlLiteTriggers(services =\u003e services.AddMethodCallConverter(converter))\n    .Options;\n\nvar dbContext = new TestDbContext(options);\n```\n\nNow this function can be used in a trigger and it will be translated into SQL\n\n```cs\nmodelBuilder.Entity\u003cTransaction\u003e()\n    .AfterDelete(trigger =\u003e trigger\n        .Action(action =\u003e action\n            .Condition(oldTransaction =\u003e oldTransaction.Description.Like('%payment%'))\n            \n```\n\n### Trigger prefix customization\n\nYou can change the standard library prefix for trigger using the next static variable\n```cs\nLaraue.EfCoreTriggers.Common.Constants.AnnotationKey = \"MY_PREFIX\"\n```\n\n### Generic triggers\n\nDefine the generic trigger class inherits `GenericTrigger\u003cBaseTriggerClass\u003e`\n```cs\n// Trigger for all classes inherits class Notification\nprivate sealed class NotificationsTriggers : GenericTrigger\u003cNotification\u003e\n{\n    public override void ApplyTrigger\u003cTImplEntity\u003e(EntityTypeBuilder\u003cTImplEntity\u003e modelBuilder)\n    {\n        modelBuilder\n            .AfterInsert(x =\u003e x\n                .Action(y =\u003e y\n                    .Insert\u003cNotificationLog\u003e(inserted =\u003e new NotificationLog\n                    {\n                        Text = inserted.New.Text,\n                        NotificationType = typeof(TImplEntity).ToString(),\n                        OriginalId = inserted.New.Id\n                    })));\n    }\n}\n```\n\nRegistering generic trigger\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    modelBuilder.AddGenericTrigger(new NotificationsTriggers());\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwin7user10%2Flaraue.efcoretriggers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwin7user10%2Flaraue.efcoretriggers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwin7user10%2Flaraue.efcoretriggers/lists"}