Почему Entity Framework пытается вставить NULL?

У меня есть две таблицы: Quote и Agent. Цитата имеет столбец с именем 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?

EDIT: Я использую шаблон репозитория в своем приложении. Я использую 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]) значения (@0, null, null, @1, null, @2, null, null, @3, @4, @5, @6) выберите [QuoteID], [AgentID] от [dbo]. [Цитата] где @@ROWCOUNT > 0 и [QuoteID] = scope_identity()

Ответ 1

Я решил свою проблему.

Позвольте мне предисловие, сказав, что я использую ASP.NET MVC - если вы пишете однопользовательское настольное приложение, ваша проблема может сильно отличаться от моей.

В моем случае я, по-видимому, придумал плохое решение проблемы, которая была у меня раньше.

Я создавал несколько экземпляров моего объектного контекста - и когда вы создаете сущности с одним экземпляром и пытаетесь связать их с сущностью, созданной другим экземпляром, вы получаете странные ошибки.

Чтобы обойти это, я подумал, ну, я просто сделаю так, чтобы у меня был только один объектный контекст. Поэтому я создал класс с открытым геттером, который создавал бы экземпляр, если он еще не был создан, и возвращает его. Вид одноэлементного шаблона, гарантирующий, что у меня был один объект-контекст для всего приложения.

В моем приложении я иногда создаю объекты "отбрасывания" - например. временный объект, иногда предварительно заполненный несколькими значениями по умолчанию, поэтому я могу отобразить форму. Когда форма отправляется, новый объект создается, заполняется, проверяется и затем сохраняется.

Сохранение завершится неудачно, если вы сообщите об ошибке, описанной на этой странице, о том, что некоторый атрибут пуст, даже если я заполнил все поля, и объект прошел проверку.

Проблема заключалась в том, что она не пыталась сохранить объект, который я только что создал, - он зависал от объекта "отбрасывания" из предыдущего запроса и сначала пытался сохранить его.

Исходя из PHP, то, что меня отбросило, было осознанием того, что приложения ASP.NET MVC имеют совершенно другой жизненный цикл из приложений PHP. В PHP запускаются сценарии, обрабатываются запросы, заканчиваются, потом заканчиваются, тогда как в ASP.NET они запускаются, выполняются некоторое время, обслуживая множество запросов, а затем, в конце концов, они заканчиваются и перезапускаются.

Создав свой объектный контекст в статическом методе, я создавал не один экземпляр для запроса, а один экземпляр для каждого приложения. Поскольку объект-контекст сохраняется между запросами, мои объекты "отбрасывания" будут накапливаться - и в конечном итоге, когда я попытаюсь сохранить SaveChanges(), он, конечно, потерпит неудачу.

Эта путаница частично связана с тем, что Entity Framework была написана с учетом настольных приложений - она ​​не была разработана для жизненного цикла веб-приложения.

Вы можете обойти это, и вот решение:

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

Надеюсь, это полезно для других: -)

Ответ 2

Вы проверяете значение entity.Agent? Я подозреваю, что нуль приходит от FirstOrDefault(), когда он встречает запрос, который не возвращает записей.

Даже если у объекта-агента установлены все его значения, это не имеет значения, если entity не имеет ссылки на объект-агент.