[{"data":1,"prerenderedAt":59},["ShallowReactive",2],{"blog-post-removing-infrastructure-information-from-domain-code-4":3,"blog-sidebar-tags":43,"blog-sidebar-popular":46},{"overview":4,"post":32},{"slug":5,"title":6,"subtitle":7,"opener":8,"tags":9,"date":14,"headerImage":15,"author":20,"images":30,"__typename":31},"removing-infrastructure-information-from-domain-code-4","Removing infrastructure information from domain code - Part 4","Always ensure tracked entities for navigation properties operations","Ensure entities are always tracked for navigation properties operations by implementing a custom LazyLoadingIntercepter.",[10,11,12,13],"DDD","C#","EF","Clean code","2021-04-14T00:00:00Z",[16],{"fileName":17,"url":18,"__typename":19},"DDD.png","https:\u002F\u002Fcms.spatial-focus.net\u002Fapi\u002Fassets\u002Fspatialfocus\u002Fe23416f4-3d80-404b-bf82-b5c79d9dee2e\u002F","Asset",[21],{"id":22,"flatData":23,"__typename":29},"247edfce-2ecf-40b8-87e1-58e549d0c243",{"name":24,"image":25,"__typename":28},"Christoph Perger",[26],{"url":27,"__typename":19},"https:\u002F\u002Fcms.spatial-focus.net\u002Fapi\u002Fassets\u002Fspatialfocus\u002F967c15d7-bd4a-4d6e-9e34-b891dccb9906\u002F","AuthorFlatDataDto","Author",null,"PostsFlatDataDto",{"slug":5,"title":6,"subtitle":7,"headerImage":33,"opener":8,"text":35,"tags":36,"date":14,"repo":37,"author":38,"images":30,"__typename":31},[34],{"fileName":17,"url":18,"__typename":19},"# About this series\n\nWhen designing a clean DDD solution, many times you need to pollute your domain with infrastructure code in order to get Entity Framework (EF) working. This blog series will look at some of these issues and how to resolve them.\n\n# The issue\n\nSometimes you need to delete or replace a navigation property of an entity. Even with lazy loading enabled the entity to replace or delete might not be tracked by the context while modifying the navigation property. This could lead to undesired behaviour (see [Replacing One-To-One Entity does not delete old entity and throws unique constraint](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fissues\u002F24064)). There are workarounds to circumvent this, like eager loading the property in all EF queries. What we sometimes do is to make an explicit call to the navigation property getter so that lazy loading will kick in:\n\n```csharp\npublic class Car\n{\n    public CarHolder? CarHolder { get; protected set; }\n\n    public void TrashCar()\n    {\n        \u002F\u002F This triggers lazy loading and will ensure that CarHolder is tracked by the context\n        _ = CarHolder;\n\n        \u002F\u002F This will now correctly remove the relation between the car and the current car holder\n        CarHolder = null;\n    }\n}\n```\n\nEven though this works it is quite cumbersome especially for bigger models and there is a chance that it will be forgotten here and there. Less experienced EF developers might also be irritated by statements like this if not accompanied by comments like above. And of course it's another infrastructure detail polluting our domain which we want to get rid of as part of this series.\n\n# The solution\n\nWhat if we would trigger lazy loading when accessing the setter? We accept the additional (maybe unnecessary) database roundtrip in favor of being able to just write `CarHolder = null` that will work in all cases, regardless whether `CarHolder` is already tracked by the context or not.\n\nThe responsible code of this lazy loading functionality is straightforward and consists of two files: an [IProxyFactory](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fblob\u002Fmain\u002Fsrc\u002FEFCore.Proxies\u002FProxies\u002FInternal\u002FProxyFactory.cs) and [IInterceptor](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fblob\u002Fmain\u002Fsrc\u002FEFCore.Proxies\u002FProxies\u002FInternal\u002FLazyLoadingInterceptor.cs) implementation. The interesting part is within the `LazyLoadingInterceptor` where it intercepts property getter calls:\n\n```csharp\nif (methodName.StartsWith(\"get_\", StringComparison.Ordinal))\n{\n    var navigationName = methodName.Substring(4);\n    var navigationBase = this.entityType.FindNavigation(navigationName) ??\n        (INavigationBase)this.entityType.FindSkipNavigation(navigationName);\n\n    if (navigationBase != null && !(navigationBase is INavigation navigation && navigation.ForeignKey.IsOwnership))\n    {\n        this.loader.Load(invocation.Proxy, navigationName);\n    }\n}\n```\n\nThe only thing we need to do here is to create our own `CustomLazyLoadingInterceptor`, expand this block and also check for `set_`:\n\n```csharp\nif (methodName.StartsWith(\"get_\", StringComparison.Ordinal) ||\n    methodName.StartsWith(\"set_\", StringComparison.Ordinal))\n{\n    \u002F\u002F Trigger lazy loading\n}\n```\n\nThe `ProxyFactory` is responsible for creating those `LazyLoadingInterceptor` instances, so we have to provide our own implementation and replace `LazyLoadingInterceptor` with our `CustomLazyLoadingInterceptor`.\n\nWhile configuring the `DbContext` in the `ServiceCollection` description you can now replace the existing with our custom implementation:\n\n```csharp\nservices.AddDbContext\u003CSampleContext>(options =>\n{\n    options.UseSqlite(\"DataSource=local.db\").UseLazyLoadingProxies();\n\n    \u002F\u002F Replace with our custom implementation\n    options.ReplaceService\u003CIProxyFactory, CustomProxyFactory>();\n}, ServiceLifetime.Transient);\n```\n\nProxies created by EF Core will now also intercept setters for navigation properties and ensure those are tracked by the context before we apply any operation on it.\n\n# The result\n\nInstead of polluting our domain entities with navigation property operations that contain workarounds to ensure entities are always tracked, we end up with a clean domain entity class and slim and clear functions:\n\n```csharp\npublic class Car\n{\n    public CarHolder? CarHolder { get; protected set; }\n\n    \u002F\u002F This triggers lazy loading and will ensure that CarHolder is tracked by the context\n    \u002F\u002F before setting it to null\n    public void TrashCar() => CarHolder = null;\n}\n```\n\nThe only thing you should take care of is to check occasionally for changes in [ProxyFactory](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fblob\u002Fmain\u002Fsrc\u002FEFCore.Proxies\u002FProxies\u002FInternal\u002FProxyFactory.cs) and [LazyLoadingInterceptor](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fblob\u002Fmain\u002Fsrc\u002FEFCore.Proxies\u002FProxies\u002FInternal\u002FLazyLoadingInterceptor) (doesn't happen too often) and update your custom implementation if necessary.\n\nThe part 5 and end of our series will take a closer look on how to use pure readonly collections for collection navigation properties.",[10,11,12,13],"https:\u002F\u002Fgithub.com\u002FSpatialFocus\u002Fsample-ef-removing-infrastructure-code-from-domain",[39],{"id":22,"flatData":40,"__typename":29},{"name":24,"image":41,"__typename":28},[42],{"url":27,"__typename":19},[11,10,12,13,44,45],"Geo","web",[47,53],{"slug":48,"title":49,"headerImage":50,"__typename":31},"radial-progress-css-animation","Radial progress with CSS",[51],{"url":52,"__typename":19},"https:\u002F\u002Fcms.spatial-focus.net\u002Fapi\u002Fassets\u002Fspatialfocus\u002Fb7cdc0ef-364e-4444-8dd3-b85f19e87820\u002F",{"slug":54,"title":55,"headerImage":56,"__typename":31},"load-testing-put-endpoints-with-apache-jmeter","Load testing PUT endpoints",[57],{"url":58,"__typename":19},"https:\u002F\u002Fcms.spatial-focus.net\u002Fapi\u002Fassets\u002Fspatialfocus\u002Fad44fc84-935c-4ab4-ab0e-9fbcb84fb2c1\u002F",1783525744119]