{"id":16473853,"url":"https://github.com/budiadiono/efcodefirsthelper","last_synced_at":"2025-10-14T15:09:41.028Z","repository":{"id":36926358,"uuid":"41233553","full_name":"budiadiono/EFCodeFirstHelper","owner":"budiadiono","description":"Helps Entity Framework developers when using CodeFirst to improve the productivity","archived":false,"fork":false,"pushed_at":"2015-10-18T13:22:27.000Z","size":156,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-19T19:43:54.321Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/budiadiono.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-23T03:05:23.000Z","updated_at":"2015-08-23T03:06:18.000Z","dependencies_parsed_at":"2022-09-12T11:32:07.792Z","dependency_job_id":null,"html_url":"https://github.com/budiadiono/EFCodeFirstHelper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/budiadiono/EFCodeFirstHelper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/budiadiono%2FEFCodeFirstHelper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/budiadiono%2FEFCodeFirstHelper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/budiadiono%2FEFCodeFirstHelper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/budiadiono%2FEFCodeFirstHelper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/budiadiono","download_url":"https://codeload.github.com/budiadiono/EFCodeFirstHelper/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/budiadiono%2FEFCodeFirstHelper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279019297,"owners_count":26086709,"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-10-14T02:00:06.444Z","response_time":60,"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":[],"created_at":"2024-10-11T12:28:29.124Z","updated_at":"2025-10-14T15:09:40.973Z","avatar_url":"https://github.com/budiadiono.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Entity Framework CodeFirst Helper\n\nThis helper was made to help Entity Framework developers when using CodeFirst to improve the productivity.\n\n## AutoCompositeKeyHelper\nIs a helper to deal with automatic incremental primary key for an entity that has multiple primay keys (Composite Key).\n\nFor example you have these models:\n\n```\npublic class School {\n    public long Id { get; set; }\n    public string Name { get; set; }\n    public ICollection\u003cStudent\u003e Students { get; set; }\n}\n    \npublic class Student {\n    public long Id { get; set; }\n\n    public School School { get; set; }\n    public long SchoolId { get; set; }\n\n    public string Name { get; set; }\n}\n\n```\n\nWith this configuration:\n\n```\nprotected override void OnModelCreating(DbModelBuilder builder) {\n    builder.Entity\u003cSchool\u003e()\n    \t.HasKey(x =\u003e x.Id);\n        \n\tbuilder.Entity\u003cStudent\u003e()\n        .HasKey(x =\u003e new { x.SchoolId, x.Id })\n        .HasRequired(x =\u003e x.School)\n        .WithMany(x=\u003ex.Students)\n        .HasForeignKey(x =\u003e x.SchoolId);\n}\n```                \n\n\nThe `Student` class has composite key `Id` and `SchoolId`.\n\nSay that we have 2 Schools, with Id 1 and 2. In every time you add `Student` you will need to create, either calculate the value of `Id` since database won't generate it for you. Unless you give `[DatabaseGenerated(DatabaseGeneratedOption.Identity)]` attribute on `Id` property then you will not bothered again to think about it's values for every insertion. \n\nThen you'll get these results:\n\n| SchoolId        | Id           | Name  |\n| :-------------: |:-------------:| -----|\n| 1 | 1 | Ane |\n| 1 | 2 | Budi |\n| 1 | 3 | Barbara |\n| 2 | 4 | Erwin |\n| 2 | 5 | Rois |\n| 2 | 6 | Samantha |\n\nThat the values of `Id` sequentially generated by database automatically. But for some reason you may want to expect these results:\n\n\n| SchoolId        | Id           | Name  |\n| :-------------: |:-------------:| -----|\n| 1 | 1 | Ane |\n| 1 | 2 | Budi |\n| 1 | 3 | Barbara |\n| 2 | 1 | Erwin |\n| 2 | 2 | Rois |\n| 2 | 3 | Samantha |\n\nThat the `Id` of Student should be reset to 1 for every new `School` added. \n\nTo achieve it, we need a database trigger to help us to modify the `Id` value on everytime you insert new data on `Student`. We are also need 1 helper table to store the latest inserted `Id` for particular group in the table, let's name this table as **__Sequences**, with structure like this:\n\n| Column Name        | Type           | Description |\n| :-------------: |:-------------:| -----|\n| *Model* | nvarchar(50) | Name of table |\n| *Constrains* | nvarchar(300) | Other primary keys name and value of the table |\n| LastId | bigint | Highest value of Id used by the table filtered by Constrains |\n\nAnd here's the trigger should look like:\n\n```\nCREATE TRIGGER [dbo].[Student_Auto_Composite_Key]\nON [dbo].[Student]\nINSTEAD OF INSERT\nAS \nBEGIN\n    -- We don't want to return any affected row number\n\tSET NOCOUNT ON;\n\n    -- Open identity insertion access\n    SET IDENTITY_INSERT [dbo].[Student] ON;\n\n    -- The model name\n    DECLARE @model as nvarchar(max);\n    SET @model = '[dbo].[Student]';\n\n    -- Build sequence unique key\n\tDECLARE @constrains as nvarchar(max);\n\tSET @constrains = (SELECT '[SchoolId]=' + CAST([SchoolId] AS nvarchar(max)) FROM INSERTED);\n\n    -- Get the higher value of PK ([Id]), just incase that data may modified outside or trigger were applied on dirty data.\n    DECLARE @last_id as bigint;\n    SET @last_id = ISNULL((SELECT MAX([Id]) FROM [dbo].[Student] WHERE [SchoolId] IN (SELECT [SchoolId] FROM INSERTED)), 0);\n\n    -- Get last PK ([Id]) value from sequence\n    DECLARE @seq_id as bigint;\n\tSET @seq_id = ISNULL((SELECT [LastId] FROM [dbo].[__Sequences] WHERE Model = @model AND [Constrains] = @constrains), 0);\n\n    -- Pick the highest value to be use as PK value\n    IF (@seq_id \u003e @last_id)                        \n        SET @last_id = @seq_id;\n    \n    -- Increment\n    SET @last_id = @last_id + 1;\n\n    IF (@seq_id \u003e 0)\n        -- There is an already sequence for this table, just update it\n\t\tUPDATE [dbo].[__Sequences] SET [LastId]=@last_id WHERE [Model] = @model AND [Constrains] = @constrains;\n\tELSE\n        -- Otherwise, make a new one\n\t\tINSERT INTO [dbo].[__Sequences] ([Model], [Constrains], [LastId]) VALUES(@model, @constrains, @last_id);\n\t\n    -- Do insertion with modified PK value\n    insert into [dbo].[Student] ([Id],[SchoolId],[Name]) select @last_Id,[SchoolId],[Name] from INSERTED;\n    \n    -- Close identity insertion access\n    SET IDENTITY_INSERT [dbo].[Student] OFF;\n\n    -- EF needs these values\n    SELECT @last_Id AS [Id],[SchoolId] FROM INSERTED;\nEND\n```\n\nYou can see more detail explanation about how this trigger works on my blog post on http://www.budiadiono.com/2015/08/28/mssql-server-grouped-auto-increment-key-for-composite-key/.\n\nSeems that was a good idea, but not if you have to write the trigger by hand :) Specially if you have bunch of composite keyed tables. So this helper created to helps us to generate that trigger for us.\n\n### Installation\nInstead of build this project manually, you can get it from NuGet package manager:\n\n`Install-Package EFCodeFirstHelper`\n\n### Usage\n\nFirstly, you have to make sure that the main primary key of your model decorated with `[DatabaseGenerated(DatabaseGeneratedOption.Identity)]` attribute. In the example above, you need to decorate the propery `Id` of `Student`:\n\n```\npublic class Student {\n    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]\n    public long Id { get; set; }\n\n    public School School { get; set; }\n    public long SchoolId { get; set; }\n\n    public string Name { get; set; }\n}\n```\nOr by fluent you do this:\n```\nbuilder.Entity\u003cStudent\u003e()\n\t.Property(x =\u003e x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);\n```\n\nNow, you need to call the helper inside the ``Seed(...)`` method from the *Database Initializer* or *Database Migration Configuration* class if you are using Migrations.\n\nFirst, create the helper instance:\n\n```\nprotected override void Seed(DataContext context) {\n    // Helper instance\n    var helper = new CompositeKeys.AutoCompositeKeyHelper(context);\n    ...\n```\n\nNext, within this method, if you want to create triggers for all entities you can continue with:\n\n```\n    // Build __Sequences table and triggers for all composite keyed tables in the context\n    helper.Build();    \n```\n\nThe helper will determines the entities that contains composite key automatically and build the triggers for you. The helper will also create **__Sequences** table if you don't have it yet.\n\nOtherwise, you can customize the process. Specially, if you want to build for only single or several entities, you can do with:\n\n```\n    // Here's sequence table will be created if its doesn't exists yet\n    helper.InitSequencesTable();\n    \n    // Tell helper to build trigger for specific entities\n    helper.BuildTrigger(typeof(School));\n    helper.BuildTrigger(typeof(Course));\n```\n\nOnce this ``Seed(...)`` method called, the triggers should automatically created in your database.\n\nFor more detail investigation you can play with the `EFCodeFirstHelper.Test` test project.\n\n### Notes\nIf you are in the middle of working on some project and the data is already there, then this helper should working fine. \n\nYou also need no worry if the **__Sequences** table accidentally deleted because trigger will also look for the highest key value from the table data it self.\n\nJust let me know if something wrong happened by put it on this repository **Issues**.\n### Limitation\n- Only works for SQL Server.\n- Type of primary keys must be an integer family.\n\n### Contribute\nFeel free for all suggestions, improvements and pull requests.\n\n### Credits\nThanks to:\n\nhttp://romiller.com/tag/metadataworkspace/\n\nhttp://www.codeproject.com/Tips/890432/Entity-Framework-DiagnosticsContext-Get-Detailed\n\n## License\nFeel free to use for any kind of purposes except for something that smells like a crime :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbudiadiono%2Fefcodefirsthelper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbudiadiono%2Fefcodefirsthelper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbudiadiono%2Fefcodefirsthelper/lists"}