EF6.x从数据库优先迁移到代码优先,每种类型的表

时间:2017-10-23 09:34:52

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

我一直非常轻松地使用数据库开始新项目表面上是因为我在SSMS工作更加舒服。尽管如此,学习新技巧还是不错的,所以我决定逆转这个过程,从相对简单的东西开始,以便学习正确的技巧和潜在的陷阱。为此,请考虑购买和销售商品的非常简单的业务的以下方案。它将同时拥有客户和供应商,并随着它的增长,员工。首先,我简单地处理了客户和供应商,但考虑到客户也可能成为供应商的可能性,反之亦然。

从纯粹的程序化角度考虑这一点,很明显客户和供应商具有共同的属性,并且已经明确表示他们从基类继承这些属性是有意义的。从实体框架的角度来看,这向我建议了每个类型继承的表。

因此,在一个简单的类库中,我定义了以下实体:

     public abstract class EntityBase
    {
        public int Id { get; set; }

        public DateTime DateCreated { get; set; }

        public DateTime DateModified { get; set; }

        [Timestamp]
        public byte[] RowVersion { get; set; }
    }

  [Table("Contacts", Schema = "Contacts")]
    public abstract class Contact : EntityBase
    {


        public string ContactName { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        public string CompanyName { get; set; }


        public string Email { get; set; }

        public string WorkTelephoneNumber { get; set; }


        public string MobileTelephone1 { get; set; }

        public string MobileTelephone2 { get; set; }
    }

 [Table("Customers", Schema = "Contacts")]
    public class Customer : Contact
    {
        public DateTime? FirstInvoiceDate { get; set; }

        public DateTime DateStarted { get; set; }
    }

  [Table("Suppliers", Schema = "Contacts")]
    public class Supplier : Contact
    {
        public DateTime DateStarted { get; set; }

        public DateTime? FirstInvoiceDate { get; set; }

    }

如图所示,有一个简单的客户背景;

 public class CustomerContext : DbContext
{

    public CustomerContext() : base("Ef6Test")
    {
    }



    public DbSet<Customer> Customers { get; set; }

    public override int SaveChanges()
    {
        AddTimestamps();
        return base.SaveChanges();
    }




    private void AddTimestamps()
    {
        var entities = ChangeTracker.Entries().Where(x => x.Entity is EntityBase && (x.State == EntityState.Added || x.State == EntityState.Modified));

        foreach (var entity in entities)
        {
            if (entity.State == EntityState.Added)
            {
                ((EntityBase)entity.Entity).DateCreated = DateTime.UtcNow;

            }

            ((EntityBase)entity.Entity).DateModified = DateTime.UtcNow;

        }
    }

    public override async Task<int> SaveChangesAsync()
    {
        AddTimestamps();
        return await base.SaveChangesAsync();
    }

}

供应商有此复制品。

如果我然后针对此运行迁移,它将创建一个包含Contacts表的数据库,该表与客户和供应商之间存在一个零或一个关系(几乎就是我想要的)。

然后我开始测试这个,用以下代码创建一个简单的控制台应用程序;

    class Program
{
    static void Main(string[] args)
    {

        InsertCustomers();
        InsertSuppliers();
        ShowCustomers();
        ShowSuppliers();
        Console.ReadLine();
    }

    private static void InsertCustomers()
    {
        var customer1 = new Customer
        {
            FirstName = "Mickey",
            LastName = "Mouse",
            DateStarted = DateTime.Now
        };

        var customer2 = new Customer
        {
            FirstName = "Fred",
            LastName = "Flintstone",
            DateStarted = DateTime.Now
        };

        using (var context = new CustomerContext())
        {
            context.Customers.Add(customer1);
            context.Customers.Add(customer2);
            context.SaveChanges();
        }
    }


    private static void InsertSuppliers()
    {
        var supplier1 = new Supplier
        {
            FirstName = "Roger",
            LastName = "Rabbit",
            DateStarted = DateTime.Now
        };

        var supplier2 = new Supplier
        {
            FirstName = "Wylie",
            LastName = "Kyote",
            DateStarted = DateTime.Now
        };

        using (var context = new SupplierContext())
        {
            context.Suppliers.Add(supplier1);
            context.Suppliers.Add(supplier2);
            context.SaveChanges();
        }
    }
    static void ShowCustomers()
    {
        using (var ctxt = new CustomerContext())
        {
            var customers = ctxt.Customers.ToList();
            foreach (var cust in ctxt.Customers)
            {
                Console.WriteLine($"{cust.FirstName} {cust.LastName}");
            }
        }
    }
    static void ShowSuppliers()
    {
        using (var ctxt = new SupplierContext())
        {
            var suppliers = ctxt.Suppliers.ToList();
            foreach (var sup in ctxt.Suppliers)
            {
                Console.WriteLine($"{sup.FirstName} {sup.LastName}");
            }
        }
    }

    private static void MakeCustomerASupplier()
    {
        var modCust = new Supplier
        {
            // would like Fred Flintstone to become a supplier as well.
        };
    }
}

当我运行此操作时,我在客户上下文中第一次调用Base Save Changes时遇到异常;

enter image description here

异常中引用的所有列都可以为空或在调用基本SaveChanges之前处理(与DateCreated和DateModified一样,或者像在RowVersion上一样在sqlserver上设置。

显然,我要么误解了正确的语法,要么误解了Table Per Type继承的整个概念。对于这种情况,这是一种合理的方法吗(我设想以后可能会限制对员工信息的访问,因此将所有内容合并到一个巨大的联系人表中似乎是错误的)?

如果方法合理,但语法错误可能会让某人了解正确的方法,也可以从那时起如何让Fred Flintstone成为供应商以及他目前的客户(或者如果数据已经保存回数据库了!)?

1 个答案:

答案 0 :(得分:0)

问题中显示的错误似乎是由于从迁移创建数据库时dot Net类型DateTime直接映射到SqlType日期时间。

将以下代码添加到相关的上下文

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
        base.OnModelCreating(modelBuilder);
    }

我能够成功添加客户和供应商,因为我认为应该可以这样做。在撰写本文时,我还没有解决我问题的第二部分,但希望这会让所有通过搜索遇到这个问题的人的生活变得容易一些。