Pourquoi Entity Framework essaie-t-il d'insérer NULL?

.net entity-framework

Question

J'ai deux tables, citation et agent. La citation a une colonne appelée AgentID qui est une clé étrangère dans l'agent.

Lors de l'ajout des tables à mon modèle dans le VS, la classe Quote a une référence à l'agent.

Lorsque j'essaie d'ajouter un nouveau devis, je crée une nouvelle entité de devis et configure l'agent comme suit:

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

Juste avant que SaveChanges soit appelé, j'examine l'objet et vérifie que toutes les valeurs sont définies. L'objet Agent a toutes ses valeurs définies. J'ai même vérifié la propriété EntityKey et elle est définie.

Malgré les valeurs présentes, j'obtiens cette erreur:

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

Je ne sais pas quoi vérifier, il existe peut-être un moyen de visualiser le code SQL?

EDIT: J'utilise le modèle de référentiel dans mon application. J'utilise PONO dans mon application, puis crée de nouveaux objets d'entité. Lorsque je sauvegarde un nouveau devis, j'appelle cette méthode:

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

Voici quelque chose d'étrange. J'ai pu voir le code SQL généré et cela me semble étrange:

insérer [dbo]. [Quote] ([QuoteKey], [CommisionAmount], [QuoteRequestID], [DiscoveryMethod], [SelectedOption], [CreatedDateTimeStamp], [SentDateTimeStamp], [CompletedDateTimeStamp], [CustomerName], [ClientEmail], [ CustomerPrimaryPhone], [CustomerAlternatePhone]) valeurs (@ 0, null, null, @ 1, null, @ 2, null, null, @ 3, @ 4, @ 5, @ 6) sélectionnez [QuoteID], [AgentID] parmi [ dbo]. [Quote] où @@ ROWCOUNT> 0 et [QuoteID] = scope_identity ()

Réponse populaire

J'ai résolu mon problème.

Permettez-moi de commencer par dire que j'utilise ASP.NET MVC. Si vous écrivez une application de bureau mono-utilisateur, votre problème peut être complètement différent du mien.

Dans mon cas, j'avais apparemment trouvé une mauvaise solution à un problème que j'avais précédemment.

Je créais plusieurs instances de mon objet-contexte - et lorsque vous créez des entités avec une instance et essayez de les associer à une entité créée par une autre instance, vous obtenez des erreurs étranges.

Pour résoudre ce problème, je me suis dit que je n'aurais qu'un seul objet-contexte. J'ai donc créé une classe avec un getter public, qui créerait l'instance si elle n'était pas déjà créée et la renverrait. Une sorte de motif singleton, assurant que je n'avais qu'un seul objet-contexte pour toute l'application.

Dans mon application, je crée parfois des objets "jetables", par exemple une entité temporaire, parfois pré-remplie avec quelques valeurs par défaut, afin de pouvoir restituer un formulaire. Lorsque le formulaire est soumis, un nouvel objet est créé, rempli, validé, puis enregistré.

La sauvegarde échouerait cependant - donnant l'erreur décrite sur cette page, selon laquelle un attribut était vide, même si j'avais rempli tous les champs et que l'entité avait passé la validation.

Le problème était qu'il n'essayait pas de sauvegarder l'objet que je venais de créer - il était accroché à l'objet "à jeter" de la requête précédente et essayait de le sauvegarder en premier.

Venant de PHP, ce qui m’a bouleversé ici est la prise de conscience que les applications ASP.NET MVC ont un cycle de vie très différent de celui des applications PHP. En PHP, les scripts démarrent, traitent une requête, se terminent puis se terminent - alors qu’en ASP.NET, ils démarrent, s’exécutent pendant un certain temps, en servant de nombreuses requêtes, puis finissent par redémarrer.

En créant mon objet-contexte dans une méthode statique, je ne créais pas une instance par requête, mais une instance par application. Etant donné que le contexte d'objet persiste entre les demandes, mes entités "à jeter" vont s'empiler - et finalement, lorsque j'essaierai de SaveChanges (), il échouera bien sûr.

Cette confusion provient en partie du fait qu'Entity Framework a été conçu pour les applications de bureau: il n'a pas été conçu pour le cycle de vie d'une application Web.

Vous pouvez contourner ce problème et voici la 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;
        }
    }
}

Vous pouvez maintenant le votre objet-contexte de n'importe où:

MyEntities DB = MyNamespace.App.DB;

J'ai trouvé la solution dans ce long article, qui explore plusieurs manières (bonnes et mauvaises) de gérer le cycle de vie d'un objet-contexte:

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

J'espère que cela aidera les autres :-)



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow