web-dev-qa-db-fra.com

Une erreur s'est produite lors de l'enregistrement d'entités qui n'exposent pas les propriétés de clé étrangère pour leurs relations

J'ai simplement un code dans Entity Framework 4.1 code en premier:

PasmISOContext db = new PasmISOContext();
var user = new User();
user.CreationDate = DateTime.Now;
user.LastActivityDate = DateTime.Now;
user.LastLoginDate = DateTime.Now;
db.Users.Add(user);

db.SaveChanges();
user.Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") };
db.SaveChanges();


db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

Le problème est que j'obtiens une erreur

Une erreur s'est produite lors de l'enregistrement des entités qui n'exposent pas les propriétés de clé étrangère pour leurs relations. La propriété EntityEntries renvoie null car une seule entité ne peut pas être identifiée comme la source de l'exception. La gestion des exceptions lors de l'enregistrement peut être facilitée en exposant les propriétés de clé étrangère dans vos types d'entité. Voir l'exception interne pour plus de détails.

à

db.Users.Add(new User() { Avatar = new Avatar() { Link = new Uri("http://myUrl/%2E%2E/%2E%2E") } });
db.SaveChanges();

Je ne comprends pas pourquoi cette opération similaire fonctionne. Y a-t-il un problème avec mon modèle ou avec ef-code-first?

public class Avatar
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string LinkInString { get; set; }

    [NotMapped]
    public Uri Link
    {
        get { return new Uri(LinkInString); }
        set { LinkInString = value.AbsoluteUri; }
    }
}

public class User
{
    [Key]
    public int Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public Avatar Avatar { get; set; }
    public virtual ICollection<Question> Questions { get; set; }
    public virtual ICollection<Achievement> Achievements { get; set; }

    public DateTime CreationDate { get; set; }
    public DateTime LastLoginDate { get; set; }
    public DateTime LastActivityDate { get; set; }
}
48
user278618

Pour ceux d'entre vous qui auraient toujours cette erreur avec toutes les clés correctement définies, regardez vos entités et assurez-vous de ne pas laisser un champ datetime avec une valeur nulle.

161
Baral

Ce message d'erreur peut être renvoyé pour tout type de raison. La propriété 'InnerException' (ou son InnerException, ou l'InnerException de cela, etc.) contient la cause principale réelle du problème.

Il serait bien sûr utile de savoir où le problème s'est produit - quel (s) objet (s) de l'unité de travail est à l'origine du problème? Le message d'exception vous indique normalement dans la propriété 'EntityEntries', mais dans ce cas, pour une raison quelconque, cela ne peut pas être fait. Cette complication de diagnostic - de la propriété "EntityEntries" étant vide - est apparemment parce que certaines entités "n'exposent pas les propriétés de clé étrangère pour leurs relations".

Même si l'OP obtient l'erreur en raison de l'échec de l'initialisation de DateTimes pour la deuxième instance de User, il obtient la complication de diagnostic - 'EntityEntries' étant vide et un message de niveau supérieur déroutant. .. parce que l'une de leurs entités n'expose pas les propriétés de clé étrangère. Pour résoudre ce problème, Avatar doit avoir un public virtual ICollection<User> Users { get; set; } propriété définie.

13
David Bullock

Le problème a été résolu en ajoutant une propriété FK.

6
user278618

Dans mon cas, la situation suivante me donnait la même exception:

Imaginez un premier modèle EF de code où vous avez une entité Garage qui a une collection d'entités Car. J'avais besoin de retirer une voiture du garage alors j'ai fini avec un code qui ressemblait à ceci:

garageEntity.Cars.Remove(carEntity);

Au lieu de cela, cela aurait dû ressembler à ceci:

context.Cars.Remove(carEntity);
3
Memet Olsen

Juste pour ceux qui pourraient avoir des problèmes similaires. J'ai eu la même erreur, mais pour une raison différente. Dans l'un des objets enfants, j'ai défini la [Clé] comme étant une valeur identique pour différents enregistrements. Une erreur stupide de ma part, mais le message d'erreur ne vous mène pas instantanément au problème.

2
Simon The Cat

Dans mon cas, l'exception a été levée car EF avait mal créé une migration. Il a manqué de définir identité: vrai sur la deuxième table. Allez donc dans les migrations qui ont créé les tables pertinentes et vérifiez s'il n'a pas pu ajouter d'identité.

CreateTable(
    "dbo.LogEmailAddressStats",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            EmailAddress = c.String(),
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.LogEmailAddressStatsFails",
    c => new
        {
            Id = c.Int(nullable: false), // EF missed to set identity: true!!
            Timestamp = c.DateTime(nullable: false),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.LogEmailAddressStats", t => t.Id)
    .Index(t => t.Id);

Une colonne Id doit avoir une identité (c'est-à-dire une incrémentation automatique!), Il doit donc s'agir d'un bogue EF.

Vous pouvez ajouter une identité manuellement avec SQL directement à la base de données, mais je préfère utiliser Entity Framework.

Si vous rencontrez le même problème, je vois deux solutions faciles:

Alt 1

inverser la migration créée de manière incorrecte avec

update-database -target:{insert the name of the previous migration}

Ajoutez ensuite identité: true manuellement au code de migration, puis update-database encore.

Alt 2

vous créez une nouvelle migration qui ajoute une identité. Si vous n'avez aucun changement dans les modèles et que vous exécutez

add-migration identity_fix

cela créera une migration vide. Ensuite, ajoutez simplement ceci

    public partial class identity_fix : DbMigration
    {
        public override void Up()
        {
            AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false, identity: true));
        }

        public override void Down()
        {
            AlterColumn("dbo.LogEmailAddressStatsFails", "Id", c => c.Int(nullable: false));
        }
    }
2
fredrik.hjarner

Ce problème peut également résulter de déclarations de clé inversées. Si vous utilisez couramment pour configurer la relation, assurez-vous que les touches gauche et droite sont mappées sur l'entité correcte.

1
Edyn

Encore un autre cas ici. Une requête a été castée dans une liste et, ce faisant, elle a créé des entités par leur constructeur pour comparaison dans l'expression linq juste après ToList (). Cela a créé des entités qui sont entrées dans l'état supprimé après la fin de l'expression linq.
Toutefois! Il y a eu un petit ajustement qui a créé une autre entité dans le constructeur, de sorte que cette nouvelle entité a été liée à une entité qui a été marquée comme supprimée.

Quelques codes pour illustrer:

query.Except(_context.MyEntitySetSet()
                .Include(b => b.SomeEntity)
                .Where(p => Condition)
                .ToList() // This right here calls the constructor for the remaining entities after the where
                .Where(p => p.Collection.First(b => Condition).Value == 0)
                .ToList();

Le constructeur de MyEntity:

public partial class MyEntity
{
    protected MyEntity()
    {
        // This makes the entities connected though, this instance of MyEntity will be deleted afterwards, the instance of MyEntityResult will not.
        MyEntityResult = new MyEntityResult(this);
    }
}

Ma solution était de m'assurer que l'expression entière était faite à l'intérieur de IQueryable afin qu'il n'y ait pas d'objets créés.

0
Mixxiphoid

J'ai eu le même problème. dans mon cas, c'était dû au champ datetime avec une valeur nulle. J'ai dû passer une valeur à datetime et tout s'est bien passé

0
onlyme

Aujourd'hui, j'ai fait face à ce problème et j'ai essayé les solutions possibles indiquées ci-dessus, mais aucune ne m'a aidé. J'ai eu le modèle UnitOfWork implémenté et le système validait les données en dernier après avoir ajouté tous les enregistrements.

Dans mon cas, le système combinait les deux modèles et interrogeait la base de données

Nom d'objet non valide 'dbo.RoleModelUserModel'.

où il s'agissait en fait de deux modèles différents.

J'ai résolu ce problème en réorganisant les instructions d'insertion et en ajoutant d'abord l'entité parent. Dans ce cas, l'utilisateur a d'abord ajouté et le problème a été résolu.

0
Nawaz Khan

Une autre réponse:

J'ai utilisé ceci:

public List<EdiSegment> EdiSegments { get; set; }

au lieu de cela:

public virtual ICollection<EdiSegment> EdiSegments { get; set; }

et a obtenu le message d'erreur noté ci-dessus.

0
Greg Gum

J'ai eu la même erreur et dans mon cas, le problème était que j'ai ajouté un objet de relation qui avait déjà été chargé "AsNoTracking". J'ai dû recharger la propriété de la relation.

BTW, Certains suggèrent d'utiliser "Attach" pour les relations qui existent déjà dans db, je n'ai cependant pas essayé cette option.

0
Hamed

Je ne suis pas tout à fait sûr que cela va aider dans votre cas, car je configure mes tables à l'aide de l'API Fluent, cependant, pour autant que je sache, le problème se pose indépendamment du fait que le schéma soit configuré à l'aide d'annotations de données (attributs) ou API Fluent (configuration).

Il semble y avoir un bogue dans EF (v. 6.1.3) car il omet certaines modifications du schéma lors de la mise à jour de la base de données vers la prochaine migration. La route la plus rapide est (pendant la phase de développement) de supprimer à nouveau toutes les tables de la base de données et les migrations runt de la phase d'initialisation.

Si vous êtes déjà en production, la solution la plus rapide que j'ai trouvée a été de changer manuellement le schéma dans la base de données ou, si vous voulez avoir le contrôle de version des modifications, manipuler manuellement les méthodes p () = et Down () dans votre migration.

0
Konrad Viltersten

Dans mon cas, le problème était que j'avais renommé une colonne de manière incorrecte, donc la migration a fait deux colonnes, une appelée "TeamId" et une appelée "TeamID". C # s'en soucie, SQL ne le fait pas.

0
Duston