简体   繁体   中英

Strange entity updating in Entity Framework Code-First

I'm solving the problem with updating entity before saving to database and got strange behavior.

I'm using Entity Framework 4.1 Code-First in ASP.NET MVC 3 web application. Here is model:

public class Order
{
    public int OrderId { get; set; }
    public int CarId { get; set; }
    public DateTime BeginRentDate { get; set; }
    public DateTime EndRentDate { get; set; }
    public decimal RentPrice { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    public int CarId { get; set; }
    public string Brand { get; set; }
    public string Model { get; set; }
    public string NumberPlate { get; set; }
    public decimal RentPrice { get; set; }
}

Each Car has a RentPrice. This price should be copied to Order's RentPrice when creating one. The car is selecting by user so initially Order.RentPrice is 0.

Here I want to copy price value:

[HttpPost]
public ActionResult Create(Order order)
{
    order.RentPrice = _context.Cars.Find(order.CarId).RentPrice;

    if (ModelState.IsValid)
    {
        _context.Orders.Add(order);
        _context.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(order);
}

It's not working because of an error on the SaveChanges that entity has validation errors. OK. I found that need first to call UpdateModel(order); and then change values.

So what I have. Working code:

_context.Orders.Add(order);
UpdateModel(order);
order.RentPrice = 777;
_context.SaveChanges();

Not working code:

_context.Orders.Add(order);
UpdateModel(order);
order.RentPrice = _context.Cars.Find(order.CarId).RentPrice;
_context.SaveChanges();

Working code (!):

_context.Orders.Add(order);
UpdateModel(order);
var t = (double)_context.Cars.Find(order.CarId).RentPrice;
order.RentPrice = (decimal)t;
_context.SaveChanges();

Can someone explain, please, what is going on here? Especially magic on the 3nd and 4th lines in the last block of code.

Update

I'm getting DbEntityValidationException : "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details." From the inner exception: "OriginalValues cannot be used for entities in the Added state."

When you get

"Validation failed for one or more entities. See 'EntityValidationErrors' property for more details." From the inner exception: "OriginalValues cannot be used for entities in the Added state."

It means there was errors such as NOT NULL collumns that were blank or other constraints , check the entity validation errors by debugging or like

try{
...
catch ( DbEntityValidationException ex )
   {
foreach ( var validationErrors in ex.EntityValidationErrors )
    {
     foreach ( var validationError in validationErrors.ValidationErrors )
     {
      System.Diagnostics.Trace.TraceInformation( "Property: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage );
     }
    }
}

OriginalValues cannot be used for entities in the Added state.

Can be corrected by ensuring that non identity primary key fields are specified as not generated in the model.

modelBuilder
    .Entity<T>()
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    // this can also be achieved using attributes on the entity.

The error is actually self explanatory when you have context, but baffling otherwise. The database generates two statements for the insert, the second of which is:

SELECT [PrimaryKeyId]
FROM [dbo].[Entity]
WHERE @@ROWCOUNT > 0 AND [PrimaryKeyId] = scope_identity()
/* SP:StmtCompleted... */

This statement will not return any rows for non identity columns. Hence the OriginalValues will remain unaltered in the added state. This also explains why this exception is wrapped in an OptimisticConcurrencyException as the number of rows affected is used to detect existing altered data.

I am sure the problem would have been rectified ages ago, just wanted to contribute my 2 cents.

I also ran into an issue with the same error message, I was using Oracle and EF 5.0. Finally I got a solution after wasting more than 12 hours.

For me it was lack of the permissions for the user on the table where I was trying to insert values and the error message absolutely had no hints of the actual permission issue, which was really weird.

Hope someone finds this useful and doesn't waste hours like me in troubleshooting.

Cheers,

Avi

Have you declared the primary key anywhere? You should do that either using FluentAPI or attribute like this:

public class Order
{
    [Key]
    public int OrderId { get; set; }
    public int CarId { get; set; }
    public DateTime BeginRentDate { get; set; }
    public DateTime EndRentDate { get; set; }
    public decimal RentPrice { get; set; }
    public virtual Car Car { get; set; }
}

public class Car
{
    [Key]
    public int CarId { get; set; }
    public string Brand { get; set; }
    public string Model { get; set; }
    public string NumberPlate { get; set; }
    public decimal RentPrice { get; set; }
}

Or in your Context, you can use Fluent API to declare the key, like this

builder.Entity<Car>().HasKey(x=>x.CarId);
builder.Entity<Order>().HasKey(x=>x.OrderId);

I have an idea, but it's more of a guess... Maybe that by the time you get in that Create function, the context already knows of that Order? If it does, trying to re-add it again could maybe give you that error.

In your last piece of code (the one that surprisingly work), have you tried to use an intermediary var without casting to double?

I'm also intrigued of that UpdateModel... I use EF but not MVC so maybe it has nothing to do about it, but if it's a custom function, what does it do?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM