web-dev-qa-db-fra.com

Sérialisation des objets Entity Framework avec une relation un à plusieurs

J'essaie d'utiliser EF avec Code First et l'API Web. Je n'ai aucun problème jusqu'à ce que j'entre dans la sérialisation des relations plusieurs-à-plusieurs. Lorsque j'essaie d'exécuter la méthode API Web suivante ci-dessous, j'obtiens le message d'erreur suivant:

public class TagsController : ApiController
{

        private BlogDataContext db = new BlogDataContext();

        // GET api/Tags
        public IEnumerable<Tag> GetTags()
        {
            return db.Tags.AsEnumerable();
        }
}

J'obtiens l'erreur suivante:

'System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D' avec le nom de contrat de données 'Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D: http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies ' est pas prévu . Envisagez d'utiliser un DataContractResolver ou ajoutez des types non connus statiquement à la liste des types connus - par exemple, en utilisant l'attribut KnownTypeAttribute ou en les ajoutant à la liste des types connus transmis à DataContractSerializer.

J'ai lu certains SO articles ( article 1 , article 2 ) que le correctif consiste à ajouter l'attribut suivant:

[DataContract (IsReference = true)]

mais cela n'a eu aucun effet. L'utilisation de [IgnoreDataMember] n'a également aucun effet. La seule option qui semble fonctionner consiste à définir Configuration.ProxyCreationEnabled sur false. Est-ce ma seule option? Suis-je en train de manquer quelque chose?

Exemples d'objets POCO:

Balise

[DataContract(IsReference = true)]
public class Tag
{
        public Tag()
        {
            this.Blogs = new HashSet<Blog>();
        }

        [Key]
        [DataMember]
        public int Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        [IgnoreDataMember]
        public virtual ICollection<Blog> Blogs { get; set; }
}

Blog

[DataContract(IsReference = true)]
public class Blog
{
    public Blog()
    {
        this.Tags = new HashSet<Tag>();
    }

    [Key]
    [DataMember]
    public int Id { get; set; }

    [DataMember]
    public string Name { get; set; }

    [IgnoreDataMember]
    public virtual ICollection<Tag> Tags { get; set; }
}
34
Blake Blackwell

Lorsque vous voyez un objet comme:

System.Data.Entity.DynamicProxies.Tag_FF17EDDE6893000F7672649A39962DB0CA591C699DDB73E8C2A56203ED7C7B6D

Il s'agit d'une version d'exécution générée par EF d'un proxy vers ce qui serait normalement considéré comme un objet POCO.

Entity Framework a créé cet objet car il suit les modifications apportées aux objets, de sorte que lorsque vous appelez .SaveChanges(), il peut optimiser ce qu'il faut faire. L'inconvénient est que vous n'utilisez pas réellement l'objet spécifique que vous avez défini, donc les contrats de données et les cadres (Json.net) ne peuvent pas les utiliser comme ils le feraient avec votre objet POCO d'origine.

Pour empêcher EF de renvoyer cet objet, vous avez deux choix (ATM):

Tout d'abord, essayez de désactiver la création d'objets proxy sur votre DbContext .

DbContext.Configuration.ProxyCreationEnabled = false;

Cela désactivera complètement la création d'objets proxy pour chaque requête vers le DbContext spécifique. (Cela n'affecte pas l'objet mis en cache dans le ObjectContext).

Deuxièmement, utilisez EntityFramework 5.0 + avec AsNoTracking () (ProxyCreationEnabled est toujours disponible dans EF 5.0 également)

Vous devriez également pouvoir

DbContext.Persons.AsNoTracking().FirstOrDefault();

ou

DbContext.Persons.
  .Include(i => i.Parents)
  .AsNoTracking()
  .FirstOrDefault();

Au lieu de globalement désactiver la création de proxy pour le DbContext, cela ne le désactive que par requête. (Ceci [~ # ~] ne [~ # ~] affecte l'objet mis en cache dans le ObjectContext, il n'est pas mis en cache)

85
Erik Philips

Je voulais laisser la création de proxy inteact et j'ai trouvé que l'utilisation de ProxyDataContractResolver semblait résoudre le problème pour moi. Voir msdn pour une référence sur la façon de l'utiliser dans wcf, qui n'est pas exactement WebAPI, mais devrait vous aider à suivre le bon chemin.

3
Dave