Как загрузить сущности в частные коллекции, используя структуру сущностей - PullRequest
7 голосов
/ 01 апреля 2010

У меня есть модель домена POCO, которая подключена к платформе сущностей с использованием нового класса 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);
        }
    }

В приведенном выше примере я установил тип коллекции Photos в IEnumerable, поскольку это сделает его доступным только для чтения. Единственный способ добавить / удалить фотографии - это публичные методы.

Проблема в том, что Entity Framework не может загрузить объекты Photo в коллекцию IEnumerable, поскольку она не относится к типу ICollection.

Изменение типа на ICollection позволит вызывающим абонентам вызывать метод Mentod для самой коллекции, что не годится.

Какие у меня варианты?

Изменить:

Я мог бы изменить код, чтобы он не предоставлял общедоступную собственность для фотографий:

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

    }

И используйте GetPhotos (), чтобы вернуть коллекцию. Другая проблема с этим подходом заключается в том, что я потеряю возможности отслеживания изменений, поскольку не могу пометить коллекцию как виртуальную. Невозможно пометить свойство как частное виртуальное.

В NHibernate я считаю, что можно сопоставить прокси-класс с приватной коллекцией через конфигурацию. Я надеюсь, что это станет особенностью EF4. В настоящее время мне не нравится невозможность контролировать коллекцию!

Ответы [ 5 ]

5 голосов
/ 13 мая 2011

Способ сделать это - иметь защищенное виртуальное свойство, которое отображается в вашей модели, и открытое свойство, которое возвращает 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);
    }
}
1 голос
/ 10 мая 2011

Антон, это помогло бы мне лучше понять вашу проблему, если бы вы могли объяснить, почему вы не хотите, чтобы разработчики обращались к методу Add вашей коллекции. Это потому, что список предназначен только для чтения или потому, что вы хотите запустить некоторую пользовательскую бизнес-логику при добавлении нового объекта?

В любом случае ... Я собираюсь предположить, что вы пытаетесь сделать последнее (т. Е. Запускать пользовательскую бизнес-логику при изменении коллекции). Я сделал аналогичное решение для моего проекта, и идея заключается в следующем:

Шаблон TT, который создает POCO в EF4, создает все коллекции в виде списков TrackableCollection. В этом классе есть событие CollectionChanged, на которое вы можете подписаться и прослушать любые изменения в вашей коллекции.

Таким образом, вы можете сделать что-то следующим образом:

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

Хорошая вещь в вышеупомянутом решении состоит в том, что вы все еще используете свою сущность Product в «EF» манере ... где любой разработчик в вашей команде может просто получить доступ к свойству каталога сущностей и должен запустить явную функцию со строгой типизацией .

0 голосов
/ 07 марта 2013

Немного опоздал на вечеринку, но это то, для чего нужны наблюдаемые объекты. Позвольте структуре данных делать то, что она делает лучше всего. Используйте ObservableCollection в качестве типа поля, если вы не хотите создавать собственную коллекцию, которая делает то, что вам нужно, и предоставлять обычный тип ICollection из вашего свойства. Вы можете запустить любую логику в родительской сущности, которая вам нужна, когда связанные сущности в коллекции изменяются с помощью события CollectionChanged. Если вам нужно выборочно включать или отключать модификации, достаточно легко расширить существующий тип коллекции или написать прокси-коллекцию, которая позволяет вызывать метод для переключения изменчивости коллекции (ISupportInitialize может быть использован для хорошего представления этой возможности. Кстати ).

0 голосов
/ 16 июля 2010

Почему бы не попробовать следующее и оставить свойства использования?

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

В качестве альтернативы вы можете использовать шаблон декоратора для инкапсуляции класса в тот, который нельзя изменить напрямую в коллекции.

0 голосов
/ 01 апреля 2010

(извиняюсь за начальную краткость сообщения - я отвечал со своего телефона)

Вы можете создать свою коллекцию через запрос LINQ по набору сущностей EF. Однако вы сохраняете полученную коллекцию как внутренний элемент данных для своего бизнес-класса и выставляете IEnumerable<Photo>, возвращаемый путем вызова AsEnumerable() для объекта, установленного в результате публичной фотографии.

Вы также можете кэшировать IEnumerable<Photos> внутренне, чтобы не вызывать AsEnumerable() каждый раз, когда вызывающий абонент запрашивает сбор. Конечно, это означает, что если пользователю необходимо обновить коллекцию с помощью ваших открытых методов, вам, возможно, придется обновить кэшированный IEnumerable. Это может создать небольшую проблему, если вызывающая сторона также кэширует указатель на предыдущий IEnumerable.

В качестве альтернативы, если ваш вызывающий всегда будет работать с полным набором сущностей, класс EntitySet (от которого будут наследоваться все ваши наборы EF) реализует IEnumerable<TEntity>, так что вы можете напрямую вернуть набор сущностей своему вызывающему.

Обратите внимание, что если вы хотите, чтобы загрузка коллекции из набора сущностей EF происходила вне области вашего бизнес-класса, вы можете создать в своем классе конструктор, который принимает ICollection. Таким образом, когда вы создаете свой объект, коллекция запечатывается в нем и отображается только как IEnumerable.

...