Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 分配实体实例而不是实体id将创建新记录_C#_Entity Framework - Fatal编程技术网

C# 分配实体实例而不是实体id将创建新记录

C# 分配实体实例而不是实体id将创建新记录,c#,entity-framework,C#,Entity Framework,我有两张桌子: 公共类财政 { ……其他领域 公共int FiscalYears_Id{get;set;} } 公共类SkipHeader { ……其他领域 公共int FiscalYears_Id{get;set;} 公共虚拟财政部{get;set;} } 正在尝试创建新的SkipHeader,如下所示: var skipHeader=new skipHeader() { …将其他字段分配给 FiscalYear=Session.FiscalYear, } 将导致数据库创建新的Fi

我有两张桌子:

公共类财政
{   
……其他领域
公共int FiscalYears_Id{get;set;}
}
公共类SkipHeader
{
……其他领域
公共int FiscalYears_Id{get;set;}
公共虚拟财政部{get;set;}
}
正在尝试创建新的
SkipHeader
,如下所示:

var skipHeader=new skipHeader()
{
…将其他字段分配给
FiscalYear=Session.FiscalYear,
}
将导致数据库创建新的
FiscalYear
记录,而不是使用
Session.FiscalYear
,它只是在程序启动时分配给的静态属性。但是,如果我改为分配
FiscalYears\u Id

var skipHeader=new skipHeader()
{
…将其他字段分配给
FiscalYears\u Id=Session.FiscalYear.FiscalYears\u Id,
}
程序按预期使用现有记录


这个错误让我和我的同事逃避了好几个月!现在我找到了一个解决方案,我想知道为什么会出现这种情况?

假设您的
DbContext
具有范围(每个请求)依赖关系的标准设置-原因是
DbContext
的新实例不跟踪
会话.FiscalYear
实例-因此它会创建新的。另一种解决方法是使用:


关于EF中的更改跟踪器。

假设您有非常标准的设置,其中
DbContext
的作用域(每个请求)依赖项-原因是
DbContext
的新实例不跟踪
会话。FiscalYear
实例-因此它会创建新的。另一种解决方法是使用:

关于EF中的更改跟踪器

这个错误让我和我的同事逃避了好几个月!现在我找到了一个 解决方案,我想知道为什么会出现这种情况

发生这种情况是因为DbContext不知道您的FiscalYear对象实例,例如它是表示新记录还是现有记录

以以下为例:

var fiscalYear = new FiscalYear { Id = 4, Name = "2019/20" };
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
FiscalYear fiscalYear = null;
using (var context = new AppDbContext())
{
    fiscalYear = context.FiscalYears.Single(x => x.Id == 4);
}

using (var context = new AppDbContext())
{
    var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
    context.SkipHeaders.Add(skipHeader);
    context.SaveChanges();
}
fiscalYear
在这个实例中是一个对象实例,它被赋予了ID和名称。当我们将它与一个新的SkipHeader关联并将SkipHeader添加到DbContext时,EF将看到这个fiscalYear。因为它不是上下文跟踪的对象,所以它将其视为一个新实体,就像SkipHeader一样

根据您的实体如何配置来处理PK,将决定发生什么

如果您的PK(Id)设置为标识列(DB将填充),则将插入FISCALIEAR并为其分配下一个可用的Id值。调用
SaveChanges()
后,fiscalYear.Id将是“6”或“22”,或者下一个分配给它的新Id将是什么。(不是“4”) 如果您的PK不是标识列(应用程序将填充),并且ID 4的DB中已经存在一个FiscalYear行,则EF将在
SaveChanges()
上引发重复的密钥异常

人们感到困惑的是,他们假设由于FiscalYear在某一点(比如在web请求期间)是从DbContext加载的,所以在传递到该DbContext范围之外的另一个方法时,它仍然以某种方式充当跟踪实体。(在另一个更新web请求期间)它不是。例如,当web请求从客户端接受FinancialYear作为参数时,它正在反序列化FinancialYear。就EF而言,这与上面的
新财务年度{}
示例完全相同。DbContext不知道该实体

以以下为例:

var fiscalYear = new FiscalYear { Id = 4, Name = "2019/20" };
var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
context.SkipHeaders.Add(skipHeader);
context.SaveChanges();
FiscalYear fiscalYear = null;
using (var context = new AppDbContext())
{
    fiscalYear = context.FiscalYears.Single(x => x.Id == 4);
}

using (var context = new AppDbContext())
{
    var skipHeader = new SkipHeader { FiscalYear = fiscalYear };
    context.SkipHeaders.Add(skipHeader);
    context.SaveChanges();
}
这提供了由DbContext的一个实例加载,但随后由DbContext的另一个实例引用的会计年度的基本概要。当调用
SaveChanges
时,您将获得与现在类似的行为。这是web请求中的基本情况,因为当返回实体时,实体定义只是一个契约,实体被序列化以发送给客户端。当它返回到另一个请求时,一个新的未跟踪对象被反序列化

作为一般规则,不应将实体传递到从中读取它们的DbContext的范围之外。EF确实通过分离和重新附加实体来支持这一点,但这确实比它通常值的麻烦多,因为您不能100%依赖于使用
DbContext.Attach()仅附加实体
通常情况下,上下文已经在跟踪另一个实体实例,而附加将失败。在这些情况下,您需要将引用替换为已跟踪的实体。(捕捉可能场景的混乱条件逻辑)在处理EF时,引用就是一切。EF将具有相同键值的两个不同对象引用视为单独的不同对象。与其四处传递引用,通常更简单,只传递FK更好。这样做的好处是web请求的有效负载更小

您发现的一个选项是通过FK进行更新:

var skipHeader = new SkipHeader()
{
    ... other fields get assigned to
    FiscalYears_Id = Session.FiscalYear.FiscalYears_Id,
}
但是,如果实体同时公开FK(FiscalYear_Id)和导航属性(FiscalYear),则在更新记录时可能会发现不匹配的情况。随着应用程序的发展,这一点需要小心

例如,举一个例子,您正在编辑一个财务Id为“4”的现有SkipHeader。这将有一个相关的财政参考,PK为“4”

以下面的代码为例:

var skipHeader = context.SkipHeaders.Include(x => x.FiscalYear).Single(x => x.Id == skipHeaderId);
skipHeader.FiscalYears_Id = newFiscalYearId; // update FK from "4" to "6"

var fiscalYearId = skipHeader.FiscalYear.Id; // still returns "6"
context.SaveChanges();
我们在skip头上设置了FK值,但是直到调用
SaveChanges
之后才更新FiscalYear的引用。在处理FKs和导航属性时,这可能是一个重要的细节。现在,通常我们不会麻烦去导航属性再次获取ID,但是
using (var context = new AppDbContext())
{
    var fiscalYear = context.FiscalYears.Single(x => x.Id == fiscalYearId);
    var skipHeader = new SkipHeader()
    {
        ... other fields get assigned to
        FiscalYear = fiscalYear;
    }
    context.SaveChanges();
}