我一直非常轻松地使用数据库开始新的项目表面上是因为我在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表的数据库,该表与Customers和Suppliers具有一对一或一个关系(几乎就是我想要的)。
然后我开始测试这个用以下代码创建一个简单的控制台应用程序;
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时遇到异常;
在异常中引用的所有列都是可空的或在调用基本SaveChanges之前处理(与DateCreated和DateModified一样,或者像在RowVersion上一样在sqlserver上设置)。
很明显,我要么误解了正确的语法,要么误解了Table Per Type继承的整个概念。对于这种情况,这是一种合理的方法吗(我预计在以后的某个时候你可能想要限制对员工信息的访问,因此将所有内容合并到一个巨大的联系人表中似乎是错误的)?
如果这种方法是合理的,但语法错误可能会让某人知道正确的方法,也可能从那时起如何让Fred Flintstone成为供应商以及他目前所在的客户(或将会是如果数据已保存回数据库!)?
看起来问题中说明的错误是由于从迁移创建数据库时dot Net类型DateTime被直接映射到SqlType日期时间。
通过将以下代码添加到相关的上下文中
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
base.OnModelCreating(modelBuilder);
}
我能够成功地添加客户和供应商,因为我认为应该可以这样做。在撰写本文时,我还没有解决我问题的第二部分,但希望这会让通过搜索遇到这个问题的任何人都能轻松一点。