C# IDbSetExtensions.AddOrUpdate和关系
IDbSetExtensions.AddOrUpdate旨在帮助编写在数据库为空或已填充的情况下都能正常工作的代码。但是链接对象需要不同的代码。当数据库为空时,对象还没有ID,您可以通过指定导航属性来链接它们。但是,当对象已经存在时,导航属性不起作用,您需要直接设置外键。导航属性在这两种情况下都适用于代理,但代价是没收POCO。编辑:实际上,当两个实体都是旧的时,代理不起作用 当EF尝试将CountryID设置为0时,此示例在第二次SaveChanges调用中崩溃:C# IDbSetExtensions.AddOrUpdate和关系,c#,entity-framework,C#,Entity Framework,IDbSetExtensions.AddOrUpdate旨在帮助编写在数据库为空或已填充的情况下都能正常工作的代码。但是链接对象需要不同的代码。当数据库为空时,对象还没有ID,您可以通过指定导航属性来链接它们。但是,当对象已经存在时,导航属性不起作用,您需要直接设置外键。导航属性在这两种情况下都适用于代理,但代价是没收POCO。编辑:实际上,当两个实体都是旧的时,代理不起作用 当EF尝试将CountryID设置为0时,此示例在第二次SaveChanges调用中崩溃: public class C
public class Country
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
public class Person
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual int CountryID { get; set; }
public virtual Country Country { get; set; }
}
public class Context : DbContext
{
public DbSet<Person> Person { get; set; }
public DbSet<Country> Country { get; set; }
}
class Program
{
static void Foo()
{
using (var db = new Context())
{
//var c = new Country();
var c = db.Country.Create();
c.Name = "usa";
db.Country.AddOrUpdate(x => x.Name, c);
//var p = new Person();
var p = db.Person.Create();
p.Name = "billg";
p.Country = c;
db.Person.AddOrUpdate(x => x.Name, p);
db.SaveChanges();
}
}
static void Main()
{
Database.SetInitializer<Context>(new DropCreateDatabaseAlways<Context>());
Foo();
Foo();
}
}
公共类国家
{
公共虚拟整数ID{get;set;}
公共虚拟字符串名称{get;set;}
}
公共阶层人士
{
公共虚拟整数ID{get;set;}
公共虚拟字符串名称{get;set;}
公共虚拟int CountryID{get;set;}
公共虚拟国家{get;set;}
}
公共类上下文:DbContext
{
公共DbSet Person{get;set;}
公共数据库集国家{get;set;}
}
班级计划
{
静态void Foo()
{
使用(var db=new Context())
{
//var c=新国家();
var c=db.Country.Create();
c、 Name=“美国”;
db.Country.AddOrUpdate(x=>x.Name,c);
//var p=新人();
var p=db.Person.Create();
p、 Name=“billg”;
p、 国家=c;
db.Person.AddOrUpdate(x=>x.Name,p);
db.SaveChanges();
}
}
静态void Main()
{
SetInitializer(新的DropCreateDatabaseAlways());
Foo();
Foo();
}
}
如何使用AddOrUpdate
IDbSetExtensions.AddOrUpdate旨在帮助编写在数据库为空或已填充的情况下都能正常工作的代码
AddOrUpdate
仅用于代码优先迁移的Seed
方法。它不应该在普通代码中使用,因为它有很大的开销和一些限制。开销是对数据库和反射的额外查询。限制是它只检查您正在传递的主要实体,而不检查其关系。每个关系都应该通过单独调用AddOrUpdate
来处理:
static void Foo()
{
using (var db = new Context())
{
var c = new Country() {Name = "abc"};
db.Country.AddOrUpdate(x => x.Name, c);
var p = new Person()
{
Name = "me",
CountryID = c.ID,
Country = c
};
db.Person.AddOrUpdate(x => x.Name, p);
db.SaveChanges();
}
}
我强烈建议你阅读它,我缩短了我的代码粘贴在这里。我的应用程序仅在dbmigtorinsconfiguration.Seed覆盖中使用AddOrUpdate。您的示例有效,但我对同时指定Country和CountryID感到不安。您是否建议始终同时指定这两个选项?我的应用程序使用复合键,只分配导航属性更容易、更清晰、更不容易出错。@BrunoMartinez AddOrUpdate不会设置导航属性(和数据库)所依赖的外键值-即使该实体已被“添加或更新”或直接查询(EF应该能够修复关系)。实际上,您只需设置FK,除非稍后需要在种子中导航关系。此外,如果要将关系用作
identifierExpression
的一部分,则必须使用FK Id,因为它必须是基元或枚举值,并且属性映射到列-不能使用其他实体。RE:“只分配导航属性更容易、更清晰、更不容易出错。”我建议为您的实体添加一些扩展方法到AddRelatedEntity(RelatedEntity){..}
,或者只使用本机DbSet
方法基本上编写您自己的AddOrUpdate()
。令人沮丧的是,此API表面没有遵循与其他DbContext/DbSet操作相同的约定\_(ツ)_/¯