2

This question is coming from a frustration when using the Entity Framework and ASP.NET MVC, but I assume it's a general frustration across more frameworks and languages.

Lets say we have an object called Invoice. The Invoice has a Recipient. In my object model I have set it up like this:

    [ForeignKey("Debtor")]
    public int RecipientId { get; set; }

    public virtual Recipient Recipient { get; set; }

In .NET, and assumably also other frameworks, that means when I have an Invoice object, I can lazyload the Recipient data.

If I save the Invoice object, it will also save the Recipient object in the Recipient database. That is in some cases very fine, but...

This is my problem

I also have a RecipientService. Here I have some business logic before I store a Recipient. I will set a creation date, verify some fields are correct and set some other values. When I call my Create method on my RecipientService, this works perfectly.

But obviously, this code is completely skipped when I save my Invoice in database when using the ORM Entity Framework.

My question

What is the most common approach to solve this?

Is it to simply disallow saving objects with an actual object as a property, and just save it with the ID reference?

In my InvoiceService I could easily say something like:

Recipient newRecipient = _recipientService.Create(recipient);
invoice.Recipient = null;
invoice.RecipientId = newRecipient.Id;

But is this really the best and most common approach?

I hope someone can help out here :-)

  • I'm not sure what you mean by "solve this" but Entity Framework doesn't try to re-create an entity from a navigation property if it has an existing ID, if that's the problem. It might update it but update != create. – guillaume31 May 11 '18 at 12:07
0

I think the common approach is to drop EF and replace with hand coded repos. But!

Here Invoice is your aggregate root object. If i do:

myInvoice.Recipient.Name = "new name";
repo.Save(myInvoice);

Then I expect the name change to be saved.

You should only save Invoices and never lone recipients. This allows you to put all your business logic for saving in the Invoice Repository and ensure that all rules are enforced.

Alternatively if you remove the Recipient child object from the Invoice then you can persist both separately, as the only requirement is that a Recipient with the linked id exists. and this can be enforced by both the Invoice and Recipient repositories.

  • Ah, makes sense. In my case the Recipient can live as it's own object (ie being a receiver of multiple invoices). In that case I guess I should remove the relation because it should be able to able to live on its own. Do you agree? :-) – Lars Holdgaard May 11 '18 at 9:31
  • @Ewan you're speaking from a very specific angle which is DDD. I'm not sure this helps with the original problem about Entity Framework. Also : "the common approach is to drop EF and replace with hand coded repos" - well, not so common... – guillaume31 May 11 '18 at 11:46
  • @guillaume31 "aggregate root" is a DDD term, but the concept goes beyond DDD. we will have to do a survey to find out about EF – Ewan May 11 '18 at 11:52
  • 1
    I fail to see how the OP's problem is solved by the concept of AR. What's more, reading Lars's comment I guess he understood "Remove Recipient from Invoice" as "remove the navigation property and ID" when that may not be the best idea with regard to Entity Framework. – guillaume31 May 11 '18 at 11:57
  • 1
    I have interpreted the question broadly "my ORM isn't doing what I want! why?" rather than "How do I achieve this exact result in EF" I think this interpretation is a better fit for this site and to drop EF or adapt to its default way is a better recommendation than to hack around with various EF options to make it fit the required scenario – Ewan May 11 '18 at 12:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.