22 votes

Puis-je utiliser Entity Framework Version 6 ou 7 pour mettre à jour un objet et de ses enfants automatiquement?

J'ai trois tables. Word -> WordForm -> SampleSentence. Chaque Word a différents WordForms puis chaque forme peut avoir un ou plusieurs SampleSentence

CREATE TABLE [dbo].[Word] (
    [WordId]       VARCHAR (20) NOT NULL,
    [CategoryId]   INT          DEFAULT ((1)) NOT NULL,
    [GroupId]      INT          DEFAULT ((1)) NOT NULL,
    PRIMARY KEY CLUSTERED ([WordId] ASC),
    CONSTRAINT [FK_WordWordCategory] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[WordCategory] ([WordCategoryId]),
    CONSTRAINT [FK_WordWordGroup] FOREIGN KEY ([GroupId]) REFERENCES [dbo].[WordGroup] ([WordGroupId])
);

CREATE TABLE [dbo].[WordForm] (
    [WordFormId]   VARCHAR (20)  NOT NULL,
    [WordId]       VARCHAR (20)  NOT NULL,
    [Primary]      BIT           DEFAULT ((0)) NOT NULL,
    [PosId]        INT           NOT NULL,
    [Definition]   VARCHAR (MAX) NULL,
    PRIMARY KEY CLUSTERED ([WordFormId] ASC),
    CONSTRAINT [FK_WordFormPos] FOREIGN KEY ([PosId]) REFERENCES [dbo].[Pos] ([PosId]),
    CONSTRAINT [FK_WordFormWord] FOREIGN KEY ([WordId]) REFERENCES [dbo].[Word] ([WordId])
);


CREATE TABLE [dbo].[SampleSentence] (
    [SampleSentenceId] INT           IDENTITY (1, 1) NOT NULL,
    [WordFormId]       VARCHAR (20)  NOT NULL,
    [Text]             VARCHAR (MAX) NOT NULL,
    CONSTRAINT [PK_SampleSentence] PRIMARY KEY CLUSTERED ([SampleSentenceId] ASC),
    CONSTRAINT [FK_SampleSentenceWordForm] FOREIGN KEY ([WordFormId]) REFERENCES [dbo].[WordForm] ([WordFormId])
);

Je prends les données de ces tables pour un avant-client final et cela modifie les données et ajoute ou supprime des WordForms et SampleSentences.

Je puis apporter les données vers le serveur.

Est-il possible que Entity Framework pouvez vérifier pour voir les changements dans l'objet que je ramène sur le serveur et de faire des changements à la base de données ou dois-je faire une certaine forme de comparaison où je l'ai vérifier l'avant et l'après de la Parole, WordForm et Exemple de Phrase objets?

Pour référence, voici les objets C# je suis en utilisant:

public class Word
    {
        public string WordId { get; set; } // WordId (Primary key) (length: 20)
        public int CategoryId { get; set; } // CategoryId
        public int GroupId { get; set; } // GroupId

        // Reverse navigation
        public virtual System.Collections.Generic.ICollection<WordForm> WordForms { get; set; } // WordForm.FK_WordFormWord

        // Foreign keys
        public virtual WordCategory WordCategory { get; set; } // FK_WordWordCategory
        public virtual WordGroup WordGroup { get; set; } // FK_WordWordGroup

        public Word()
        {
            CategoryId = 1;
            GroupId = 1;
            WordForms = new System.Collections.Generic.List<WordForm>();
        }
    }

public class WordForm
    {
        public string WordFormId { get; set; } // WordFormId (Primary key) (length: 20)
        public string WordId { get; set; } // WordId (length: 20)
        public bool Primary { get; set; } // Primary
        public int PosId { get; set; } // PosId
        public string Definition { get; set; } // Definition

        // Reverse navigation
        public virtual System.Collections.Generic.ICollection<SampleSentence> SampleSentences { get; set; } // SampleSentence.FK_SampleSentenceWordForm

        // Foreign keys
        public virtual Pos Pos { get; set; } // FK_WordFormPos
        public virtual Word Word { get; set; } // FK_WordFormWord

        public WordForm()
        {
            Primary = false;
            SampleSentences = new System.Collections.Generic.List<SampleSentence>();
        }
    }

public class SampleSentence : AuditableTable
    {
        public int SampleSentenceId { get; set; } // SampleSentenceId (Primary key)
        public string WordFormId { get; set; } // WordFormId (length: 20)
        public string Text { get; set; } // Text

        // Foreign keys
        public virtual WordForm WordForm { get; set; } // FK_SampleSentenceWordForm
    }

Voici ce que j'ai pu arriver jusqu'ici, mais cela n'inclut pas la vérification de la SampleSentence et je ne suis pas sûr de la façon de le faire:

    public async Task<IHttpActionResult> Put([FromBody]Word word)
    {
        var oldObj = db.WordForms
            .Where(w => w.WordId == word.WordId)
            .AsNoTracking()
            .ToList();
        var newObj = word.WordForms.ToList();

        var upd = newObj.Where(n => oldObj.Any(o =>
            (o.WordFormId == n.WordFormId) && (o.PosId != n.PosId || !o.Definition.Equals(n.Definition) )))
            .ToList();
        var add = newObj.Where(n => oldObj.All(o => o.WordFormId != n.WordFormId))
            .ToList();
        var del = oldObj.Where(o => newObj.All(n => n.WordFormId != o.WordFormId))
            .ToList();
        foreach (var wordForm in upd)
        {
            db.WordForms.Attach(wordForm);
            db.Entry(wordForm).State = EntityState.Modified;
        }
        foreach (var wordForm in add)
        {
            db.WordForms.Add(wordForm);
        }
        foreach (var wordForm in del)
        {
            db.WordForms.Attach(wordForm);
            db.WordForms.Remove(wordForm);
        }
        db.Words.Attach(word);
        db.Entry(word).State = EntityState.Modified;
        await db.SaveChangesAsync(User, DateTime.UtcNow);
        return Ok(word);
    }

16voto

Gert Arnold Points 27642

Désolé, aucun

La réponse à votre question littéralement (comme dans le titre) est pas. Il n'y a aucun moyen de le faire automatiquement avec Entity Framework. Dans ce qui est appelé les scénarios déconnectés, correctement enregistrer les modifications à partir d'un client est quelque chose que les développeurs doivent prendre soin d'eux-mêmes.

Comme mentionné, EF utilisé pour avoir l'auto-suivi des entités, mais bientôt, cette approche a été déprécié bien que dans la documentation officielle, il n'a jamais été clairement expliqué pourquoi. Probablement parce que "STEs fait (suivi des modifications) plus facile, mais le coût de faire presque tout le reste vraiment dur.." Il s'inscrit parfaitement dans l' ObjectContext de l'API de la base de données de première classe générée modèles avec les modèles de t4, mais, comme nous le savons tous, l' DbContext API et le code-première sont devenus EF est recommandé (et bientôt la seule prise en charge de l'architecture. Bien sûr, avec le code-première, l'EF ne peut pas appliquer tout de STE mise en œuvre.

Ou...?

C'est un peu frustrant que EF jamais de combler cette lacune plus tard, par exemple en fournissant une API similaire à ce que GraphDiff offre (ou peut-être, maintenant, je dois dire offert). Il y a deux solutions de rechange raisonnables que je suis au courant.

Entité Cadre de la proposition de

Lerman et Miller, dans leur livre Programmation Entity Framework: DbContext, a proposé une autre technique qui a été le plus proche d'une substitution de l'autonomie des entités de suivi de l'EF équipe a mis au point jusqu'à présent. Il tourne autour de cette interface:

public interface IObjectWithState
{
    State State { get; set; }
    Dictionary<string, object> OriginalValues { get; set; }
}

State est

public enum State
{
    Added,
    Unchanged,
    Modified,
    Deleted
}

Pour que cette approche fonctionne correctement, chaque entité doit implémenter l'interface. De plus, chaque DbContext sous-classe a besoin d'un certain nombre de méthodes. Une méthode pour remplir l' OriginalValues de la propriété, lorsqu'une entité est matérialisé et méthodes pour synchroniser son changement de tracker avec les variations enregistrées dans des entités lorsqu'elles sont renvoyées à un contexte. C'en est trop pour copier tout ce code, vous pouvez les trouver dans le livre, à partir de la page 102.

Eh bien, si vous mettez en place tous que, vous avez l'auto-suivi des entités de toutes sortes. C'est assez élaboré, même si, une fois mis en œuvre, il "fonctionne". Cependant, un inconvénient majeur est que tous les consommateurs de votre contexte, à définir cette State de la propriété lorsqu'une entité est ajoutée ou supprimée. C'est une lourde responsabilité imposer sur le code client!

Brise

Brise offre une solution complète qui va tout le chemin à partir du DAL à votre service pour code javascript dans le client. C'est à la fois très pratique et incroyablement effrayant.

En javascript, vous obtenez une LINQ-syntaxe:

var query = breeze.EntityQuery
           .from("Customers")
           .where("CompanyName", "startsWith", "A")
           .orderBy("CompanyName");

Celle-ci communique avec la Brise de l' EntityManager dans le code C#:

var manager = new Breeze.Sharp.EntityManager(serviceName);
var results = await manager.ExecuteQuery(query);

Cette EntityManager , fondamentalement, est un wrapper autour d'un contexte EF. Si toutes les pièces mobiles ont été mis en place correctement, il est pratiquement apporte le contexte EF dans votre javascript, avec le suivi des modifications, enregistrer les modifications et tous les. Je travaille avec elle dans un projet et vraiment, il est très pratique.

Mais si vous utilisez de la Brise, il Brise tout le chemin. Elle affecte tout. Un changement dans le schéma de base de données nécessite des modifications dans le code javascript. C'est effrayant, mais quelque chose que vous pouvez être utilisé pour. Mais si vous voulez faire les choses à votre façon, il devient très difficile (mais pas impossible) de plier Brise à vos besoins. Comme vivre avec votre belle-mère. Je pense que dans de nombreux cas, à la fin d'une combinaison de Brise et d'autres modèles devient inévitable.

Mais si vous voulez il de toute façon?

Généralement parlant, un inconvénient majeur de tout le suivi automatisé de débranché les entités, c'est qu'il est bien trop facile à utiliser entité d'origine des objets pour le transfert de données. La chose est, dans la plupart des cas, à la pleine entités contenir beaucoup plus de données que le client l'exige (ou est autorisé à voir). En utilisant consacré slim Otd peut améliorer les performances de façon spectaculaire. Et bien sûr, ils agissent comme une couche d'abstraction entre la DAL et de l'INTERFACE utilisateur/contrôleurs.

Oui, avec des Otd nous avons toujours à "redessiner l'état côté serveur". Ainsi soit-il. C'est vraiment l'approche recommandée pour les scénarios déconnectés.

Jean-Papa, dans son PluralSight cours sur le hot-serviette de modèle pour les SPAs, lors de l'explication de la Brise, reconnaît ce problème. Il propose une solution partielle "entités". C' est une solution, mais assez élaboré et maladroit. Et bien sûr, toujours les entités sont à la base du transfert de données.

6voto

Comme mentionné par l'une des précédentes affiches, ce n'est pas pris en charge nativement dans les objectifs EF et est l'une des fonctionnalités les plus demandées.

Toutefois, Il est possible, si vous êtes prêt à utiliser la GraphDiff bibliothèque (ou d'écrire quelque chose de semblable à vous-même). Je vous recommande fortement d'utiliser l'open source GraphDiff de la bibliothèque pour faire des mises à jour graphiques déconnecté dans EntityFramework.

Veuillez jeter un oeil à cet article, par Brent McKendrick, pour obtenir des instructions sur la façon de le faire.

Le dépôt github pour ce projet peut également être trouvé ici

À partir de l'article ci-dessus, la mise à jour d'un graphe d'entités liées, c'est simple comme l'exemple ci-dessous:

using (var context = new TestDbContext())  
{
    // Update the company and state that the company 'owns' the collection contacts, with contacts having associated advertisement options.
    context.UpdateGraph(company, map => map
        .OwnedCollection(p => p.Contacts, with => with
            .AssociatedCollection(p => p.AdvertisementOptions))
        .OwnedCollection(p => p.Addresses)
    );

    context.SaveChanges();
}

Veuillez noter que ce projet n'est pas maintenue plus longtemps, bien que je l'ai utilisé (ainsi que beaucoup d'autres), sans de graves problèmes.

1voto

Lesmian Points 3555

Jetez un oeil à ce sujet: Entity Framework 5 mise à Jour d'un Enregistrement, mise à Jour des relations lors de l'enregistrement des changements de EF4 des objets POCO. Je pense que ça décrit bien toutes les possibilités et de leurs avantages et inconvénients. En principe, tout système de suivi va vous forcer à les stocker quelque part à l'entité d'origine à des fins de comparaison ou pour interroger de nouveau. Mentionné à l'Autonomie des Entités de Suivi en faire de même - que vous avez à stocker de l'objet d'origine en session ou de l'état d'affichage qui va dégrader les performances d'une page (plus ici: l'Autonomie des Entités de Suivi vs POCO Entités). Quant à moi, je voudrais juste changer l'état de l'objet modifié et laissez ef mise à jour de la base de données (comme vous le faites dès maintenant dans l' Put([FromBody]Word word) méthode) et ce n'est pas seulement de mon point de vue - Entity Framework: mise à Jour des entités liées.

Beaucoup de personnes ont demandé de telles capacités de fusion (http://entityframework.codeplex.com/workitem/864), mais pour l'instant, EF ne fournit pas de support. Cependant, certains développeurs avis qu'il y a des alternatives comme NHibernate qui ont intégré les fonctionnalités que - http://www.ienablemuch.com/2011/01/nhibernate-saves-your-whole-object.html.

1voto

zpul Points 910

Oui, vous pouvez, à l'aide de le faible impact des bibliothèques de faire ce que vous avez besoin de:

https://github.com/AutoMapper/AutoMapper https://github.com/TylerCarlson1/Automapper.Collection

J'ai l'habitude de faire passer un réduit formulaire de classes pour le client (qui ne contient qu'un ensemble réduit de colonnes/propriétés), pour diverses raisons.

Notez que EF classes sont dans le ORM espace de noms, alors que la réduction de classes sont dans l'espace de noms courant.

Vous trouverez ci-dessous un exemple de code:

private IMapper map;

private void InitMapper()
{
    map = new MapperConfiguration(cfg => {
        cfg.CreateMap<ORM.SampleSentence, SampleSentence>();
        cfg.CreateMap<ORM.WordForm, WordForm>();
        cfg.CreateMap<ORM.Word, Word>();
        cfg.CreateMap<SampleSentence, ORM.SampleSentence>();
        cfg.CreateMap<WordForm, ORM.WordForm>();
        cfg.CreateMap<Word, ORM.Word>();
        cfg.AddProfile<CollectionProfile>();
    }).CreateMapper();

    EquivilentExpressions.GenerateEquality.Add(new GenerateEntityFrameworkPrimaryKeyEquivilentExpressions<YourDbContext>(map));
}

public async Task<IHttpActionResult> Put([FromBody]Word word)
{
    var dbWord = db.Word.Include(w => w.WordForm).Where(w => w.WordId == word.WordId).First();

    if (dbWord != null)
    {
        InitMapper();
        map.Map(word, dbWord);
        db.SaveChanges();

        var ret = map.Map<Word>(dbWord);

        return Ok(ret);
    }
    else
    {
        return NotFound();
    }

}

Cette solution fonctionne bien et EF publiera une mise à jour contenant uniquement les champs qui ont réellement changé.

Remarque: l'Utilisation de AutoMapper vous pouvez également configurer les champs qui doivent être mis à jour/mappé (également différemment pour chaque direction). Exemple: vous voulez un peu de champs à "lecture seule" pour l'utilisateur, vous pouvez les marquer avec opt.Ignore() dans l'Objet->ORM.La direction de l'objet

    cfg.CreateMap<Word, ORM.Word>()
        .ForMember(dest => dest.WordId, opt => opt.Ignore())

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X