Entity Framework 没有 setter 的核心属性

Entity Framework Core properties without setters

在我的 .net core 3.1 应用程序中,我想将属性封装在某个实体中:

public class Sample : AuditableEntity
{
    public Sample(string name)
    {
        Name = name;
    }

    public int Id { get; }

    public string Name { get; }
}

所以我删除了所有 public setters 并且因为我想检查这样的样本是否已经存在时我的代码中的某个地方

_context.Samples.Any(r => r.Name == name)

该行导致错误:System.InvalidOperationException: 'No suitable constructor found for entity type 'Sample'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'name' in 'Sample(string name)'.'

所以我在代码中添加了空构造函数

public class Sample : AuditableEntity
{
    public Sample() { } // Added empty constructor here

    public Sample(string name)
    {
        Name = name;
    }

    public int Id { get; }

    public string Name { get; }
}

现在该行导致错误:System.InvalidOperationException: 'The LINQ expression 'DbSet<Sample> .Any(s => s.Name == __name_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'

但如果我将私有集添加到 Name(或 public),那么一切正常(即使没有空构造函数)。

public class Sample : AuditableEntity
{
    public Sample(string name)
    {
        Name = name;
    }

    public int Id { get; }

    public string Name { get; private set; } // added setter, removed empty constructor
}

任何人都可以解释为什么这个 setter 是必需的,例如 Id 不需要 setter.

这与 Farhad Jabiyev 评论中提到的反射有关。 EF 找不到 属性,它被隐藏了。当您将其设置为 private set 时,EF 可以通过反射访问它,因此一切正常。

这可以通过支持字段来完成https://docs.microsoft.com/en-us/ef/core/modeling/backing-field

From link above: Backing fields allow EF to read and/or write to a field rather than a property. This can be useful when encapsulation in the class is being used to restrict the use of and/or enhance the semantics around access to the data by application code, but the value should be read from and/or written to the database without using those restrictions/enhancements.

这意味着您必须像这样通过流利 API 将映射添加到您的支持字段。

modelBuilder.Entity<Sample >()
            .Property(b => b.Name)
            .HasField("_name"); // this would have to be the name of the backing field

要访问自动道具的支持字段,您可以使用此-> Is it possible to access backing fields behind auto-implemented properties?

我自己加吧,这样会更简单。 所以我的 class 看起来像这样。 您需要上面的映射,该映射解决了我的 属性 是私有的问题。如果没有映射,这将失败。

public class Sample : AuditableEntity
{
    private string _name; //now EF has access to this property with the Mapping added
    public Sample(string name)
    {
        _name = name;
    }

    public int Id { get; } 

    public string Name => _name;
}

请看一下勒曼的做法-> https://www.youtube.com/watch?v=Z62cbp61Bb8&feature=youtu.be&t=1191

Can anyone explain me why this setter is required, for instance Id does not require that setter.

实际上两者都需要 setter。解释包含在 EF Core Included and excluded properties 文档主题中并且很简单:

By convention, all public properties with a getter and a setter will be included in the model.

它没有说 为什么 ,但这并不重要,因为它就是这样工作的 "by design"。

因此,要么将私有 setter 添加到您的属性中,要么使用 fluent API 将它们显式包含在实体模型中(从而覆盖 EF Core 约定),例如Sample class 仅 getter 属性:

modelBuilder.Entity<Sample>(builder =>
{
    builder.Property(e => e.Id);
    builder.Property(e => e.Name);
});

我个人觉得添加 private setter 更容易,也更不容易出错。