在 EF4 CTP5 中使用 POCO 删除多对一关系中的子实体

发布于 2024-10-09 19:02:46 字数 3521 浏览 1 评论 0原文

简短版本:

使用经典的 Order 和 OrderLine 示例,如何在 POCO 中执行以下操作:

public partial class Order {

    public void RemoveInvalidOrderLines() {
        OrderLines.Remove(OrderLines.Where(ol => ol.Quantity > MaxQuantity));
    }
}

在使用 POCO 的多对一关系中,这只会删除对 OrderLine 中的 Order 的引用。有没有办法自动删除OrderLine实体?或者我是否必须注入存储库或将代码提升到服务中?在数据库中使用触发器?有这方面的模式或最佳实践吗?我在网上找到的所有示例似乎都使用了一些简单的添加和“这就是为什么你应该使用 POCO”:-)

情况:

在 VS2010 中使用 EF4 CTP5,生成持久性无知的 POCO(动态代理) ),我为如何处理相关“子”实体的删除而苦苦挣扎。

问题: 下面的 Employee 类中的 ICollectionrelative 中的 Remove 方法仅删除关系,而不删除最终实体。有没有办法强制它也删除实体,而不注入存储库/服务或将代码从实体中取出?我想要一个像 POCO 一样干净的类。

动机: 如果没有员工,Relative 实体就不能存在,因此处理添加、删除等最自然的地方是在 Employee 类中。再说一次,我愿意接受建议。 :-)

示例: 一名员工可以有多个亲属。有一个 Employee 类的存储库,提供常用的查找器。我希望员工能够删除亲属,而不仅仅是外籍关系。我是否必须注入存储库或类似的内容才能执行此操作?我尝试将上下文和存储库排除在 POCO 之外。

假设如下表:

Employee:             
Id int (PK, not null)
Name varchar

Relative:
 Id int (PK, not null)
 EmployeeId int (FK, not null)
 Name varchar

生成以下 POCO:

public partial class Employee {
...
    public virtual ICollection<Relative> Relatives
    {

        get
        {
            if (_relatives == null)
            {
                var newCollection = new FixupCollection<Relative>();
                newCollection.CollectionChanged += FixupRelatives;
                _relatives = newCollection;
            }
            return _relatives;
        }
        set
        {
            if (!ReferenceEquals(_relatives, value))
            {
                var previousValue = _relatives as FixupCollection<Relative>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupRelatives;
                }
                _relatives = value;
                var newValue = value as FixupCollection<Relative>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupRelatives;
                }
            }
        }
    }

当然,

Relative:
public partial class Relative {
...
 public virtual Employee Employee
    {
        get { return _employee; }
        set
        {
            if (!ReferenceEquals(_employee, value))
            {
                var previousValue = _employee;
                _employee = value;
                FixupEmployee(previousValue);
            }
        }
    }
    private Employee _employee;


}

现在,可以执行以下操作:

var employeeRepository = new EmployeeRepository(dbContext);
var employee = employeeRepository.Get(1234);
var relative = new Relative { Name = "Lars" };
employee.Relatives.Add(relative); 
context.SaveChanges();

将亲属添加到“相对”表中,并引用该员工。

但是,如果我想在 Employee 类中实现 DeleteRelative 方法:(

public void DeleteRelative(string name) {
     Relatives.Remove(Relatives.FirstOrDefault());
}

天真的代码假设亲属存在等)

我得到:

System.InvalidOperationException:操作失败: 由于一个或多个外键属性不可为 null,因此无法更改该关系。当关系发生更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

这是完全可以理解的,因为“Relative”中的 EmployeeId 字段不能为空。

因此,除了将存储库/上下文注入实体之外,是否可以通过任何方式解决这个问题(恕我直言,这违背了 POCO 的目的)。

另一种选择是将其全部包装在服务或事务脚本中,但同样,我希望 EF4 能够处理此问题,而无需我配置/连接实体或执行事务脚本。

非常感谢任何帮助,如果您需要更多信息,请告诉我。

哦,祝大家新年快乐。 :-)

Short version:

Using the classic Order and OrderLine example, how can I do the following in a POCO:

public partial class Order {

    public void RemoveInvalidOrderLines() {
        OrderLines.Remove(OrderLines.Where(ol => ol.Quantity > MaxQuantity));
    }
}

In a many-to-one relationship using POCO this only removes the reference to the Order in OrderLine. Is there a way to delete the OrderLine entity automatically? Or do I have to inject a repository or lift the code up into a service? Use a trigger in the database? Is there a pattern or best practice for this? All the examples I find online seem to use a few simple adds and "This is why you should use POCOs" :-)

Situation:

Using EF4 CTP5 in VS2010, generating POCOs which are persistence ignorant (dynamic proxies), I struggle with how to handle the deletion of related "child" entities.

Question:
The Remove method in ICollection Relative in the Employee class below only removes the relationship, and not the end-entity. Is there any way to force it to delete the entity as well, without injecting a repository/service or lifting the code out of the entity? I want a clean POCO-like class.

Motivation:
The Relative entity cannot exist without an employee, so the most natural place to handle add, delete etc is in the Employee class. Then again, I'm open for suggestions. :-)

Example:
One Employee can have many Relatives. There is a repository for the Employee class, providing the usual finders. I would like Employee to be able to delete Relatives, and not only the FK-relationship. Do I have to inject repositories or similar to do this? I try to keep the contexts and repositories out of the POCOs.

Assume the following tables:

Employee:             
Id int (PK, not null)
Name varchar

Relative:
 Id int (PK, not null)
 EmployeeId int (FK, not null)
 Name varchar

The folllowing POCOs are generated:

public partial class Employee {
...
    public virtual ICollection<Relative> Relatives
    {

        get
        {
            if (_relatives == null)
            {
                var newCollection = new FixupCollection<Relative>();
                newCollection.CollectionChanged += FixupRelatives;
                _relatives = newCollection;
            }
            return _relatives;
        }
        set
        {
            if (!ReferenceEquals(_relatives, value))
            {
                var previousValue = _relatives as FixupCollection<Relative>;
                if (previousValue != null)
                {
                    previousValue.CollectionChanged -= FixupRelatives;
                }
                _relatives = value;
                var newValue = value as FixupCollection<Relative>;
                if (newValue != null)
                {
                    newValue.CollectionChanged += FixupRelatives;
                }
            }
        }
    }

And of course,

Relative:
public partial class Relative {
...
 public virtual Employee Employee
    {
        get { return _employee; }
        set
        {
            if (!ReferenceEquals(_employee, value))
            {
                var previousValue = _employee;
                _employee = value;
                FixupEmployee(previousValue);
            }
        }
    }
    private Employee _employee;


}

Now, one can do this:

var employeeRepository = new EmployeeRepository(dbContext);
var employee = employeeRepository.Get(1234);
var relative = new Relative { Name = "Lars" };
employee.Relatives.Add(relative); 
context.SaveChanges();

and the relative is added to the Relative table, and references the employee.

However, if I want to implement a DeleteRelative-method in the Employee-class:

public void DeleteRelative(string name) {
     Relatives.Remove(Relatives.FirstOrDefault());
}

(Naive code assuming relatives exist etc)

I get:

System.InvalidOperationException: The operation failed:
The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

Which is perfectly understandable, since the EmployeeId field in Relative cannot be null.

So, again, is it possible to solve this in any way other than injecting repositories/context into the entity (which IMHO kind of defeats the purpose of a POCO).

Another alternative is to wrap it all up in a service or transaction script, but again, I'd like EF4 to handle this without me configuring/wiring up the entities or doing transaction scripts.

Any help is much appreciated, and if you need more information, do tell me.

Oh, and a happy new year to you all. :-)

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

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

发布评论

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

评论(1

ヤ经典坏疍 2024-10-16 19:02:46

如果它是您的应用程序架构中的一个选项,您可能会考虑在您的实体上启用动态代理。这使得上下文能够创建实体的子类,该子类会覆盖您的属性以跟踪更改。在您的情况下,当对象丢失其引用时,这将删除与您的对象对应的行(假设您在上下文中调用 SaveChanges() )。

时,可以启用动态代理

  • 当类是公共且未密封
  • 。该类具有公共或受保护的属性(在您的示例中,特别是包含“订单行”的集合属性)。
  • 该类具有公共或受保护的无参数构造函数。

看了这个问题,你似乎满足这些要求。

可以通过在上下文中设置属性来启用动态代理(也可以在实体框架设计器视图中完成):

this.ContextOptions.ProxyCreationEnabled = true;

If it's an option in your application's architecture you might consider enabling Dynamic Proxies on your entities. This enables the context to create a subclass of your entity, that overrides your properties to track changes. In your case, this will delete rows corresponding to your object when the object loses it's references (assuming you call SaveChanges() on the context).

Dynamic proxies can be enabled when

  • The class is public and not sealed.
  • The class has public or protected properties (in your example specifically the collection property that contains the "order lines").
  • The class has a public or protected parameterless constructor.

Looking at the question you seem to meet these requirements.

Enabling dynamic proxies can be done by setting the property on your context (can also be done in the Entity Framework designer view):

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