Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Entity Framework trying to insert NULL?

I have two tables, Quote and Agent. Quote has a column called AgentID that is a foreign key in Agent.

When adding the tables to my model in the VS the Quote class has a reference to Agent.

When attempting to add a new quote I create a new quote entity and set the Agent like this:

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

Right before SaveChanges is called I examine the object and see that all of the values are set. The Agent object has all of its values set. I even checked the EntityKey property and it is set.

Despite the values being there I am getting this error:

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

I am not sure what else to check, perhaps there is a way to view the SQL?

EDIT: I am using the repository pattern in my application. I use PONO's in my application then create new entity objects. When I save a new quote I call this method:

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)
        }

Here is something odd. I was able to see the SQL generated and it looks strange to me:

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()
like image 444
HitLikeAHammer Avatar asked Feb 16 '10 19:02

HitLikeAHammer


1 Answers

Let me preface by saying that I'm using ASP.NET MVC - if you're writing a single-user desktop-application, your problem may be completely different from mine.

In my case, I had apparently come up with a bad solution to a problem I had earlier.

I was creating multiple instances of my object-context - and when you create entities with one instance, and try to associate them with an entity created by another instance, you get weird errors.

To work around that, I thought, well, I'll just make sure I have only one object-context. So I created a class with a public getter, which would create the instance if not already created, and return it. Kind of a singleton pattern, ensuring that I had one object-context for the entire application.

In my app, I occasionally create "throw-away" objects - e.g. a temporary entity, sometimes pre-populated with a few default values, just so I can render a form. When the form is submitted, a new object is created, populated, validated, and then saved.

The save would fail though - giving the error described on this page, about some attribute being empty, even though I had populated all the fields, and the entity passed validation.

The problem was, it wasn't trying to save the object I just created - it was hanging on to the "throw-away" object from the previous request, and trying to save that one first.

Coming from PHP, what threw me off here was the realization that ASP.NET MVC apps have a very different lifecycle from PHP apps. In PHP, scripts start, process a request, end then they end - whereas in ASP.NET, they start, run for a while, serving many requests, and then, eventually, they end and restart.

By creating my object-context in a static method, I was creating not one instance per request - but one instance per application. Because the object-context persists between requests, my "throw-away" entities will pile up - and eventually, when I try to SaveChanges(), it will of course fail.

This confusion stems in part from the fact that the Entity Framework was written with desktop applications in mind - it was not designed for the life-cycle of a web application.

You can work around this, and here's the solution:

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;
        }
    }
}

You can now the your object-context from anywhere:

MyEntities DB = MyNamespace.App.DB;

I found the solution in this lengthy article, which explores several (right and wrong) ways to manage the lifecycle of an object-context:

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

like image 88
mindplay.dk Avatar answered Sep 30 '22 23:09

mindplay.dk