Reputation: 567
After upgrading the EntityFramework NuGet packages (.Design, .SqlServer, and .Tools) to 7.0 and using scaffold-dbcontext in the PM console to re-generate (reverse engineer) the model classes from the database, I'm seeing differences in the models that precipitate many project build errors.
Here is an example of a (dummy) model class that scaffold-dbcontext
produces under EF6:
public partial class Foo
{
public Foo()
{
Deps = new HashSet<Dep>();
}
public int FooId { get; set; }
public int BossId { get; set; }
public virtual Boss Boss { get; set; } = null!;
public virtual ICollection<Dep> Deps { get; set; }
}
And here is what I get with EF7 against the same database:
public partial class Foo
{
public int FooId { get; set; }
public int BossId { get; set; }
public virtual Boss Boss { get; set; } = null!;
public virtual ICollection<Dep> Deps { get; } = new List<Dep>();
}
Notice:
In many places in my code I'm building new entities with new dependent collections (all from external data) and adding them to the context. The lack of setter/constructor makes that a problem.
I've had difficulty finding any documentation about this (breaking, to me) change.
Upvotes: 2
Views: 958
Reputation: 1330
As of version 7.0.11 of EF core and dotnet-ef tool, the scaffolding now adds the setter to the collection properties. This should address the Property or Indexer Foo.Deps cannot be assigned to -- it is read-only
error.
public virtual ICollection<Dep> Deps { get; set;} = new List<Dep>();
Upvotes: 0
Reputation: 567
As a follow-up to David Browne's answer regarding the use of T4 templates, here are two T4 snippets that got the job done for me in EntityType.t4.
public partial class <#= EntityType.Name #>
{
<#if (EntityType.GetNavigations().Count(x => x.IsCollection) > 0) { #>
public <#= EntityType.Name #>()
{
<#
foreach (var navigation in EntityType.GetNavigations())
{
if (navigation.IsCollection)
{
#>
<#= navigation.Name#> = new HashSet<<#= navigation.TargetEntityType.Name#>>();
<#
}
}
#>
}
<# WriteLine("");
}
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new List<<#= targetType #>>();
with this:
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; set; }
These edits may not encompass all scenarios, so please feel free to improve them.
Upvotes: 2
Reputation: 88996
Replacing HashSet with List is for better performance on common scenarios.
Removing the setter and constructor is the simplest pattern for common scenarios. And the introduction of T4 templates to customize reverse engineering makes changing the defaults less impactful since you can customize the scaffolding.
Upvotes: 2