EF 中的 ObjectContext 感知实体可避免域模型贫血

发布于 2024-10-15 00:36:31 字数 423 浏览 10 评论 0原文

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

我是 NHibernate 人员,我知道在 NH 中这是可能的,--如果在 EF 世界中这是一个愚蠢的问题,我很抱歉。

本质上,我希望我的一些实体具有 DbContext 类型的属性,每当我将实体与上下文关联时,该属性就会由框架本身设置为上下文的实例。理想情况下,此类将使用 IContextAware 标记接口或类似的东西进行标记。

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

In Entity Framework, is it possible to make the framework inject the DbContext into each object (entity) that is attached to or retrieved from the Context?

I'm an NHibernate guy and I know it is possible in NH, -- sorry if it is a stupid question in EF world.

Essentially, I want some of my entities to have a property of type DbContext that will get set to the instance of the context by the framework itself, whenever I associate the entity with the context. Ideally such classes will be marked with IContextAware marker interface or something like that.

The reason I want to do this is (=goal), I want to avoid Anemic Domain Model anti-pattern this time around. I figured if I have ObjectContext injected into entities, they will be able to access DB, thereby allowing me to implement queries and more complex logic right inside domain classes themselves. If you know other ways to accomplish my goal (esp. in context of web app) please do, but please try to avoid answers like "you shouldn't do this because". Thanks!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乱世争霸 2024-10-22 00:36:31

您不应该这样做,因为您希望将持久性问题排除在域对象之外 =)

但如果必须,您可以挂钩由 ObjectContext 触发的 ObjectMaterialized 事件。在 CTP5 中,您需要在 DbContext 的构造函数中像这样转换 DbContext:

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

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

You shouldn't do this because you want to keep persistence concerns out of your domain objects =)

But if you MUST, you can hook into the ObjectMaterialized event fired by ObjectContext. In CTP5, you need to cast your DbContext like so in the constructor for your DbContext:

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

Then implement your function ObjectContext_OnObjectMaterialized(object sender, ObjectMaterializedEventArgs e). Via the EventArgs, you will be able to access your object, which has just been materialized. From there, you can set your POCO's ObjectContext/DbContext property, which has to be either public or internal.

花期渐远 2024-10-22 00:36:31

除了将您的域与特定的持久性技术耦合之外,在该级别注入上下文还存在其他问题。例如,您注入的上下文的生命周期是多少?该上下文对于每个实体是否应该始终具有相同的生命周期?

我了解您想要在实体上定义业务方法,因此您可以说 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);
        }
    }
}

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

Besides coupling your domain to a specific persistance technology, there are other conserns with injecting the context at that level. For instance, what is the lifetime of the context you inject and should that context always have the same lifetime for each entity?

I understand you want to define your business methods on the entities, so you can say customer.MakeCustomerPreferred. There are however, other ways to do this, without having to write the business logic at that level in the application`. For instance, you can use business events. Here's an example:

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

The DomainEvents class is an ambient context that allows you to get all handlers for the specific domain event and execute them.

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

With this in place you can define your handlers at a higher level in your architecture and plug in zero, one, or more handlers for each business event. You can inject the context in a handler.

撩起发的微风 2024-10-22 00:36:31

我们为客户提供遵循主题发起者所要求的方法的选项。为了做到这一点,我们甚至在我们的 eXpressApp Framework (XAF) 产品中实现了类似的解决方案(ObjectMaterialized 以及 ObjectContext 和 ObjectStateManager 的其他事件)。这在大多数情况下都不会出现任何问题,因为实体与“上下文”具有相同的生命周期。这也有助于我们为在设计数据模型和业务逻辑时面临同样困难的客户提高可用性。

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

我希望这些信息对您有所帮助。

We provide our clients with an option to follow the approach requested by the topic starter. In order to do this, we even implemented a similar solution (the ObjectMaterialized and other events of ObjectContext and ObjectStateManager) in our eXpressApp Framework (XAF) product. This works without any issues in most scenarios since entities have the same life time as the "context". This also helps us improve usability for our clients who face the same difficulties when designing their data models and business logic.

In our case, the domain model is not coupled with a specific persistence technology, because we have a special "ObjectSpace" abstraction on the ORM context (in addition to the Entity Framework our product supports our in-house ORM - eXpress Persistent Objects(XPO)).

So, we offer our clients an IObjectSpaceLink interface (with a single IObjectSpace property) that is supposed to be implemented by entities requiring context for their business logic.

Additionally, we provide an IXafEntityObject interface (with the OnCreated, OnLoaded, OnSaving methods) for the most popular business rules. Here is an example of an entity implementing both interfaces from our 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() {}

In turn, here is the code of our framework that links these pieces together internally (below is for 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;
    }

I hope this information helps you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文