【问题标题】:Why is Entity Framework trying to insert NULL?为什么实体框架试图插入 NULL?
【发布时间】:2010-02-16 19:58:12
【问题描述】:

我有两个表,报价和代理。 Quote 有一个名为 AgentID 的列,它是 Agent 中的外键。

在 VS 中将表格添加到我的模型时,Quote 类引用了 Agent。

当尝试添加新报价时,我创建了一个新的报价实体并像这样设置代理:

entity.Agent = (from x in entities.AgentEntities 
    where x.AgentID == quote.AgentID select x).FirstOrDefault();

就在调用 SaveChanges 之前,我检查了对象并看到所有值都已设置。 Agent 对象设置了它的所有值。我什至检查了 EntityKey 属性并设置了它。

尽管存在这些值,但我收到此错误:

Cannot insert the value NULL into column 'AgentID', table 'Database.dbo.Quote'; 
column does not allow nulls. INSERT fails.

我不知道还要检查什么,也许有办法查看 SQL?

编辑: 我在我的应用程序中使用存储库模式。我在我的应用程序中使用 PONO,然后创建新的实体对象。当我保存新报价时,我调用此方法:

public override void CreateQuote(Quote quoteToCreate)
{
  var entity = ConvertQuoteToQuoteEntity(quoteToCreate);
  entities.AddToQuoteEntities(entity);
  entities.SaveChanges();  //Error is thrown here
}

private QuoteEntity ConvertQuoteToQuoteEntity(Quote quote)
        {
            var entity = new QuoteEntity();

            if (quote != null)
            {
                entity.QuoteID = quote.QuoteID;
                entity.DiscoveryMethod = quote.DiscoveryMethod;
                entity.CompletedDateTimeStamp = quote.CompletedDateTimeStamp;
                entity.CommisionAmount = quote.CommisionAmount;
                entity.QuoteKey = quote.QuoteKey;
                entity.SelectedOption = quote.SelectedOption;
                entity.SentDateTimeStamp = quote.SentDateTimeStamp;
                entity.CustomerName = quote.CustomerName;
                entity.CustomerEmail = quote.CustomerEmail;
                entity.CustomerPrimaryPhone = quote.CustomerPrimaryPhone;
                entity.CustomerAlternatePhone = quote.CustomerAlternatePhone;
                entity.Agent = (from x in entities.AgentEntities where x.AgentID == quote.AgentID select x).First<AgentEntity>();
            }
            return entity;  //Everything looks good here (Agent is fully populated)
        }

这里有点奇怪。我能够看到生成的 SQL,它对我来说看起来很奇怪:

insert [dbo].[Quote]([QuoteKey], [CommisionAmount], [QuoteRequestID], [DiscoveryMethod], [SelectedOption], [CreatedDateTimeStamp], [SentDateTimeStamp], [CompletedDateTimeStamp], [CustomerName], [CustomerEmail], [CustomerPrimaryPhone], [CustomerAlternatePhone])
values (@0, null, null, @1, null, @2, null, null, @3, @4, @5, @6)
select [QuoteID], [AgentID]
from [dbo].[Quote]
where @@ROWCOUNT > 0 and [QuoteID] = scope_identity()

【问题讨论】:

  • 你能显示更多的代码吗?您可以使用 SQL Profiler 查看 SQL。
  • 基于INSERT,它根本没有设置Quote.AgentId 字段,因此是NULL。由于您说entity.Agent 不是NULL,这意味着可能是代理属性映射错误,或者EF 认为您没有更改它。前者似乎更有可能。我建议仔细检查映射。
  • 好吧,我决定“放弃”并切换到 Linq 2 SQL。根据我的经验,EF 是复杂的,或者只是没有准备好。或者有一些基本的东西我就是不明白。
  • 通常,当人们难以让 EF 正常工作时,他们会编写太多代码——同时使用多个 ObjectContexts,不必要地摆弄映射等等。我最好的建议是从非常简单的东西开始,从那里开始工作和构建。
  • 更新:我已经升级到 .NET 4 并且一切正常 - 我没有遇到上述问题。

标签: .net entity-framework


【解决方案1】:

让我先说我正在使用 ASP.NET MVC - 如果您正在编写单用户桌面应用程序,您的问题可能与我的完全不同。

就我而言,我显然想出了一个糟糕的解决方案来解决我之前遇到的问题。

我正在创建我的对象上下文的多个实例 - 当您使用一个实例创建实体并尝试将它们与另一个实例创建的实体相关联时,您会遇到奇怪的错误。

为了解决这个问题,我想,好吧,我会确保我只有一个对象上下文。所以我创建了一个带有公共 getter 的类,如果尚未创建实例,它将创建实例并返回它。一种单例模式,确保整个应用程序都有一个对象上下文。

在我的应用程序中,我偶尔会创建“丢弃”对象 - 例如一个临时实体,有时会预先填充一些默认值,以便我可以呈现表单。提交表单时,会创建、填充、验证并保存一个新对象。

虽然保存会失败 - 给出此页面上描述的错误,关于某些属性为空,即使我已经填充了所有字段,并且实体通过了验证。

问题是,它并没有尝试保存我刚刚创建的对象 - 它挂在上一个请求中的“丢弃”对象上,并尝试先保存那个对象。

来自 PHP,让我感到震惊的是 ASP.NET MVC 应用程序的生命周期与 PHP 应用程序截然不同。在 PHP 中,脚本开始,处理请求,结束,然后结束 - 而在 ASP.NET 中,它们开始,运行一段时间,服务许多请求,然后最终结束并重新启动。

通过在静态方法中创建对象上下文,我不是为每个请求创建一个实例,而是为每个应用程序创建一个实例。因为对象上下文在请求之间持续存在,所以我的“丢弃”实体会堆积起来——最终,当我尝试 SaveChanges() 时,它当然会失败。

这种混淆部分源于实体框架是为桌面应用程序编写的 - 它不是为 Web 应用程序的生命周期而设计的。

您可以解决这个问题,这是解决方案:

public class App
{
    public static MyEntities DB
    {
        get {
            // Create (as needed) and return an object context for the current Request:

            string ocKey = "MyEntities_" + HttpContext.Current.GetHashCode().ToString("x");

            if (!HttpContext.Current.Items.Contains(ocKey))
                HttpContext.Current.Items.Add(ocKey, new MyEntities());

            return HttpContext.Current.Items[ocKey] as MyEntities;
        }
    }
}

您现在可以从任何地方获取您的对象上下文:

MyEntities DB = MyNamespace.App.DB;

我在这篇冗长的文章中找到了解决方案,该文章探讨了管理对象上下文生命周期的几种(正确和错误)方法:

http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx

【讨论】:

  • 虽然不太可能,但.GetHashCode() 不能保证为对象的每个实例返回唯一值。最好使用会话 ID。另一种解决方案是向HttpApplication 对象添加一个属性,编写一个可以使用HttpContext.Current.Application as Global 之类的东西获取它的静态属性,然后使用事件来设置/取消设置该属性。 (任何给定的HttpApplication 实例一次只会与一个请求相关联,但以后可能会被另一个请求重用;因此为什么取消设置/处置很重要)或者只使用 IoC 容器 :)
【解决方案2】:

您在检查entity.Agent 的值吗?我怀疑null来自FirstOrDefault(),当它遇到一个没有返回记录的查询时。

即使代理对象设置了所有值,entity 没有对代理对象的引用也没关系。

【讨论】:

  • entity.Agent 已完全填充。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-21
  • 1970-01-01
  • 2015-03-21
  • 1970-01-01
  • 2016-07-05
  • 2020-12-29
  • 1970-01-01
相关资源
最近更新 更多