{"id":91880,"date":"2021-08-23T17:00:31","date_gmt":"2021-08-23T17:00:31","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=91880"},"modified":"2021-08-23T17:09:51","modified_gmt":"2021-08-23T17:09:51","slug":"change-delete-behavior-and-more-on-ef-core","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/change-delete-behavior-and-more-on-ef-core\/","title":{"rendered":"Change Delete Behavior and more on EF Core"},"content":{"rendered":"<p><strong>Entity Framework<\/strong> had objects called conventions. These objects can be used to make configurations on the entity framework context to change its default behavior.<\/p>\n<p>Two examples of behaviors we usually configure are the pluralize behavior for object names and delete cascade for foreign keys.<\/p>\n<p><strong>Entity Framework Core<\/strong>, on the other hand, doesn&#8217;t have conventions. How could we achieve the same configuration on entity framework core?<\/p>\n<h2>Configuring Conventions in Entity Framework Core<\/h2>\n<p>Instead of depending on conventions we need to make the configurations in a &#8220;manual&#8221; way. It means setting the configuration on each object of the schema as needed.<\/p>\n<p>We may need a bit more code and also need to take care to execute this code after the definition of the schema. In this way the behaviors we are configuring will apply to all the objects in the schema.<\/p>\n<p>This is an example of the <strong>OnModelCreating<\/strong> applying the code to avoid the pluralize behavior and remove the delete cascade behavior from all foreign keys:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow: auto;width: auto;border: solid gray;border-width: .1em .1em .1em .8em;padding: .2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\" class=\"crayon:false\">        <span style=\"color: #008800;font-weight: bold\">protected<\/span> <span style=\"color: #008800;font-weight: bold\">override<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">OnModelCreating<\/span>(ModelBuilder modelBuilder)\r\n        {\r\n            <span style=\"color: #008800;font-weight: bold\">if<\/span> (modelBuilder == <span style=\"color: #008800;font-weight: bold\">null<\/span>)\r\n            <span style=\"color: #008800;font-weight: bold\">throw<\/span> <span style=\"color: #008800;font-weight: bold\">new<\/span> <span style=\"color: #0066bb;font-weight: bold\">ArgumentNullException<\/span>(<span style=\"background-color: #fff0f0\">\"modelBuilder\"<\/span>);\r\n\r\n            <span style=\"color: #888888\">\/\/ for the other conventions, we do a metadata model loop<\/span>\r\n            <span style=\"color: #008800;font-weight: bold\">foreach<\/span> (<span style=\"color: #333399;font-weight: bold\">var<\/span> entityType <span style=\"color: #008800;font-weight: bold\">in<\/span> modelBuilder.Model.GetEntityTypes())\r\n            {\r\n                <span style=\"color: #888888\">\/\/ equivalent of modelBuilder.Conventions.Remove&lt;PluralizingTableNameConvention&gt;();<\/span>\r\n                entityType.SetTableName(entityType.DisplayName());\r\n\r\n                <span style=\"color: #888888\">\/\/ equivalent of modelBuilder.Conventions.Remove&lt;OneToManyCascadeDeleteConvention&gt;();<\/span>\r\n                entityType.GetForeignKeys()\r\n                    .Where(fk =&gt; !fk.IsOwnership &amp;&amp; fk.DeleteBehavior == DeleteBehavior.Cascade)\r\n                    .ToList()\r\n                    .ForEach(fk =&gt; fk.DeleteBehavior = DeleteBehavior.Restrict);\r\n            }\r\n\r\n            <span style=\"color: #008800;font-weight: bold\">base<\/span>.OnModelCreating(modelBuilder);\r\n        }\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<h2>Creating Extension Methods<\/h2>\n<p>We can do a bit better, turning this code more reusable using extension methods. Our first attempt to use extension methods can be like this:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow: auto;width: auto;border: solid gray;border-width: .1em .1em .1em .8em;padding: .2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\" class=\"crayon:false\"><span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">class<\/span> <span style=\"color: #bb0066;font-weight: bold\">ContextExtensions<\/span>\r\n{\r\n    <span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">RemovePluralizeBehavior<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder)\r\n    {\r\n        builder.EntityLoop(et =&gt; et.SetTableName(et.DisplayName()));\r\n    }\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">RemoveOneToManyCascade<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder)\r\n    {\r\n        builder.EntityLoop(et =&gt; et.GetForeignKeys()\r\n            .Where(fk =&gt; !fk.IsOwnership &amp;&amp; fk.DeleteBehavior == DeleteBehavior.Cascade)\r\n            .ToList()\r\n            .ForEach(fk =&gt; fk.DeleteBehavior = DeleteBehavior.Restrict));\r\n    }\r\n\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">private<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">EntityLoop<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder, Action&lt;IMutableEntityType&gt; action)\r\n    {\r\n        <span style=\"color: #008800;font-weight: bold\">foreach<\/span> (<span style=\"color: #333399;font-weight: bold\">var<\/span> entityType <span style=\"color: #008800;font-weight: bold\">in<\/span> builder.Model.GetEntityTypes())\r\n        {\r\n            action(entityType);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Mind the interesting way I ensured the loop would be built only once and re-used, parameterizing the loop with Actions\/Lambda expressions.<\/p>\n<p>Once we built the extension methods, the <strong>OnModelCreating<\/strong> will become like this:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow: auto;width: auto;border: solid gray;border-width: .1em .1em .1em .8em;padding: .2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\" class=\"crayon:false\">        <span style=\"color: #008800;font-weight: bold\">protected<\/span> <span style=\"color: #008800;font-weight: bold\">override<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">OnModelCreating<\/span>(ModelBuilder modelBuilder)\r\n        {\r\n            <span style=\"color: #008800;font-weight: bold\">if<\/span> (modelBuilder == <span style=\"color: #008800;font-weight: bold\">null<\/span>)\r\n                <span style=\"color: #008800;font-weight: bold\">throw<\/span> <span style=\"color: #008800;font-weight: bold\">new<\/span> <span style=\"color: #0066bb;font-weight: bold\">ArgumentNullException<\/span>(<span style=\"background-color: #fff0f0\">\"modelBuilder\"<\/span>);\r\n\r\n            modelBuilder.AddRemovePluralizeConvention();\r\n            modelBuilder.AddRemoveOneToManyCascadeConvention();\r\n\r\n            modelBuilder.ApplyConventions();\r\n\r\n            <span style=\"color: #008800;font-weight: bold\">base<\/span>.OnModelCreating(modelBuilder);\r\n        }\r\n<\/pre>\n<\/div>\n<h2>\nOptimizing the Extension<\/h2>\n<p>This code could still be improved, because in the way it is the same loop is being repeated twice. We can make some changes to our extension to avoid this. The new code will be like this:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow: auto;width: auto;border: solid gray;border-width: .1em .1em .1em .8em;padding: .2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\" class=\"crayon:false\"><span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">class<\/span> <span style=\"color: #bb0066;font-weight: bold\">ContextExtensions<\/span>\r\n{\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">private<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> List&lt;Action&lt;IMutableEntityType&gt;&gt; Conventions=<span style=\"color: #008800;font-weight: bold\">new<\/span> List&lt;Action&lt;IMutableEntityType&gt;&gt;();\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">AddRemovePluralizeConvention<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder)\r\n    {\r\n         Conventions.Add(et =&gt; et.SetTableName(et.DisplayName()));\r\n    }\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">AddRemoveOneToManyCascadeConvention<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder)\r\n    {\r\n        Conventions.Add(et =&gt; et.GetForeignKeys()\r\n            .Where(fk =&gt; !fk.IsOwnership &amp;&amp; fk.DeleteBehavior == DeleteBehavior.Cascade)\r\n            .ToList()\r\n            .ForEach(fk =&gt; fk.DeleteBehavior = DeleteBehavior.Restrict));\r\n    }\r\n\r\n    <span style=\"color: #008800;font-weight: bold\">public<\/span> <span style=\"color: #008800;font-weight: bold\">static<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">ApplyConventions<\/span>(<span style=\"color: #008800;font-weight: bold\">this<\/span> ModelBuilder builder)\r\n    {\r\n        <span style=\"color: #008800;font-weight: bold\">foreach<\/span> (<span style=\"color: #333399;font-weight: bold\">var<\/span> entityType <span style=\"color: #008800;font-weight: bold\">in<\/span> builder.Model.GetEntityTypes())\r\n        {\r\n            <span style=\"color: #008800;font-weight: bold\">foreach<\/span>(Action&lt;IMutableEntityType&gt; action <span style=\"color: #008800;font-weight: bold\">in<\/span> Conventions)\r\n                action(entityType);\r\n        }\r\n\r\n        Conventions.Clear();\r\n    }\r\n}\r\n<\/pre>\n<\/div>\n<p>\nThe new <strong>OnModelCreating<\/strong> will be like this:<\/p>\n<p>&nbsp;<\/p>\n<div><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff;overflow: auto;width: auto;border: solid gray;border-width: .1em .1em .1em .8em;padding: .2em .6em\">\n<pre style=\"margin: 0;line-height: 125%\" class=\"crayon:false\">        <span style=\"color: #008800;font-weight: bold\">protected<\/span> <span style=\"color: #008800;font-weight: bold\">override<\/span> <span style=\"color: #008800;font-weight: bold\">void<\/span> <span style=\"color: #0066bb;font-weight: bold\">OnModelCreating<\/span>(ModelBuilder modelBuilder)\r\n        {\r\n            <span style=\"color: #008800;font-weight: bold\">if<\/span> (modelBuilder == <span style=\"color: #008800;font-weight: bold\">null<\/span>)\r\n                <span style=\"color: #008800;font-weight: bold\">throw<\/span> <span style=\"color: #008800;font-weight: bold\">new<\/span> <span style=\"color: #0066bb;font-weight: bold\">ArgumentNullException<\/span>(<span style=\"background-color: #fff0f0\">\"modelBuilder\"<\/span>);\r\n\r\n            modelBuilder.AddRemovePluralizeConvention();\r\n            modelBuilder.AddRemoveOneToManyCascadeConvention();\r\n\r\n            modelBuilder.ApplyConventions();\r\n\r\n            <span style=\"color: #008800;font-weight: bold\">base<\/span>.OnModelCreating(modelBuilder);\r\n        }\r\n<\/pre>\n<\/div>\n<\/div>\n<h2>Conclusion<\/h2>\n<p><strong>.NET<\/strong> core avoid some pre-built classes such as the conventions, but we still can create our own libraries to reuse and simplify the code in our own way.<\/p>\n<p>I made these <a href=\"https:\/\/github.com\/DennesTorres\/EfCoreContextHelper\">extension methods available on GitHub<\/a>, you can use, fork, improve and include in your artifacts<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Entity Framework had objects called conventions. These objects can be used to make configurations on the entity framework context to change its default behavior. Two examples of behaviors we usually configure are the pluralize behavior for object names and delete cascade for foreign keys. Entity Framework Core, on the other hand, doesn&#8217;t have conventions. How&#8230;&hellip;<\/p>\n","protected":false},"author":50808,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[145416,145418,145420],"coauthors":[6810],"class_list":["post-91880","post","type-post","status-publish","format-standard","hentry","category-blogs","tag-dotnet","tag-dotnetcore","tag-entity-framework-core"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91880","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/50808"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=91880"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91880\/revisions"}],"predecessor-version":[{"id":92180,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91880\/revisions\/92180"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=91880"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=91880"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=91880"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=91880"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}