ObjectContext осведомленный объект в EF, чтобы избежать Anemic Domain Model - PullRequest
0 голосов
/ 29 января 2011

В Entity Framework возможно ли заставить инфраструктуру внедрять DbContext в каждый объект (сущность), который присоединен или извлечен из контекста?

Я парень из NHibernate, и я знаю, что это возможно в NH, - извините, если это глупый вопрос в мире EF.

По сути, я хочу, чтобы некоторые из моих сущностей имели свойство типа DbContext, которое будет устанавливаться на экземпляр контекста самой платформой всякий раз, когда я связываю сущность с контекстом. В идеале такие классы должны быть помечены интерфейсом маркера IContextAware или чем-то подобным.

Причина, по которой я хочу это сделать (= цель), на этот раз я хочу избежать анти-паттерна Anemic Domain Model. Я подумал, что если у меня есть ObjectContext, внедренный в сущности, они смогут получить доступ к БД, что позволит мне реализовать запросы и более сложную логику прямо в самих классах домена. Если вы знаете другие способы достижения моей цели (особенно в контексте веб-приложения), пожалуйста, сделайте, но, пожалуйста, постарайтесь избегать ответов типа «вы не должны этого делать, потому что». Спасибо !!!

Ответы [ 3 ]

2 голосов
/ 29 января 2011

Вы не должны этого делать, потому что вы хотите не допускать проблем с постоянством в свои доменные объекты =)

Но если вы ДОЛЖНЫ, вы можете подключиться к событию ObjectMaterialized, инициируемому ObjectContext.В CTP5 вам нужно привести ваш DbContext следующим образом в конструкторе для вашего DbContext:

((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += 
    this.ObjectContext_OnObjectMaterialized;

Затем реализовать вашу функцию ObjectContext_OnObjectMaterialized (отправитель объекта, ObjectMaterializedEventArgs e).Через EventArgs вы сможете получить доступ к вашему объекту, который только что был материализован.Оттуда вы можете установить свойство ObjectContext / DbContext вашего POCO, которое должно быть либо открытым, либо внутренним.

1 голос
/ 01 февраля 2011

Помимо привязки вашего домена к определенной технологии персистентности, есть и другие соображения, связанные с внедрением контекста на этом уровне. Например, каково время жизни контекста, который вы вводите, и должен ли этот контекст всегда иметь одинаковое время жизни для каждой сущности?

Я понимаю, что вы хотите определить свои бизнес-методы для сущностей, поэтому вы можете сказать customer.MakeCustomerPreferred. Однако есть и другие способы сделать это, не записывая бизнес-логику на этом уровне в приложении ». Например, вы можете использовать деловые мероприятия. Вот пример:

public class Customer
{
    public void MakeCustomerPreferred()
    {
        var e = new MakeCustomerPreferredEvent()
        {
            Customer = this
        };

        DomainEvents.Current.Handle(e);
    }
}

public interface IDomainEvent { }

public interface IHandle<T> where T : IDomainEvent
{
    void Handle(T instance);
}

public class MakeCustomerPreferredEvent : IDomainEvent
{
    prop Customer Customer { get; set; }
}

Класс DomainEvents представляет собой окружающий контекст , который позволяет вам получить все обработчики для определенного события домена и выполнить их.

public class DomainEvents
{
    public static DomainEvents Current = new DomainEvents();

    public virtual void Handle<T>(T instance) 
        where T : IDomainEvent
    {
        var handlers =
           YourIocContainer.GetAllInstances<IHandle<T>>();

        foreach (var handler in handlers)
        {
            handler.Handle(instance);
        }
    }
}

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

0 голосов
/ 27 января 2015

Мы предоставляем нашим клиентам возможность следовать подходу, запрошенному инициатором темы. Чтобы сделать это, мы даже реализовали подобное решение (ObjectMaterialized и другие события ObjectContext и ObjectStateManager) в нашем продукте eXpressApp Framework (XAF). Это работает без проблем в большинстве сценариев, так как сущности имеют то же время жизни, что и «контекст». Это также помогает нам повысить удобство использования для наших клиентов, которые сталкиваются с такими же трудностями при разработке своих моделей данных и бизнес-логики.

В нашем случае модель предметной области не связана с определенной технологией персистентности, поскольку у нас есть специальная абстракция «ObjectSpace» в контексте ORM (в дополнение к Entity Framework наш продукт поддерживает наш собственный ORM - eXpress Persistent Объекты (XPO)).

Итак, мы предлагаем нашим клиентам интерфейс IObjectSpaceLink (с одним свойством IObjectSpace), который должен быть реализован объектами, которым требуется контекст для их бизнес-логики.

Кроме того, мы предоставляем интерфейс IXafEntityObject (с методами OnCreated, OnLoaded, OnSaving) для наиболее популярных бизнес-правил. Вот пример сущности, реализующей оба интерфейса из нашего BCL:

        // IObjectSpaceLink
    IObjectSpace IObjectSpaceLink.ObjectSpace {
        get { return objectSpace; }
        set { objectSpace = value; }
    }

    // IXafEntityObject
    void IXafEntityObject.OnCreated() {
        KpiInstance kpiInstance = (KpiInstance)objectSpace.CreateObject(typeof(KpiInstance));
        kpiInstance.KpiDefinition = this;
        KpiInstances.Add(kpiInstance);
        Range = DevExpress.ExpressApp.Kpi.DateRangeRepository.FindRange("Now");
        RangeToCompare = DevExpress.ExpressApp.Kpi.DateRangeRepository.FindRange("Now");
    }
    void IXafEntityObject.OnSaving() {}
    void IXafEntityObject.OnLoaded() {}

В свою очередь, вот код нашей платформы, которая внутренне связывает эти части (ниже для Entity Framework 6).

        private void ObjectContext_SavingChanges(Object sender, EventArgs e) {
        IList modifiedObjects = GetModifiedObjects();
        foreach(Object obj in modifiedObjects) {
            if(obj is IXafEntityObject) {
                ((IXafEntityObject)obj).OnSaving();
            }
        }
    }
    private void ObjectContext_ObjectMaterialized(Object sender, ObjectMaterializedEventArgs e) {
        if(e.Entity is IXafEntityObject) {
            ((IXafEntityObject)e.Entity).OnLoaded();
        }
    }
    private void ObjectStateManager_ObjectStateManagerChanged(Object sender, CollectionChangeEventArgs e) {
        if(e.Action == CollectionChangeAction.Add) {
            if(e.Element is INotifyPropertyChanged) {
                ((INotifyPropertyChanged)e.Element).PropertyChanged += new PropertyChangedEventHandler(Object_PropertyChanged);
            }
            if(e.Element is IObjectSpaceLink) {
                ((IObjectSpaceLink)e.Element).ObjectSpace = this;
            }
        }
        else if(e.Action == CollectionChangeAction.Remove) {
            if(e.Element is INotifyPropertyChanged) {
                ((INotifyPropertyChanged)e.Element).PropertyChanged -= new PropertyChangedEventHandler(Object_PropertyChanged);
            }
            if(e.Element is IObjectSpaceLink) {
                ((IObjectSpaceLink)e.Element).ObjectSpace = null;
            }
        }
        OnObjectStateManagerChanged(e);
    }
    public virtual Object CreateObject(Type type) {
        Guard.ArgumentNotNull(type, "type");
        CheckIsDisposed();
        Object obj = CreateObjectCore(type);
        if(obj is IXafEntityObject) {
            ((IXafEntityObject)obj).OnCreated();
        }
        SetModified(obj);
        return obj;
    }

Надеюсь, эта информация поможет вам.

...