I am a little confused by your response. I thought I answered your question directly with an extended discussion of using a surrogate for the Company associated with the Contact ... a ContactCompany helper class.
The word "respository" doesn't appear until the last sentence ... and in a manner that does not pretend to speak to the matter of protecting entities from unwanted changes. I'll take that up below.
I do not know if creating a new EntityManager (EM) for each VM is a good idea in your application. Certainly not something I have ever done nor would typically choose to do.
I'm talking specifically about the granularity of VM. I agree that an EM is a terrific sandbox for accumulating and isolating a user's changes in one workflow from the changes that user is making in another workflow taking place "at the same time". A travel agent, for example, could be working on several bookings at once, each of them progressing independently and each of which may either be saved or discarded independently.
--
Calling EndEditCore is intentional. You could call EM.SaveChanges any time. If you were in the middle of an edit and the UI has not committed its changes, we're in a tough spot. Do we commit the changes for you? We figure that's what you want us to do. So we do it.
But I wouldn't count on this being called by EVERY SaveChanges. In principle it need only be called if something had called BeginEdit first ... and only certain UIs do that.
I think you only want to perform special logic to evaluate whether this is a real Contact Company during the Save. The proper way to do that is to handle the EntityManager.Saving event.
You want to learn about that event. If you intend to validate entities on the client before save, you have to write a Saving handler that iterates over the entities-to-be-saved and validates them. Client-side validation is not automatic (as it is on the server) ... not as I write this although we may add this feature in future.
--
YIKES!
I cannot see how your Company.Create method can work. You create a new Company and then you pretend (via "AcceptChanges") that it exists in the database. If you subsequently change the company and then try to save, you will cause DevForce and EF to attempt a database UPDATE for a table row that doesn't exist. This should fail.
If this code actually works, it must be because of something I am not seeing.
Meanwhile, even if it did succeed, the Company.IsNew flag is still true. After a save, something should set it false. I don't see what does that.
And then there is the problem that you have not destroyed these new Companies that you are creating and then detaching from Contact. They linger on in the EntityManager, unnamed and Contact-less. Harmless now, perhaps, but looks like a landmine to me.
Please don't do it this way. See below.
--
EntityAspect.EntityState tells you if the entity is new (is "added") but it doesn't tell you if someone has made changes since you created the entity.
I've seen interest in that distinction before ... usually in the context of this same intent of yours to create a related entity but keep that entity only if the user "filled it in".
Suppose you don't like my ContactCompany idea. Suppose you prefer to create a Customer, defensively, and then delete it if it is never used. May I suggest another approach?
- Add an internal "IsNewAndUnchanged" flag to the Company; it will be false by default. Do not make it public.
- Set it "true" at the end of your Company.Create
- Set it "false" if and when there is any subsequent change to this Company entity
- In the EntityManager.Saving event handler, ask the Contact to prepare itself for save (e.g., validate itself)
- Such preparation can test its Company's IsNewAndUnchanged flag; if that is true, you call contact.Company.EntityAspect.RejectChanges() and it will go away.
- You should have a validation rule that prevents saving of a Company for which this flag is true without regard for whether it is the child of a Contact; chances are you don't want such a thing in your database.
Observe that this flag is always false for queried and saved entities. It can only be true when you have created a Company and not touched it.
Don't bother with breaking the relationship or killing off this Company until you are ready to save. Forget about EndEditCore.
You are probably wondering how to implement step #3. How do you know if there is a subsequent change to the Company?
I can think of several ways but the best, I think, is to attach an event handler to the PropertyChanged event in Company and clear the flag. If you want to get fancy, your event handler can detach itself from the event once it has cleared the flag.
Alternatively, you could override the OnPropertyChanged method and clear the flag there (sure you'll clear it forever for every Company but the cost is negligable).
--
You wrote:
"Company it is still exposed as a property on Contact and potentially subject to direct manipulation elsewhere in the application, such as clearing its Name property. I need to handle those kinds of scenarios too."
If this is your worry than you may want to hide the Contact.Company property and drag the ContactCompany helper into the Contact class. I still think this is better than incorporating all that conditional code (my 1-6) inside the Contact; it really doesn't have anything to do with Contact per se; the concerns are about plumbing of the related Company. I wouldn't want all that code gunking up my Contact entity.
Let's step back a second though. Are you really worried about the "potential for direct manipulation" of related enties all over the application? You asked about how the UI could be built such that it would not require you to deal with phantom "new Company" objects. I responded to that one use case.
But protecting an entity against unwanted manipulation of its related entities by non-UI components? If you're really worried about that , you may be using the wrong technology. You might prefer something like Command Query Responsibility Separation (CQRS) in which you query for read-only DTOs and you always edit and save with other DTOs.
You'll be chucking out the baby with the bathwater in my opinion; it's a lot more work than its proponents acknowledge. But it gives you the potential for the kind of safety that you're talking about without having to play games with creating and deleting entities.
I don't know. I'm a little concerned that you'll over-architect this and end up with giant entity classes crowded with plumbing.
Please remember that you asked for my opinion ... which you are free to take or not.