EF6-TPT-具有可审核列的继承

时间:2020-10-09 11:01:49

标签: entity-framework inheritance audit ef-database-first table-per-type

TL; DR

我需要在现有数据库上实现TPT。我有表格:实体,公司和人。 Company和Person通过其主键(一对一)-EntityId与Entity链接。

有关表包含审核列: CreateDate(非NULL),ChangeDate,CreateUserId(非NULL),ChangeUserId,CreateTransactionId,ChangeTransactionId ,我无法删除它们。我能够实现TPT和GET正常工作,但是当我尝试使用EF6将记录插入到表中时会引发错误,例如列之一中的CreateDate不能为null。原因是:审核数据已映射到Entity表,并且没有将该信息添加到Person / Company表中。

TPT是通过Person和Company类从实体派生的方式实现的。

在使用TPT时如何强制EF6在两个表中保存属性?实体拆分不起作用,它会引发一个错误,即非键属性无法映射两次。

如果不是EF6,那么是否有一种方法(例如前/后触发器)可以用数据填充问题列?

背景-当前实施-使用EF6

数据库

我有一个看起来像这样的现有数据库: CurrentDatabase

如您所见:

  • 实体是一个表,用于存储“人”和“公司”的所有常用信息
  • 人员和公司扩展特定实体的数据
  • 实体人和实体公司(橙色标记)之间存在一对一关系
  • 应用程序通过CustomerEntityId与实体表(一对多)(橙色标记)链接
  • 所有表均包含审核列(黄色标记): CreateDate(非NULL),ChangeDate,CreateUserId(非NULL),ChangeUserId,CreateTransactionId,ChangeTransactionId 。应用程序表也包含它们,但为简单起见,我决定不在打印屏幕上显示它们。

代码

    public class ApplicationEntity : AbstractAuditableEntity
    {
        public override int Id { get; set; }

        public int CustomerId { get; set; }
        public virtual CustomerEntity Customer { get; set; }
    }

    public class Entity : AbstractAuditableEntity
    {
        public override int Id { get; set; }
        public string Type { get; set; }
        public string Name { get; set; }
        public int? Status { get; set; }
        public string Notes { get; set; }
    }

    public class CustomerEntity : AbstractAuditableEntity
    {
        public override int Id { get; set; }
        public int? Title { get; set; }
        public string Surname { get; set; }
        public string MiddleName { get; set; }
        public string Forename { get; set; }

        public virtual Entity Entity { get; set; }
    }

    public class CompanyEntity : AbstractAuditableEntity
    {
        public override int Id { get; set; }
        public string CompanyName { get; set; }
        public Int16 HostCompany { get; set; }

        public virtual Entity Entity { get; set; }
    }

所有现有类都派生自Abstract AuditableEntity,这意味着它们包含: CreatedByUserId,CreatedDate,CreateTransactionId,ChangedByUserId,ChangedDate,ChangeTransactionId

CustomerEntity(与Person表链接)和CompanyEntity具有属性Entity。 此外,ApplicationEntity包含一个CustomerEntity的外键,借助它,您可以从DB获取有关Entity本身的信息的对象,例如

var application = _unitOfWork.GetRepository<ApplicationEntity>().GetById(1);
var entityType = application.Customer.Entity.Type;

当然,所有配置都允许我在DB中进行GET / INSERT / UPDATE数据:

  • 我已将所有属性映射到有效列,
  • 关系是正确的

问题描述

现在,我必须对代码进行一些更改。要求是将ApplicationEntity与CompanyEntity链接起来,并假设如果某个应用程序拥有一个Company,则该公司不能与Customer链接。话虽这么说,我决定用一些TPC来实现TPT方法。

为什么?如果CustomerEntity和CompanyEntity源自相同的BaseEnterpriseEntity,然后又从Entity衍生,则可以将其与ApplicationEntity链接。一切似乎都有效。当我从存储库中获取对象时,所有字段都将正确填充。我唯一的问题是当我插入/更新数据时。我收到一条错误消息,指出无法处理数据库查询,因为其中一个属性(CreateDate)为NULL。之所以有意义,是因为该字段也在实体表中,并且EF在那里将其映射(请参见下面的查询)。

exec sp_executesql N'INSERT [dbo].[Entity]([EntityType], [EntityName], [Status], [Notes], [CreateDate], [CreateUserId], [CreateTransactionId], [ChangeDate], [ChangeUserId], [ChangeTransactionId], [CommunicationRequirement])
VALUES (@0, @1, NULL, NULL, @2, @3, @4, NULL, NULL, NULL, NULL)
SELECT [EntityId]
FROM [dbo].[Entity]
WHERE @@ROWCOUNT > 0 AND [EntityId] = scope_identity()',N'@0 varchar(8000),@1 varchar(8000),@2 datetime2(7),@3 int,@4 uniqueidentifier,@5 int',@0='P',@1='Victoria McQueen',@2='2020-10-08 14:34:07.9033492',@3=-1,@4='0000000-0000-0000-0000-000000000000'
exec sp_executesql N'INSERT [dbo].[Person]([EntityId], [Title], [Surname], [MiddleNames], [Forename])
VALUES (@0, @1, @2, NULL, @3)
',N'@0 int,@1 int,@2 varchar(8000),@3 varchar(8000)',@0=7,@1=1254,@2='McQueen',@3='Victoria',@4='',@5='Vicki'

我试图将属性拆分为映射到两个表,但是EF表示无法完成:

“公司”类型的属性只能映射一次。非关键属性“ CreatedDate”被映射多次。确保Properties方法仅指定每个非关键属性一次。

最后

我想拥有的是:

  • 表格的“审核”列必须保留
  • 创建新公司时,需要填充每个表的数据。由于我无法删除审核表,因此必须使用相同的信息填充它们,例如实体表将包含与公司表相同的CreateDate。 我已经考虑过要在company表上添加某种预插入触发器,但是看来它不适用于EF查询。

有什么建议吗?

当前类的实现:

    public class ApplicationEntity : AbstractAuditableEntity
    {
        public override int Id { get; set; }

        public int CustomerId { get; set; }
        public virtual BaseEnterpriseEntity Customer { get; set; } // or Entity as type
    }

    public class Entity : AbstractAuditableEntity
    {
        public override int Id { get; set; }
        public string Type { get; set; }
        public string Name { get; set; }
        public int? Status { get; set; }
        public string Notes { get; set; }
    }

    public abstract class BaseEnterpriseEntity : Entity
    {
        public override int Id { get; set; }
    }

    public class CustomerEntity : BaseEnterpriseEntity
    {
        public override int Id { get; set; }
        public int? Title { get; set; }
        public string Surname { get; set; }
        public string MiddleName { get; set; }
        public string Forename { get; set; }
    }

    public class CompanyEntity : BaseEnterpriseEntity 
    {
        public override int Id { get; set; }
        public string CompanyName { get; set; }
        public Int16 HostCompany { get; set; }
    }

0 个答案:

没有答案
相关问题