Controller method not updating(same result every time) - c#

I have following method in my mvc controller:
[HttpGet]
public ActionResult UserProfile(String username)
{
var user = db.Users.Find(username);
return View(user);
}
This function returns View with user profile. But result of this is the same, regardless of changes in database.
When I debug it seems like db is not changing at all, while in other controllers everything works just fine.
EDIT:
Place when I make changes
public ActionResult ExecuteRetreive(String username, String ISBN)
{
if (IsValid(username))
{
var resBook = db.Books.Find(ISBN);
var resUser = db.Users.Find(username);
var resRentedBooks = (from rb in db.RentedBooks
join b in db.Books on rb.ISBN equals b.ISBN
where b.ISBN == ISBN
where rb.Login == username
where rb.Returned == null
select rb).FirstOrDefault();
if (resRentedBooks == null)
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "" });
}
resRentedBooks.Returned = DateTime.Now;
resBook.IsRented = false;
resUser.RentedBooks--;
db.SaveChanges();
return RedirectToAction("Success", "FailSuccess");
}
else
{
return RedirectToAction("Fail", "FailSuccess",
new { error = "Niepoprawna nazwa użytkownika" });
}
}
Im new to this so dont laugh at my code :P When I display resUser.RentedBooks--; it is the same every time.

As a follow up to what #JeroenVannevel said in the comments, another problem that you might be having because you're using a static context (and one that I've had to deal with in the past) is that once a specific DbContext has loaded an entity (or a set of entities, in my case) it won't tend to refresh just because some outside changes were made in the database. It loads those entities into Local and just refers to those automatically if you query for it.
The solution, then, is to always put your DbContext calls wrapped up in a using block, since DbContext implements IDisposable.
One word of caution with this approach, since you're using MVC: If you are using lazy loading, and you know that your View will need some information from a child object (or to list the names of a collection of child objects), you will absolutely need to hydrate those child entities before you get out of the using block, or you will find yourself getting exceptions saying that your context has been disposed.

Related

ASP.NET MVC Updating Master-Detail Records in Single HTTP Post Request

I'm teaching myself C# and MVC but have a background in SQL. When updating an existing master-detail set of records in a single action (let's say for instance a customer order and order details), updating the master record is no problem. Regarding the detail records, I'm seeing examples that simply delete all existing details and then add them back in rather than add, delete or update only what's changed. That seems easy and effective but involves unnecessary changes to database records and might be an issue in complex relationships.
I've tried writing code that checks the existing values against posted values to determine the right EntityState (Added, Deleted, Modified, Unchanged) for each detail. Accomplishing this using LINQ Except and Intersect works but seems to cause an unexpected performance hit.
(Instead, I could load the original values in an "oldValue" hidden field in the original GET request to compare to the POST values except that would be unreliable in a multi-user environment and seems like a bad idea.)
I'll be happy to provide code examples, but my question is more about best practices. Is there a preferred method for updating existing master-detail sets of records?
EDIT: I've added the code below in response to questions. In this example, our application allows additional attributes to be attached to a product, kept in a separate table ProductAttributes. The view allows the user to edit both the product and the attributes on the same webpage and save at the same time. The code works fine but seems slow and lags at SaveChanges.
public ActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
db.Entry(product).State = EntityState.Modified;
// Establish entity states for product attributes.
List<ProductAttribute> existingAttributes = new List<ProductAttribute>();
existingAttributes = db.ProductAttributes.AsNoTracking()
.Where(x => x.Sku == product.Sku).ToList();
// Review each attribute that DID NOT previously exist.
foreach (ProductAttribute pa in product.ProductAttributes
.Except(existingAttributes, new ProductAttributeComparer()))
{
if (pa.Value is null)
{
// Value didn't exist and still doesn't.
db.Entry(pa).State = EntityState.Unchanged;
}
else
{
// New value exists that didn't before.
db.Entry(pa).State = EntityState.Added;
}
}
// Review each attribute that DID previously exist.
foreach (ProductAttribute pa in product.ProductAttributes
.Intersect(existingAttributes, new ProductAttributeComparer()))
{
if (pa.Value is null)
{
// Value existed and has been cleared.
db.Entry(pa).State = EntityState.Deleted;
}
else
{
if (pa.Value != existingAttributes
.FirstOrDefault(x => x.Attribute == pa.Attribute).Value)
{
// Value has been altered.
db.Entry(pa).State = EntityState.Modified;
}
else
{
// Value has not been altered.
db.Entry(pa).State = EntityState.Unchanged;
}
}
}
db.SaveChanges();
return RedirectToAction("Details", new { id = product.ProductId });
}
return View(product);
}
internal class ProductAttributeComparer : IEqualityComparer<ProductAttribute>
{
public bool Equals(ProductAttribute x, ProductAttribute y)
{
if (string.Equals(x.Attribute, y.Attribute,
StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
public int GetHashCode(ProductAttribute obj)
{
return obj.Attribute.GetHashCode();
}
}
}

Strange anomoly with: Attaching an entity of type 'X' failed because another entity of the same type already has the same primary key value

I had a similar issue with this question, when updating a record using EF6.
I really thought I had cracked the whole updating thing, but now have to almost identical functions updating in what I think was an identical way. One works, the other doesn't. I have fixed the one that doesn't work by using Jamie's comment in the above question, but I'd like to understand if the function that works, really shouldn't and so is on borrowed time and I should make more like the 'fixed' one. Or, why the 'fixed' one didn't work in the first place. I even moved them into the same controller so that the database (DB) context was guaranteed the same. Have I missed something and they are not identical (functionally) at all?
It might also help some others out there that struggle with this as I did.
The function that works (cut down) is:
[HttpPost]
[Route("UpdateAddBusinessService")]
public async Task<IHttpActionResult> UpdateAddBusinessService(BusinessServiceDTO servicetoupdateoradd)
{
... pre check stuff...
try
{
if (servicetoupdateoradd.Id != null) // This is an existing service to be updated - if Is Null then create new
{
BusinessService businessService = await db.BusinessServices.FindAsync(servicetoupdateoradd.Id);
if (businessService != null)
{
Mapper.Map(servicetoupdateoradd, businessService);
db.Entry(businessService).State = EntityState.Modified;
await db.SaveChangesAsync();
return Ok("Service Updated");
}
else
The function that doesn't work is:
[HttpPost]
[Route("UpdateImage")]
public async Task<IHttpActionResult> UpdateImage(ImageDTO imageDTO)
{
... pre check stuff ...
try
{
// First find the image
// Image imagetoupdate = await db.Images.FindAsync(imageDTO.Id); <<-This FAILS.
Image imagetoupdate = db.Images.AsNoTracking().Single(x => x.Id == imageDTO.Id); <<- This WORKS
if (imagetoupdate != null)
{
imagetoupdate = Mapper.Map<ImageDTO, Image>(imageDTO); // Move the stuff over..
db.Entry(imagetoupdate).State = EntityState.Modified;
await db.SaveChangesAsync();
return Ok();
}
I wondered (as you will no doubt), if my Mapper function was doing anything, but I suspect not (without digging too deep, but I guess it could be), my Mapper.Config functions for the two DTO's are very similar:
cfg.CreateMap<Image, ImageDTO>();
cfg.CreateMap<ImageDTO, Image>();
and:
cfg.CreateMap<BusinessService, BusinessServiceDTO>();
cfg.CreateMap<BusinessServiceDTO, BusinessService>();
I would really just like to understand the 'correct' way of doing this so it doesn't bite me again. Thanks in advance.
EDIT: I was asked (quite reasonably) if the 'pre-check stuff' does anything to fetch the data, it doesn't, but there is a subtle difference, that I might have missed...
This is from the BusinessService function that works:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
string userid = User.Identity.GetUserId(); //Check user is valid
if (servicetoupdateoradd.UserId != userid)
{
var message = "User Id Not found - Contact support";
HttpResponseMessage err = new HttpResponseMessage() { StatusCode = HttpStatusCode.ExpectationFailed, ReasonPhrase = message };
return ResponseMessage(err);
}
This is from the UpdateImage function that didn't work:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
string userid = User.Identity.GetUserId();
SGGUser user = db.Users.Find(userid); // Use this find and not UserManager becuase its a different context and buggers up the file save
if (user == null)
{
var message = "User Id Not found - Contact support";
HttpResponseMessage err = new HttpResponseMessage() { StatusCode = HttpStatusCode.ExpectationFailed, ReasonPhrase = message };
return ResponseMessage(err);
}
I see that in this one, though I don't fetch the relevant data, I do use the 'db' context.. could that be it?? The Image object does contain a reference to the user, so maybe that does some magic in the background code?
Appologies, I just didn't want to clutter up the question too much...
This line:
Mapper.Map(servicetoupdateoradd, businessService);
and this line:
imagetoupdate = Mapper.Map<ImageDTO, Image>(imageDTO); // Move the stuff over..
look similar, but do two different things.
The first line will tell Automapper to copy values from the first object over to the second object reference using the mapping rules.
The second line will tell Automapper to make a completely new reference to the entity with the mapped over values from the provided object and return it.
So in the first case, the entity reference is preserved to the one the DbContext knows about. The reference was loaded from a DbContext and should be tracking changes so you shouldn't even need to set it's entity state. In the second case, Automapper is creating an entirely new reference and assigning it over top the original reference. EF is treating that as a completely new instance and trying to attach it, resulting in it complaining because the context had already loaded that entity, you just overwrote the reference.
It should work if you change the second instance to:
Mapper.Map(imageDTO, imagetoupdate);

Add or Update a Record?

I have an MVC application with the following code in the POST method of the controller. I am doing an EF Add and obviously that is not right. I want it to add the record if it doesn't exist, otherwise Update. How can I do that please?
try
{
AttributeEntities db = new AttributeEntities();
IEnumerable<string> items = viewModel.SelectedAttributes2;
int i = 0;
foreach (var item in items)
{
var temp = item;
// Save it
SelectedHarmonyAttribute attribute = new SelectedHarmonyAttribute();
attribute.CustomLabel = viewModel.ItemCaptionText;
attribute.IsVisible = viewModel.Isselected;
string harmonyAttributeID = item.Substring(1, 1);
// attribute.OrderNumber = Convert.ToInt32(order);
attribute.OrderNumber = i++;
attribute.HarmonyAttribute_ID = Convert.ToInt32(harmonyAttributeID);
db.SelectedHarmonyAttributes.Add(attribute);
db.SaveChanges();
}
}
You would need to check the database for the record you are trying to add/update. If the look-up returns null, that means that it doesn't exist in the database. If it does, you can modify the record that you looked up and call db.SaveChanges() to persist the changes you made to the database.
Edit:
int id = Convert.ToInt32(harmonyAttributeID);
var existingEntry = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == id);
One common way to determine an add or update is by simply looking at an identifier field, and setting the appropriate state.
using System.Data;
SelectedHarmonyAttribute attribute;
using (var db = new YourDbContext())
{
db.Entry(attribute).State = attribute.HarmonyAttribute_ID == 0 ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
You could import the System.Data.Entity.Migrations namespace and use the AddOrUpdate extension method:
db.SelectedHarmonyAttributes.AddOrUpdate(attribute);
db.SaveChanges();
EDIT:
I'm assuming that SelectedHarmonyAttributes is of type DbSet
EDIT2:
Only drawback with doing it this way (and it may not be a concern for you), is that your entity isn't responsible for it's own state change. This means that you can update any property of the entity to something invalid, where you might want to internally validate it on the entity itself or maybe do some other processing you always want to occur on update. If these things are a concern for you, you should add a public Update method onto the entity and check for its existence on the database first. e.g:
var attribute = db.SelectedHarmonyAttributes.SingleOrDefault(x => x.HarmonyAttribute_ID == harmonyAttributeID);
if (attribute != null)
{
attribute.Update(viewModel.ItemCaptionText, viewModel.Isselected, i++);
}
else
{
attribute = new Attribute(viewModel.ItemCaptionText, viewModel.Isselected);
db.SelectedHarmonyAttributes.Add(attribute);
}
db.SaveChanges();
Your update method might look something like:
public void Update(string customLabel, bool isVisible, int orderNumber)
{
if (!MyValidationMethod())
{
throw new MyCustomException();
}
CustomLabel = customLabel;
IsVisible = isVisible;
OrderNumber = orderNumber;
PerformMyAdditionalProcessingThatIAlwaysWantToHappen();
}
Then make all of the entities' properties public "get" but protected "set" so they can't be updated from outside the entity itself. This might be going off an a bit of a tangent but using the AddOrUpdate method would assume you don't want to control the way an update occurs and protect your domain entity from getting into an invalid state etc. Hope this helps!

Entity Framework Update Problem

I am using Entity Framework & LINQ to retrieve data. I am having a problem with the following:
var customer= db.customers.where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
The fields are changing in the database before I call:
db.SaveChanges();
How do I avoid this?
As others have said, I believe that you are using your context in another place as well and that other location is calling savechanges and updating everything. Try doing what #Evan suggested with a using statment to make sure you have a fresh context.
AsNoTracking will not ensure that you get a entity that is not cached in the database, its purpose is to not put the objects inside the context. If you use AsNoTracking and then change the entities returned in the query you will need to attach them as modified to the context before calling savechanges or else they won't be updated.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
ctx.customers.Attach(customer);
ctx.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
I would have just commented on the other posts but don't have enough rep yet.
Check whether you are passing the db object to some other method, and SaveChanges() is called there?
Or check whether you have a catch block of an exception and you might be using SaveChanges() in the catch block to log error message?
(These are common programming mistakes)
The fields are changing in the database before I call
If you mean changing as in changing outside of application, changes in SQL Management Studio for example. Entity Framework cannot detect those changes, so as a result you might get stale objects that was cached by Entity Framework. To prevent receiving cached object and get the up-to-date values from database, use AsNoTracking.
Try putting AsNoTracking():
var customer= db.customers.AsNoTracking().where(c=>c.id==1);
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
Or if your problem is to detect concurrent updates(unfortunate terminology, it doesn't apply to UPDATE only) to same row, use rowversion(aka timestamp) field type; then on your .NET code add Timestamp attribute on the property. Example: http://www.ienablemuch.com/2011/07/entity-framework-concurrency-checking.html
public class Song
{
[Key]
public int SongId { get; set; }
public string SongName { get; set; }
public string AlbumName { get; set; }
[Timestamp]
public virtual byte[] Version { get; set; }
}
UPDATE (after your comment):
If you really has no intent to persist your object changes to database. Try detaching the object.
Try this:
var customer= db.customers.where(c=>c.id==1);
db.Entry(customer).State = System.Data.EntityState.Detached; // add this
customer.name=santhosh;
customer.city=hyd;
db.SaveChanges();
That won't save your changes on name and city to database.
If you want something more robust(the above will fail an exception if the object was not yet attached), create a helper:
private static void Evict(DbContext ctx, Type t,
    string primaryKeyName, object id)
{           
    var cachedEnt =
        ctx.ChangeTracker.Entries().Where(x =>  
            ObjectContext.GetObjectType(x.Entity.GetType()) == t)
            .SingleOrDefault(x =>
        {
            Type entType = x.Entity.GetType();
            object value = entType.InvokeMember(primaryKeyName,
                                System.Reflection.BindingFlags.GetProperty,
null, x.Entity, new object[] { });
 
            return value.Equals(id);
        });
 
    if (cachedEnt != null)
        ctx.Entry(cachedEnt.Entity).State = EntityState.Detached;
}
To use: Evict(yourDbContextHere, typeof(Product), "ProductId", 1);
http://www.ienablemuch.com/2011/08/entity-frameworks-nhibernate.html
Can you give a little more of the surrounding code? Might be a little difficult without seeing how you are constructing your context.
This is how I typically handle updates (I hope it might give some insight):
using (var ctx = new myModel.myEntities())
{
int pollID = 1;
var poll = (from p in ctx.Polls
where p.PollID == pollID
select p).FirstOrDefault();
poll.Question = txtPoll.Text.Trim();
ctx.SaveChanges();
}
Jack Woodward, yours did not work for me.
I had to change it up a little for SQL Compact.
var customer= db.customers.AsNoTracking().Single(c=>c.id==1);
db.customers.Attach(customer);
customer.name=santhosh;
customer.city=hyd;
db.ObjectStateManager.ChangeObjectState(customer, System.Data.EntityState.Modified);
db.SaveChanges();
db.Dispose();
This worked alot better.

LINQ to SQL: To Attach or Not To Attach

So I'm have a really hard time figuring out when I should be attaching to an object and when I shouldn't be attaching to an object. First thing's first, here is a small diagram of my (very simplified) object model.
In my DAL I create a new DataContext every time I do a data-related operation. Say, for instance, I want to save a new user. In my business layer I create a new user.
var user = new User();
user.FirstName = "Bob";
user.LastName = "Smith";
user.Username = "bob.smith";
user.Password = StringUtilities.EncodePassword("MyPassword123");
user.Organization = someOrganization; // Assume that someOrganization was loaded and it's data context has been garbage collected.
Now I want to go save this user.
var userRepository = new RepositoryFactory.GetRepository<UserRepository>();
userRepository.Save(user);
Neato! Here is my save logic:
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
//DataContext.Organizations.Attach(user.Organization);
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
// Finished here as well.
user.Detach();
}
So, here we are. You'll notice that I comment out the bit where the DataContext attachs to the organization. If I attach to the organization I get the following exception:
NotSupportedException: An attempt has been made to Attach or Add an
entity that is not new, perhaps having
been loaded from another DataContext.
This is not supported.
Hmm, that doesn't work. Let me try it without attaching (i.e. comment out that line about attaching to the organization).
DuplicateKeyException: Cannot add an entity with a key that is already
in use.
WHAAAAT? I can only assume this is trying to insert a new organization which is obviously false.
So, what's the deal guys? What should I do? What is the proper approach? It seems like L2S makes this quite a bit harder than it should be...
EDIT: I just noticed that if I try to look at the pending change set (dataContext.GetChangeSet()) I get the same NotSupportedException I described earlier!! What the hell, L2S?!
It may not work exactly like this under the hood, but here's the way I conceptualize it: When you summon an object from a DataContext, one of the things Linq does is track the changes to this object over time so it knows what to save back if you submit changes. If you lose this original data context, the Linq object summoned from it doesn't have the history of what has changed in it from the time it was summoned from the DB.
For example:
DbDataContext db = new DbDataContext();
User u = db.Users.Single( u => u.Id == HARD_CODED_GUID );
u.FirstName = "Foo";
db.SubmitChanges();
In this case since the User object was summoned from the data context, it was tracking all the changes to "u" and knows how to submit those changes to the DB.
In your example, you had a User object that had been persisted somewhere (or passed from elsewhere and do not have it's original DataContext it was summoned from). In this case, you must attach it to the new data context.
User u; // User object passed in from somewhere else
DbDataContext db = new DbDataContext();
u.FirstName = "Foo";
DbDataContext.Users.Attach( u );
db.SubmitChanges();
Since the relationship between user and organization is just a GUID (OrganizationId) in your data model, you only have to attach the user object.
I'm not sure about your scaffolding code, but maybe something like this:
private const Guid DEFAULT_ORG = new Guid("3cbb9255-1083-4fc4-8449-27975cb478a5");
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
DataContext.Users.Attach(user);
}
DataContext.SubmitChanges();
}
So "attach" is used when you take an object that exists from the database, detach it (say by marshalling it over a webservice somewhere else) and want to put it back into the database. Instead of calling .Attach(), call .InsertOnSubmit() instead. You're almost there conceptually, you're just using the wrong method to do what you want.
I used an big table with 400+ columns. No way am I going to map and test all that!
Get the original object from database, and attach it with the amended object. Just make sure the object coming back in is fully populated other wise it will override it the DB with blanks!
Or you can copy the original GET into memory and work on a proper copy (not just reference) of the MOdel, then pass the original and the changed one in, instead of re getting like I do in the example. This is just an example of how it works.
public void Save(User user)
{
if (!DataContext.Users.Contains(user))
{
user.Id = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.Disabled = false;
user.OrganizationId = DEFAULT_ORG; // make the foreign key connection just
// via a GUID, not by assigning an
// Organization object
DataContext.Users.InsertOnSubmit(user);
}
else
{
var UserDB = DataContext.Users.FirstOrDefault(db => db.id == user.id); //Costs an extra call but its worth it if oyu have 400 columns!
DataContext.Users.Attach(user, userDB); //Just maps all the changes on the flu
}
DataContext.SubmitChanges();
}

Categories