实体框架 - 非关键关系

时间:2015-10-08 18:15:14

标签: c# entity-framework ef-code-first entity-framework-6

问题

我有一种情况,我需要使用Entity Framework 6,Code First,以及无法更改的旧数据库结构。该数据库有一个非常通用的表,它存储基于文本的数据以及一些非关键数据,这些数据可用于将记录与另一个表相关联。

举例说明:

enter image description here

假设 Notes 表的模型如下:

[Table("Notes")]
public class Notes
{
    [Key]
    public int RecordId { get; set; }

    [Required]
    public string RelatedTableName { get; set; }

    [Required]
    public int RelatedTableRecordId { get; set; }

    [Required]
    public string NotesText { get; set; }
}

然后我有另一个模型,看起来像这样:

[Table("Drivers")]
public class Drivers
{
    [Key]
    public int RecordId { get; set; }

    [Required]
    public string DriverName { get; set; }

    public ICollection<Notes> DriverNotes { get; private set; }
}

无外键链接表。 Drivers表通过RelatedTableName和RelatedTableRecordId字段链接到Notes表。

从数据库中读取数据并使用实体框架为模型添加水分没有问题。

我遇到的问题是我希望能够在一个事务中保存新驱动程序及其新创建的Notes,并将RelatedTableRecordId字段设置为驱动程序的主键。

如果存在外键,实体框架会知道回填该财产,但在这种情况下,它不知道这种关系。

要点

  1. 数据库结构不得更改。
  2. 必须使用Entity Framework 6 Code First
  3. 必须能够使用执行策略。
  4. 需要非关键字段之间的关系。
  5. 需要能够在一次交易中保留所有数据。
  6. 我尝试过什么

    我在审核类型数据方面遇到了类似问题,并通过执行类似以下操作解决了这个问题(注意这里非常伪):

        public override int SaveChanges()
        {
            int changes = 0;
    
            //Disable the current execution strategy as the default ones do not support user instantiated transactions.
            this.ContextConfiguration.SuspendExecutionStrategy();
    
            try
            {
                //Wrap a whole transaction inside an execution strategy so that auditing can be combined with regular saving of changes.
                this.ExecutionStrategy.Execute(
                    () =>
                    {
                        using (var transaction = this.Database.BeginTransaction())
                        {
                            //Reset the change count so that it doesn't increase each time the transaction fails.
                            changes = 0;
    
                            //Remove any audit records created by previous failed transactions.
                            this.AuditTableChanges.Local.Clear();
    
                            //Evaluate the change tracker to identify entities which will potentially require an audit trail.
                            var insertedEntities = this.ChangeTracker.Entries().Where(entryEntity => entryEntity.State == EntityState.Added).ToList();
    
                            //Save all changes to get identities.
                            changes = base.SaveChanges();
    
                            //Create the audit trail for inserted entities. This step must occur after the initial call to SaveChanges() so that the identities are set.
                            foreach (DbEntityEntry entryEntity in insertedEntities)
                            {
                                //For each inserted record, get the audit record entries and add them
                                foreach (AuditTableChange auditTableChange in GetAuditRecords(entryEntity, AuditTableChangeType.Insert).Result)
                                    this.AuditTableChanges.Add(auditTableChange);
                            }
    
                            //Save the audit trail for inserted entities.
                            changes += base.SaveChanges();
    
                            //Commit all changes to the database
                            transaction.Commit();
                        }
                    });
            }
            finally
            {
                //Re-enable the execution strategy so that other calls can benefit from the retry policy.
                this.ContextConfiguration.UnSuspendExecutionStrategy();
            }
    
            return changes;
        }
    

    这对审计数据很好,因为实现隐藏在框架中。我不希望我的开发团队在每次持久记录时都必须完成上述所有操作。

    以其简单化的形式,这就像我希望人们一样:

        public void CreateDriver()
        {
            using (MyContext context = new MyContext())
            {
                Drivers driver = new Drivers();
                driver.DriverName = "Joe Bloggs";
    
                Notes driverNote = new Notes();
                driverNote.RelatedTableName = "Drivers";
                driverNote.NotesText = "Some very long text";
    
                driver.DriverNotes.Add(driverNote);
    
                context.Drivers.Add(driver);
    
                context.SaveChanges();
            }
        }
    

    在某种程度上,我想要一个存在于代码中但不存在于数据库中的外键,以便实体框架知道填写RelatedTableRecordId字段。我已经阅读了一些关于攻击EDMX的文章,但这个项目纯粹是Code First。

    关于堆栈溢出的旧问题有些类似但与较旧版本的实体框架有关,并且没有多少帮助或者具有与上述相同的详细信息。

    我希望有人可能遇到过类似的问题并且答案可能涉及一些自定义映射/元数据或某些覆盖实体框架逻辑。

    任何帮助都将不胜感激。

    谢谢,

    Greg

0 个答案:

没有答案