Hardening property access with EF Core Power Tools
When using EF Core Power Tools (which you should if you're doing DB first on EF Core, it generates everything with public get and set by default.
I've been trying to make my domain models less anemic and more secure, so I wanted to change this to:
- read only properties and collections from the outside
- no accessible open constructors
Luckily EFPT provides customization options via T4 templating, so it's a pretty straightforward change once you know what to tweak.
Here's how to get started using the CLI.
- In efcpt.config.json, in
code-generation, set eitheruse-t4oruse-t4-splitto true. - Run the reverse engineering
dotnet efcpt "<connectionstring>" postgres - Open the newly generated
EntityType.t4
Here's the relevant modifications:
foreach (var property in EntityType.GetProperties().OrderBy(p => p.GetColumnOrder() ?? -1))
{
...
//1. set the setter as internal
public <#= code.Reference(property.ClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; internal set; }<#= needsInitializer ? " = null!;" : "" #>
<#
}
foreach (var navigation in EntityType.GetNavigations())
{
//helper for the backing field name
var privateName = navigation.Name.Substring(0, 1).ToLower() + navigation.Name.Substring(1);
...
//2. for collections create a private backing field and change the type to IReadOnlyCollection
private List<<#= targetType #>> _<#= privateName #> = new List<<#= targetType #>>();
public virtual IReadOnlyCollection<<#= targetType #>> <#= navigation.Name #> => _<#= privateName #>;
}
...
//3. create an empty private constructor
private <#= EntityType.Name #>(){ }
}
Key points:
- use internal setter for properties and navigation properties
- use a private backing
Listfor collection navigations (and skip navigations) - use
IReadOnlyCollectioninstead ofICollectionas the collection type - implement a private open constructor
- EF Core requires an open constructor but making it private prevents usage from outside
This makes for a good base for building your own functionality on top of the database model and it's very easy to get going. Big thanks to EFPT once again!
Thoughts, comments? Send me an email!