Quantcast
Channel: West Wind Message Board Messages
Viewing all articles
Browse latest Browse all 10393

Re: Saving changes with EfCodeFirst business wrapper?

$
0
0
Re: Saving changes with EfCodeFirst business wrapper?
West Wind Web Toolkit for ASP.NET
Re: Saving changes with EfCodeFirst business wrapper?
Dec. 14, 2012
05:16 pm
3O1110RSZShow this entire thread in new window
Gratar Image based on email address
From:Rick Strahl
To:Matt Slay

FWIW, Attach should also work if you really wanted to do it that way.
[TestMethod]publicvoid AttachNewTest() {var cust = new Customer(); cust.Company = "East Wind Technologies"; cust.FirstName = "Jimmy"; cust.LastName = "Ropain";var custBo = new busCustomer(); custBo.Attach(cust,true); Assert.IsTrue(custBo.Save(),custBo.ErrorMessage); } [TestMethod] publicvoid AttachExistingTest() {var cust = new Customer(); cust.Id = 1; cust.Company = "Westwind Wind Technologies"; cust.FirstName = "Ricky"; cust.LastName = "Strahl"; cust.Address = "33 Kaiea Place";var custBo = new busCustomer(); custBo.Attach(cust); Assert.IsTrue(custBo.Save(), custBo.ErrorMessage); }

I think the biggest issues that occur in EF with this aren't with single objects, but with nested objects and child relationships etc.

+++ Rick ---


Ok, well, this explains a lot...

Since I was attaching the model that was posted back from the page, it had a few nulls from missing form fields which I did not add to the view, and who knows what else wrong with it due to my lack of understanding how the Model Binder works to move properties from the posted page properties back into my actual data entity. What a mess I was creating for myself. You are right, if I post that page data verbatim back to the database, I would wipe out lots of other data fields on that row.

I think the reason that Attach() was failing in the Try/Catch probably had something to do with the way I slaughtered the properties on the entity, and it was violating some of the relationship stuff between the related models?? Here is the error message I was able to find once Attach() failed:

"A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship."

Next... GOOD NEWS!!

So, using your advice at post back to instantiate a new BO from the Id, then hand-apply certain properties from the posted values onto the new BO entity, I can now save the darn thing!! And, there is no Attach() call involved!!

The next thing I need to learn is how to use one of the mapper tools so that I do not have to hand pick transfer the properties like this.

Rick - as always... Many, many thanks for helping me figure this stuff out. I'm so relieved to finally make a full round trip on my database. Wow! It's been a trying few days to figure out what is in the end a fairly simple process.

Here is my current code, which actually works for writing a few hand-picked properties back to the database. Can you tell me if this looks like the correct pattern to use??? All I can say is that it *DOES* work, however, I'd love to hear if you have any changes to suggest?

[HttpPost]public ActionResult Edit(Quote model) {try {if (ModelState.IsValid) { var quoteBO = new busQuote(); var quote = quoteBO.Load(model.id); quote.total = model.total; quote.delivery = model.delivery; quote.notes = model.notes; quoteBO.Save(); return RedirectToAction("Index"); } return RedirectToAction("Index"); }catch {return View(); } }

EF doesn't work if you just 'assign' an entity. Entities have to be created through EF or be attached in some way. IOW, you can't just use the quote object and assign it to Entity and expect that to work.

Using the bus object you can use quoteBO.Attach(quote). With plain EF the dbSet has a similar function with options for setting the change state (which the BO method does automatically).

This should work.

HOWEVER! This is not the right way to do this.

For one thing it's really bad practice to pick up a quote object as a parameter and then post it to the database. What if some robot decides to send you a quote object with a valid id and only one value filled? At that point your entity would actually write out a single property and a bunch of empty property effectively overwriting the entire record in the database. It also means that if you have POST data on a View that every field has to be there! If it's not those missing fields effectively get wiped out.

A better way is to read the values from the quote and assign them - either manually or using something like AutoMapper or WebUtils.FormVarsToObject(). These tools have options to avoid writing missing values into the object. This should ensure that only values that are actually passed are updated, and not the entire entity.

This is something that bugs me around model binding: Rather than being able to update an existing object the object has to be a parameter and get instantiated. While ModelBinding actually does the right thing (reads only POST values), that effect is unfortunately lost when you do it on a newly created instant.

That aside though - Attach() should work.

+++ Rick ---



Rick - I have a basic MVC4 CRUD app up and running, but I cannot figure out how to call the Save() method from the post-back of the Edit action on my controller.

If I understand this EF stuff properly, somehow, I've got to get the Quote entity back into the EfCodeFirst Context, and mark it as "modified" so it will be saved back to the database. I just cannot figure out the right sequence. I've tried to "attach" the Quote entity but that fails for some reason, and when I try calling .Save(quote), it does not give an error, but yet the changes do not get saved.

Here's what I've tried:

[HttpPost]public ActionResult Edit(Quote quote) {try {if (ModelState.IsValid) { var quoteBO = new busQuote();//quoteBO.Attach(quote);//quoteBO.Context.Entry(quote).State = System.Data.EntityState.Modified; quoteBO.Entity = quote; quoteBO.Save(quote);return RedirectToAction("Index"); } return RedirectToAction("Index"); }catch {return View(); } }

The Quote entity is a complex entity, as it has a related Customer object, and a child collection of line items:

Quote entity has:
- various properties from its own table field
- has a Customer object (per PK/FK relationship defined in model attributes)
- has a collection of QuoteItems which are line items on the Quote.

Eventually, I will need to save changes to the QuoteItems collection as well, but for now, I'm just wanting to accomplish saving basic properties on the top Entity in the busQuote object (i.e. the to the Quote table).

Here's the busQuote class:

publicclass busQuote : EfCodeFirstBusinessBase<Quote, QuoteContext> { .... }

Here's the Context:

publicpartialclass QuoteContext: EfCodeFirstContext {public DbSet<Quote> Quotes { get; set; } public DbSet<QquoteItem> QuoteItems { get; set; }public DbSet<Customer> Customers { get; set; }protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new quoteMap()); modelBuilder.Configurations.Add(new quoteitemMap()); modelBuilder.Configurations.Add(new customerMap()); } }






Rick Strahl
West Wind Technologies


from Maui, Hawaii

Making waves on the Web


Viewing all articles
Browse latest Browse all 10393

Trending Articles