7 votes

Comment charger des entités dans des collections privées à l'aide du cadre d'entités ?

J'ai un modèle de domaine POCO qui est relié au cadre d'entités à l'aide de la nouvelle classe ObjectContext.

public class Product
    {
        private ICollection<Photo> _photos;

        public Product()
        {
            _photos = new Collection<Photo>();         
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public virtual IEnumerable<Photo> Photos
        {
            get
            {
                return _photos;
            }
        }

        public void AddPhoto(Photo photo)
        {
            //Some biz logic
            //...
            _photos.Add(photo);
        }
    }

Dans l'exemple ci-dessus, j'ai défini le type de collection Photos comme étant IEnumerable, ce qui la rendra accessible en lecture seule. La seule façon d'ajouter/supprimer des photos est d'utiliser les méthodes publiques.

Le problème est que l'Entity Framework ne peut pas charger les entités photo dans la collection IEnumerable car elle n'est pas de type ICollection.

En changeant le type en ICollection, les appelants pourront appeler la méthode Add sur la collection elle-même, ce qui n'est pas une bonne chose.

Quelles sont mes options ?

Editer :

Je pourrais remanier le code de manière à ce qu'il n'expose pas une propriété publique pour Photos :

public class Product
    {
    public Product()
    {
        Photos = new Collection<Photo>();         
    }

    public int Id { get; set; }
    public string Name { get; set; }
    private Collection<Photo> Photos {get; set; }

    public IEnumerable<Photo> GetPhotos()
    {
        return Photos; 
    }

    public void AddPhoto(Photo photo)
    {
        //Some biz logic
        //...
        Photos.Add(photo);
    }

    }

Et utilisez la fonction GetPhotos() pour renvoyer la collection. L'autre problème de cette approche est que je perdrai les capacités de suivi des modifications car je ne peux pas marquer la collection comme étant virtuelle - il n'est pas possible de marquer une propriété comme étant privée et virtuelle.

Dans NHibernate, je crois qu'il est possible de faire correspondre la classe proxy à la classe privé par le biais de la configuration. J'espère que cela deviendra une fonctionnalité de EF4. Pour l'instant, je n'aime pas l'impossibilité d'avoir un contrôle sur la collection !

5voto

olnod Points 3

La façon de procéder est d'avoir une propriété virtuelle protégée qui est mappée dans votre modèle et une propriété publique qui renvoie un IEnumerable.

public class Product
{
    public Product()
    {
        PhotoCollection = new Collcation<Photo>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    protected virtual ICollection<Photo> PhotoCollection {get; set; }

    public IEnumerable<Photo> Photos
    {
        get { return PhotoCollection ; } 
    }

    public void AddPhoto(Photo photo)
    {
        //Some biz logic
        //...
        PhotoCollection .Add(photo);
    }
}

1voto

Hady Points 1145

Anton, cela m'aiderait à mieux comprendre votre problème si vous pouviez expliquer pourquoi vous ne voulez pas que les développeurs accèdent à la méthode Add de votre collection. Est-ce parce que la liste est strictement en lecture seule, ou est-ce parce que vous voulez exécuter une logique métier personnalisée lorsqu'une nouvelle entité est ajoutée ?

Quoi qu'il en soit... Je vais supposer que vous essayez de faire la dernière chose (c'est-à-dire exécuter une logique métier personnalisée lorsque la collection est modifiée). J'ai réalisé une solution similaire dans le cadre d'un de mes projets, et l'idée est la suivante :

Le modèle TT qui produit les POCO dans EF4 crée toutes les collections sous forme de listes TrackableCollection. Cette classe possède un événement appelé "CollectionChanged" auquel vous pouvez vous abonner et qui vous permet d'écouter les modifications apportées à votre collection.

Vous pouvez donc procéder comme suit :

public class Product
{
    public Product()
    {
        Photos.CollectionChanged += ListCollectionChanged;
    }

    public int Id { get; set; }

    public string Name { get; set; }

    public TrackableCollection<Photo> Photos
    {
        get
        {
            // default code generated by EF4 TT
        }
        set
        {
            // default code generated by EF4 TT
        }
    }

    private void ListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            // A new item has been added to collection
            case NotifyCollectionChangedAction.Add:
                {
                    T newItem = (T) e.NewItems[0];
                    // Run custom business logic
                }
                break;

            // An existing item has been removed
            case NotifyCollectionChangedAction.Remove:
                {
                    T oldItem = (T) e.OldItems[0];
                    // Run custom business logic
                }
                break;
        }
    }
}

L'avantage de la solution ci-dessus est que vous continuez à utiliser votre entité Product d'une manière 'EF'... où n'importe quel développeur de votre équipe peut simplement accéder à une propriété du répertoire de l'entité et n'a pas besoin d'exécuter une fonction explicite typée en dur.

0voto

Un peu en retard, mais c'est à cela que servent les objets observables. Permettre à la structure de données de faire ce qu'elle fait le mieux. Utilisez ObservableCollection comme type de champ si vous ne voulez pas construire votre propre collection qui fait ce dont vous avez besoin et exposer le type ICollection normal à partir de votre propriété. Vous pouvez exécuter toute la logique dont vous avez besoin dans l'entité parente lorsque les entités apparentées de la collection changent via l'événement CollectionChanged. Si vous avez besoin d'activer ou de désactiver sélectivement des modifications, il est assez facile d'étendre un type de collection existant ou d'écrire une collection proxy qui permet un appel à une méthode pour basculer la mutabilité de la collection (ISupportInitialize peut être utilisé à bon escient pour représenter cette capacité, BTW).

-1voto

Franci Penov Points 45358

(Veuillez m'excuser pour la brièveté de mon message initial - je répondais depuis mon téléphone)

Vous pouvez construire votre collection par le biais d'une requête LINQ sur un ensemble d'entités EF. Cependant, vous conservez la collection résultante en tant que membre de données interne à votre classe métier et vous exposez l'élément IEnumerable<Photo> renvoyée par l'appel à AsEnumerable() sur l'ensemble des entités à la suite de la photo publique.

Vous pouvez mettre en cache le IEnumerable<Photos> en interne également, de sorte que vous n'appelez pas AsEnumerable() chaque fois que votre interlocuteur vous demande la collecte. Bien entendu, cela signifie que si l'utilisateur a besoin de mettre à jour la collection par le biais de vos méthodes publiques, vous devrez peut-être rafraîchir le fichier IEnumerable . Cela peut poser un petit problème si l'appelant a également mis en cache le pointeur vers le fichier IEnumerable .

Par ailleurs, si votre interlocuteur travaille toujours avec l'ensemble des entités, l'option EntitySet (dont hériteront tous vos ensembles EF) met en œuvre les éléments suivants IEnumerable<TEntity> afin de pouvoir renvoyer directement l'ensemble d'entités à l'appelant.

Notez que si vous voulez que le chargement de la collection à partir d'un ensemble d'entités EF se fasse en dehors de la portée de votre classe métier, vous pouvez créer un constructeur sur votre classe qui prend un élément ICollection . De cette façon, une fois que vous avez créé votre objet, la collection est scellée dans celui-ci et exposée uniquement en tant qu'objet IEnumerable .

-1voto

Kirk Points 804

Pourquoi ne pas essayer les solutions suivantes et laisser les propriétés d'usage ?

private ICollection<Photo> photos{get; set;}
public IEnumerable<Photo> Photos
{
    get {return (IEnumberable<Photo>)photos;}
}

Vous pouvez également utiliser le modèle du décorateur pour encapsuler la classe dans une classe dont la collection ne peut pas être modifiée directement.

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