Home > Blockchain >  EF7 DB-First: Scaffold-DBContext produces model without constructor or setter on ICollection
EF7 DB-First: Scaffold-DBContext produces model without constructor or setter on ICollection

Time:01-31

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:

  1. No constructor with HashSet in EF7
  2. No setter on the ICollection

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.

  1. Why was it done?
  2. How can I work around the limitations it brings?
  3. Is there an option to have EF7 stick to the EF6 way?

CodePudding user response:

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.

CodePudding user response:

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.

  1. Add the HashSet in a constructor:
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("");
}
  1. Add a setter to the navigation property for collections by replacing this line:
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.

  • Related