EF中的ObjectContext感知实体,以避免遗忘域模型

时间:2011-01-29 03:12:53

标签: entity-framework activerecord dependency-injection business-logic anemic-domain-model

在Entity Framework中,是否有可能使框架将DbContext注入到附加到Context或从Context中检索的每个对象(实体)?

我是NHibernate的家伙,我知道NH有可能, - 抱歉,如果这是EF世界的一个愚蠢的问题。

基本上,我希望我的一些实体拥有类型为DbContext的属性,只要我将实体与上下文相关联,它就会被框架本身设置为上下文的实例。理想情况下,这些类将使用IContextAware标记接口或类似标记。

我想这样做的原因是(=目标),我想这次避免使用Anemic Domain Model反模式。我想如果我将ObjectContext注入到实体中,它们将能够访问DB,从而允许我在域类本身内部实现查询和更复杂的逻辑。如果你知道其他方法来实现我的目标(尤其是在网络应用程序的上下文中),请尽量避免回答“你不应该这样做,因为”。感谢!!!

3 个答案:

答案 0 :(得分:2)

您不应该这样做,因为您希望保持域对象中的持久性问题=)

但是如果你必须,你可以挂钩到ObjectContext触发的ObjectMaterialized事件。在CTP5中,您需要在DbContext的构造函数中强制转换DbContext:

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

然后实现你的函数ObjectContext_OnObjectMaterialized(object sender,ObjectMaterializedEventArgs e)。通过EventArgs,您将能够访问刚刚实现的对象。从那里,您可以设置POCO的ObjectContext / DbContext属性,该属性必须是公共的或内部的。

答案 1 :(得分:1)

除了将您的域与特定的持久性技术相结合之外,还有其他的注意事项是在该级别注入上下文。例如,您注入的上下文的生命周期是什么,并且该上下文是否应始终具有每个实体的相同生命周期?

我了解您要在实体上定义业务方法,因此您可以说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类是ambient context,它允许您获取特定域事件的所有处理程序并执行它们。

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

有了这个,您可以在架构的更高级别定义处理程序,并为每个业务事件插入零个,一个或多个处理程序。您可以在处理程序中注入上下文。

答案 2 :(得分:0)

我们为客户提供了一个选项,可以按照主题启动器请求的方法进行操作。为了做到这一点,我们甚至在我们的eXpressApp Framework(XAF)产品中实现了类似的解决方案(ObjectMaterialized以及ObjectContext和ObjectStateManager的其他事件)。这在大多数情况下都没有任何问题,因为实体与“上下文”具有相同的生命周期。这也有助于我们提高在设计数据模型和业务逻辑时遇到同样困难的客户的可用性。

在我们的例子中,域模型没有与特定的持久性技术相结合,因为我们在ORM上下文中有一个特殊的“ObjectSpace”抽象(除了实体框架,我们的产品支持我们的内部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() {}

反过来,这里是我们框架的代码,它将这些部分内部链接在一起(下面是实体框架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;
    }

我希望这些信息可以帮到你。