Reputation: 932
I have been coding C# for a good while now, and I generally use Entity Framework and implement the repository pattern. The repository pattern tells us that we should generally only maintain and access repositories for our aggregate roots. Consider the following example, where Person is the root:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Pet> Pets { get; set; }
}
public class Pet
{
public int ID { get; set; }
public string Name { get; set; }
}
The above model would mean that we should generally access pets through the PersonRepository. However, if I want to modify or add a pet to a person, I have never found an elegant way to do this.
In order to correctly identify what to update, I need to call
DbContext.Entry(myPet).State = EntityState.Modified;
However, this messes with my repository pattern. As far as I can see, I have three options:
What am I missing here? Is there a better approach?
Upvotes: 2
Views: 3705
Reputation: 525
Yes there is a better approach. For a new person, use:
_context.People.Add(myPerson); //myPerson can have Pets attached
This will traverse all sub objects and mark them as NEW. When updating person, after calling the above code, you need to set which pet objects are modified/deleted.
I learned this in a Pluralsight course Entity Framework in the Enterprise. In it, Julie adds an extra field to Pets.
public enum ObjectState
{
Unchanged,
Added,
Deleted,
Modified
}
public interface IObjectWithState
{
[NotMapped]
[JsonIgnore]
ObjectState ObjectState { get; set; }
}
public class Pet : IObjectWithState
{
public int ID { get; set; }
public string Name { get; set; }
}
You might want this on all of your database entities.
In your repository
public void InsertOrUpdateGraph(Person entity)
{
_context.People.Add(entity);
if (entity.ID != default(int)) _context.ApplyStateChanges();
}
Some extensions
public static class ContextExtension
{
public static void ApplyStateChanges(this DbContext context)
{
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entry.Entity;
entry.State = stateInfo.ObjectState.ConvertState();
}
}
public static EntityState ConvertState(this ObjectState state)
{
switch (state)
{
case ObjectState.Modified:
return EntityState.Modified;
case ObjectState.Added:
return EntityState.Added;
case ObjectState.Deleted:
return EntityState.Deleted;
default:
return EntityState.Unchanged;
}
}
}
Works every time.
Upvotes: 1