zzzprojects / GraphDiff

GraphDiff is a library that allows the automatic update of a detached graph using Entity Framework code first.
https://entityframework-graphdiff.net/overview
MIT License
333 stars 102 forks source link

Adding a Graph of New Entities #88

Open jrichmeier opened 9 years ago

jrichmeier commented 9 years ago

After a cursory look at the open issues, it does not look like this one has been submitted.

I have a simple model consisting of Book and Author objects. There is a many-to-many relationship between these objects. Author objects contain a collection of Book objects and properties for Id, FirstName, and LastName, all of which are required.

I am attempting to add a graph consisting of one Book (book1) object and two associated Author objects (author1 and author2) that are all new entities.

If I issue the commands:

context.UpdateGraph(book1, m => m.OwnedCollection(e => e.Authors)); context.SaveChanges();

...then no errors are generated and the resultant records are what I would expect.

The problem is that my understanding is that I should use AssociatedCollection instead of OwnedCollection as the relationship is many-to-many.

If I issue the commands:

context.UpdateGraph(book1, m => m.AssociatedCollection(e => e.Authors)); context.SaveChanges();

...then validation exceptions are thrown stating that the values for the properties FirstName and LastName (of the Author objects) are invalid (they are null). I can verify that this is true by inspecting the entities in the change tracker of the context.

Is this really an issue or is this expected behavior? If the latter, why do I need to use OwnedCollection instead of AssociatedCollection when my understanding was that I needed to or should use the latter?

Lastly, I have a sample project demonstrating this issue that I can package up and send if need be.

ghost commented 9 years ago

Hi,

the author is not part of a single book but could have written multiple books. The book is also not part of a single author as it could have been written by more than one. Neither of them owns the other, they're just associated. So your understanding that you should use AssociatedCollection is correct.

Using OwnedCollection would ask GraphDiff to manage the entities as well as the relations, but it would consider the authors to be part of the book. So if you had two books written by the same author you'd end up with two identical author rows in your database (or a duplicate key exception) - probably not what you want.

Using AssociatedCollection asks GraphDiff to only manage the relations between entities but not the entities themselves. This should explain why you're seeing the validation issues: GraphDiff simply assumes that the associated entity already exists and does not attempt to manage its contents - it has been ordered to manage relations only. Given your scenario I think this is the right thing to do nonetheless, but you have to make sure that GraphDiffs assumptions are met and manage the authors separately.

I hope this clarifies things a bit. Otherwise feel free to ask.

jrichmeier commented 9 years ago

Thank you for the explanation.

I have modified my sample project to demonstrate a few different scenarios and can follow your comments.

What I am taking away from your explanation is that there is no way (currently) to have GraphDiff manage new entities when they are part of a many-to-many relationship. Instead, I have to manage these entities out of band (sort of). Is this correct?

If I add to the line of code that I originally reported as problematic as follows:

context.UpdateGraph(author1); context.UpdateGraph(author2); context.UpdateGraph(book1, m => m.AssociatedCollection(e => e.Authors));

then there are no issues. The result of the first two lines is that the Author entities are added to the context in the “Added” state. Couldn’t GraphDiff do the same if it reaches out to the database and discovers that they do not exist? What am I missing?

ghost commented 9 years ago

GraphDiff is currently designed to persist aggregates (DDD). That means to persist an object graph as if it were a single entity. This is done by mapping the parts of the object graph as owned. It is also designed to maintain relations between those aggregates, which are mapped as associations.

So you can have many-to-many within that aggregate (at least conceptually, I've never actually tested that myself) if you map the relation as owned collection, yet I still don't think that's what you want. As I mentioned in my previous post that would result in each object graph (book) maintaining its own set of authors (thus creating duplicate authors in the database) because they're considered to be a part of the book and not entities of their own (they aren't considered to have a meaningful identity of their own, they're just parts of the book).

There currently is no way to persist multiple aggregates with a single call to UpdateGraph because that (I think) isn't a valid use case in the context GraphDiff has been designed to support. But I'm actually not very familiar with DDD, so I might be completely wrong here. I think it might be possible to enable such mappings (perhaps as a 3rd kind of mapping?) but it would need some thinking upfront (valid use cases, where would we allow this, a name g, unit tests specifying the behavior etc.) and I won't act on this without Brents okay. @refactorthis: What do you think about this? Is this or could this become a valid use case for GraphDiff?

zozo1237 commented 3 years ago

This would be a very useful feature! (And is actually just what I assumed would happen by default)

I'm currently building a web scraper and can't know the order entities will be scraped. Thus I would like to be able to call UpdateGraph without having to worry about if an entity already exists or not.

Side note, the AssociatedCollection functionally mentioned here ("GraphDiff simply assumes that the associated entity already exists and does not attempt to manage its contents.") doesn't seem to be documented anywhere besides here.