entity-framework - 数据库的每个表中都有多个公共(public)字段 CreatedOn 和 CreatedBy。怎样才能不重复每张 table

标签 entity-framework inheritance asp.net-core-mvc

场景:

public class Department
{
    public int DepartmentId { get; set; }
    public string DepartmentName { get; set; }
    public string Description { get; set; }
    public DateTime CreatedOn {get; set; }
    public string CreatedBy {get; set; }
}

public class TestItem
{
    public int TestItemId { get; set; }
    public string TestItemName { get; set; }
    public Department Department { get; set; }
    public int DepartmentId { get; set; }
    public DateTime CreatedOn {get; set; }
    public string CreatedBy {get; set; }
}

public class Patient
{
    public int PatientId { get; set; }
    public string PatientName { get; set; }
    public DateTime CreatedOn {get; set; }
    public string CreatedBy {get; set; }
}

问题是,每次创建表时,我都必须重复添加这两列。

但我想要这样-

public class EntryLog
{
   public int EntryLogId { get; set; }
   public DateTime CreatedOn {get; set; }
   public string CreatedBy {get; set; }
}

public class Department
{
    public int DepartmentId { get; set; }
    public string DepartmentName { get; set; }
    public string Description { get; set; }

    public EntryLog EntryLog { get; set; }
    public int EntryLogId { get; set; }
}

and so on...

class A { .. }

class B { .. }

但是在为部门或患者创建行时,它会产生问题[显示与其他表的外键冲突错误]。

在 EF core 中,有每个层次结构表 (TPH),但在这种情况下,每个表都将合并为一个表。但这并没有给我任何解决方案。

期待专家的建议...

最佳答案

底线是:使用 EntryLog 作为基本类型,并且不要将其告知 EF。让 EF-core 忽略基本类型很容易:仅注册派生类型。这样做时,EF-core 会将您的子类型映射到它们自己的表,就像它们没有通用类型一样。

现在 EntryLog 将不再需要 Id,它应该是抽象的:

public abstract class EntryLog
{
    public DateTime CreatedOnUtc { get; set; }
    public string CreatedBy { get; set; }
}

这是否足够取决于您的具体要求。有几种可能性。

1。无需额外配置

如果您对 EF 将应用于公共(public)属性的默认约定感到满意,那么您就完成了。 CreatedOnUtc 将映射到 DateTime2 列(在 Sql Server 中),CreatedBy 将映射到 nvarchar(max) 列在每个表中都有一个 EntryLog 实体。

但是,如果您确实需要自定义配置 - 例如,如果您想要将 CreatedBy 映射到 nvarchar(50) 列 - 应应用其他映射指令。当然,您仍然希望只对公共(public)属性进行一次映射——如果您在 TPH 方案中映射了基本类型,也会发生这种情况。如何做到这一点?

2。基本类型中的数据注释

最简单的选择是添加数据注释:

public abstract class EntryLog
{
    public DateTime CreatedOnUtc { get; set; }

    [MaxLength(50)]
    public string CreatedBy { get; set; }
}

仅此而已。

但是有些开发团队不想使用数据注释来映射指令。此外,EF 的流畅映射提供了比数据注释更多的选项。如果数据注释由于某种原因不符合要求,则必须应用流畅的配置。但您仍然只想配置一次公共(public)属性。实现这一目标的一个可行方法是使用 IEntityTypeConfiguration s 为每个 EntryLog 并让每个具体配置派生自基类。这提供了另外两个选项。

3。基类包含常规属性

选项 4 将阐明为什么我在这里谈论“常规属性”。它看起来像这样:

abstract class EntryLogConfiguration
{
    public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : EntryLog
    {
        // Just an example of how to configure a base property.
        builder.Property(e => e.CreatedBy).HasMaxLength(50);
    }
}

class DepartmentConfiguration : EntryLogConfiguration, 
    IEntityTypeConfiguration<Department>
{
    public void Configure(EntityTypeBuilder<Department> builder)
    {
        builder.Property(p => p.DepartmentName).HasMaxLength(100);
        ConfigureBase(builder);
    }
}

在上下文中:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.ApplyConfiguration(new DepartmentConfiguration());
}

4。使用阴影属性

Shadow properties是 EF-core 的一项新功能。

Shadow properties are properties that are not defined in your .NET entity class but are defined for that entity type in the EF Core model. The value and state of these properties is maintained purely in the Change Tracker.

假设您希望将 CreatedBy 作为类属性(因为您希望在 UI 中显示它),但只需要 CreatedOnUtc 作为在背景,不应该暴露。现在 EntryLog 将如下所示:

public abstract class EntryLog
{
    public string CreatedBy { get; set; }
}

因此属性CreatedOnUtc消失了。它已作为影子属性移至基本配置:

abstract class EntryLogConfiguration
{
    public void ConfigureBase<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : EntryLog
    {
        builder.Property(e => e.CreatedBy).HasMaxLength(50);
        builder.Property<DateTime>("CreatedOnUtc");
    }
}

现在您无法直接设置 CreatedOnUtc,只能通过 EF 的更改跟踪器。最好的位置是在上下文中重写 SaveChanges:

    public override int SaveChanges()
    {
        foreach (var entry in ChangeTracker.Entries<EntryLog>())
        {
            entry.Property<DateTime>("UpdatedOnUtc").CurrentValue = DateTime.UtcNow;
        }
        return base.SaveChanges();
    }

当然,如果 UpdatedOnUtc 是常规属性,则此覆盖也会派上用场,但您可以这样做

            entry.Entity.CreatedOnUtc = DateTime.UtcNow;

我希望这能给您足够的思考空间,以找出最适合您的选项。

关于entity-framework - 数据库的每个表中都有多个公共(public)字段 CreatedOn 和 CreatedBy。怎样才能不重复每张 table ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46367828/

相关文章:

c# - ASP.NET 核心 2 : Ajax calls to API with [Authorize] fail the preflight request

asp.net-core-mvc - 从 ASP.net Core MVC 2 应用程序发送 Amazon 简单电子邮件

asp.net-mvc - 无法定义两个对象之间的关系,因为它们附加到不同的ObjectContext对象mvc 2

c++ - 类成员标识符的解析如何在 C++ 中工作

c++ - 使用具有继承性的构造函数基类和成员初始值设定项列表

python - 如何让子类在不重新定义父变量的情况下使用它们?

asp.net-core - ActionResult参数含义

c# - 无法在 LINQ to Entities 查询中构造实体

c# - 创建包含外部变量的 LambdaExpression

entity-framework - 如何在 Entity Framework 中使用内部联接删除或更新?