【问题标题】:Entity Framework Core properties without setters没有设置器的实体框架核心属性
【发布时间】:2020-04-18 13:35:28
【问题描述】:

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

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

    public int Id { get; }

    public string Name { get; }
}

因此,当我想检查此类 Sample 是否已存在时,我已经删除了所有公共设置器,因此在我的代码中某处

_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 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。

【问题讨论】:

标签: c# .net .net-core entity-framework-core entity-framework-core-3.1


【解决方案1】:

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

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

来自上面的链接:支持字段允许 EF 读取和/或写入字段而不是属性。当类中的封装被用于限制使用和/或增强应用程序代码访问数据的语义时,这可能很有用,但值应该在不使用这些限制的情况下从数据库读取和/或写入数据库/增强。

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

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

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

我会自己添加它,这样会更容易。 所以我的课看起来像这样。 您需要上面的映射,该映射解决了我的财产是私有的问题。如果没有映射,这将失败。

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

【讨论】:

  • 与使用私有设置器相比,支持字段解决方案有什么优势吗?我认为没有,除非那更明确,但有更大的样板。
  • 不,我也不这么认为,实际上在映射中使用字符串文字应该会阻止您使用它。在我的研究中,我什至试图让你的领域真正只读。所以你只能在构造函数内部实例化,但没有运气,我通过这个很有趣 -> stackoverflow.com/a/1050804/3902958
【解决方案2】:

谁能解释一下为什么需要这个setter,例如Id不需要那个setter。

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

按照惯例,所有带有 getter 和 setter 的公共属性都将包含在模型中。

它没有说明为什么,但这并不重要,因为它是“按设计”工作的。

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

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

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

【讨论】:

  • 你说得对,我有这个builder.Property(e =&gt; e.Id);。这样它就适用于 Id 字段。
  • 另外值得一提的是,仅使用示例中的 EF Core 约定,没有私有设置器 - 当对象已经初始化时,我们将失去编辑属性的可能性。 stackoverflow.com/questions/36792392/… 所以似乎最好的解决方案是只保留私有 setter 或使用 @panoskarajohn 已经表示的支持字段。
  • “我们正在失去编辑的可能性” - 当然,这是 getter only 属性的行为(相当于只读字段)。使用显式可写支持字段与使用私有设置器的自动属性相同 - EF Core 无论如何都会使用关联的支持字段。
猜你喜欢
  • 2018-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 1970-01-01
  • 2010-10-03
相关资源
最近更新 更多