Reputation: 38
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. :-)
Upvotes: 3
Views: 650
Reputation: 27105
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
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;
Upvotes: 1