[{"data":1,"prerenderedAt":59},["ShallowReactive",2],{"blog-post-removing-infrastructure-information-from-domain-code-5":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-5","Removing infrastructure information from domain code - Part 5","Dealing with readonly collections","Keep navigation properties of type IReadOnlyCollection\u003CT> small and simple with SpatialFocus.EFLazyLoading.Fody.",[10,11,12,13],"DDD","C#","EF","Clean code","2021-07-30T00: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},"0152e82d-a848-44f8-b0b8-fd8aec4b874a",{"name":24,"image":25,"__typename":28},"Christopher Dresel",[26],{"url":27,"__typename":19},"https:\u002F\u002Fcms.spatial-focus.net\u002Fapi\u002Fassets\u002Fspatialfocus\u002F305bcd01-b811-4147-8947-e025b7966355\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\nThe `IReadOnlyCollection\u003CT>` interface comes in handy when you want to prevent direct modification of a collection property - which you want to if you aim for DDD purity. It's not so easy to integrate this into Entity Framework though, especially in combination with lazy loading. If you then reference your backing field within your entity (e.g. to add\u002Fremove an item or clear the collection), there is no chance for lazy loading to kick in. See this [suggestion](https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fefcore\u002Fissues\u002F22752#issuecomment-700282063) by one of the EF Core maintainers on how we could implement this for our car sample:\n\n```csharp\npublic class CarHolder\n{\n    private readonly Action\u003Cobject, string?>? lazyLoader;\n    private List\u003CCar> cars = new();\n\n    protected CarHolder(Action\u003Cobject, string?> lazyLoader)\n    {\n        this.lazyLoader = lazyLoader;\n    }\n\n    public virtual IReadOnlyCollection\u003CCar> Cars => InternalCars.AsReadOnly();\n\n    public int CarsCount => InternalCars.Count;\n\n    protected List\u003CCar> InternalCars => this.lazyLoader.Load(this, ref this.cars, nameof(CarHolder.Cars));\n\n    public void AddCar(Car car) => InternalCars.Add(car);\n}\n```\n\nThere are a few issues with this approach:\n\n- You have to add `ILazyLoader` or `Action\u003Cobject, string?>` to your constructor and store it into a field\n- For each collection property you need an additional intermediate property which triggers lazy loading\n- (Almost) All accesses to the backing field must be done via this intermediate property\n\nThis is quite cumbersome and painful to do. Also this adds a lot of infrastructure logic to our domain which we want to avoid as part of this series.\n\n# The solution\n\nThis is not something for the average Joe but challenges like this are ideal candidates for IL weaving (if you want to spend some time :)). If you do not know what IL weaving is, there is plenty of [information](https:\u002F\u002Fmedium.com\u002F@heytherewill\u002Fnet-il-weaving-for-those-who-know-nothing-about-net-il-weaving-c0f7e461ef47) out there.\n\nWe did spend some time and came up with our [EFLazyLoading.Fody](https:\u002F\u002Fgithub.com\u002FSpatialFocus\u002FEFLazyLoading.Fody) NuGet package.\n\nWhat it does is best explained by this little example:\n\n```csharp\npublic class Customer\n{\n    private readonly List\u003COrder> orders = new();\n\n    public Customer(string name)\n    {\n        Name = name;\n    }\n\n    public int NumberOfOrders => this.orders.Count;\n\n    public virtual IReadOnlyCollection\u003COrder> Orders => this.orders.AsReadOnly();\n\n    public int Id { get; protected set; }\n\n    public string Name { get; protected set; }\n\n    public void AddOrder(Order order) => this.orders.Add(order);\n\n    public void ClearOrders() => this.orders.Clear();\n\n    public void RemoveOrder(Order order) => this.orders.Remove(order);\n}\n```\n\nThis is how I would model my domain if I could ignore any EF or lazy loading pitfalls. EFLazyLoading.Fody adds all the boilerplate which is necessarily to let this work as expected:\n\n- Adds a field `private readonly Action\u003Cobject, string>? lazyLoader` to your entity\n- Overloads existing constructors with an additional `Action\u003Cobject, string>? lazyLoader` parameter\n- For every `ReadOnlyCollection` or `IReadOnlyCollection` it finds the corresponding backing field (`propertyname`, `_propertyname`, `m_propertyname`)\n- For every access (including methods & properties) to this backing field (except the navigation property itself), it adds the corresponding `this.lazyLoader?.Invoke` statement\n\nThis is how the class would look like after the weaving:\n\n```csharp\npublic class Customer\n{\n    private readonly Action\u003Cobject, string>? lazyLoader;\n    private readonly List\u003COrder> orders = new();\n\n    public CustomerWeaved(string name)\n    {\n        Name = name;\n    }\n\n    \u002F\u002F For every constructor a constructor overload with Action\u003Cobject, string> lazyLoader will be added,\n    \u002F\u002F and the original constructor will be called\n    \u002F\u002F See https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fquerying\u002Frelated-data\u002Flazy#lazy-loading-without-proxies\n    protected CustomerWeaved(string name, Action\u003Cobject, string> lazyLoader) : this(name)\n    {\n        this.lazyLoader = lazyLoader;\n    }\n\n    public int NumberOfOrders\n    {\n        get\n        {\n            this.lazyLoader?.Invoke(this, \"Orders\");\n            return this.orders.Count;\n        }\n    }\n\n    \u002F\u002F Access via navigation property will trigger default lazy loading behaviour\n    public virtual IReadOnlyCollection\u003COrder> Orders => this.orders.AsReadOnly();\n\n    public int Id { get; protected set; }\n\n    public string Name { get; protected set; }\n\n    public void AddOrder(Order order)\n    {\n        this.lazyLoader?.Invoke(this, \"Orders\");\n        this.orders.Add(order);\n    }\n\n    public void ClearOrders()\n    {\n        this.lazyLoader?.Invoke(this, \"Orders\");\n        this.orders.Clear();\n    }\n\n    public void RemoveOrder(Order order)\n    {\n        this.lazyLoader?.Invoke(this, \"Orders\");\n        this.orders.Remove(order);\n    }\n}\n```\n\nIf you want to check the internals, the most relevant code can be found [here](https:\u002F\u002Fgithub.com\u002FSpatialFocus\u002FEFLazyLoading.Fody\u002Fblob\u002Fmaster\u002Fsrc\u002FSpatialFocus.EFLazyLoading.Fody\u002FLazyLoading.cs).\n\n# The result\n\nInstead of polluting our domain entities with a lot of EF and lazy loading workarounds, we end up with a clean domain entity class and slim and clear functions:\n\n```csharp\npublic class CarHolder\n{\n    private readonly List\u003CCar> cars = new();\n\n    public IReadOnlyCollection\u003CCar> Cars => this.cars.AsReadOnly();\n\n    public int CarsCount => this.cars.Count;\n\n    public void AddCar(Car car) => this.cars.Add(car);\n}\n```\n\nThe EFLazyLoading.Fody might not cover all use cases, so if you find something not working feel free to open an [issue](https:\u002F\u002Fgithub.com\u002FSpatialFocus\u002FEFLazyLoading.Fody\u002Fissues) in the Github repository.\n\n# Summary\n\nThe part 5 concludes our series on DDD and clean-code development. We have looked at some of the most obvious issues, where infrastructure-related code spoils our domain model. We described the issues, the potential solutions and showed the packages that help to make your domain model cleaner, simpler and less error prone. Hopefully you enjoyed reading about it and can make use of some or all of the concepts in one of your next projects.",[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",1783525744108]