Been working on this C# Entity Framework update problem for a bit now. Maybe one of you can see what I'm missing.
The problem is within the userEntry portion of the code. I've traced through and made sure that userEntry is indeed populated with the information that I intend to update. When entities.SaveChanges(); is invoked, the record is not updated in the database.
int userId;
using (Entities entities = new Entities())
{
MSIFeedStoreData feedStoreEntry = entities.MSIFeedStoreDatas
.FirstOrDefault((d) => d.Alias == user.Alias);
if (Object.ReferenceEquals(feedStoreEntry, null))
{
throw new ArgumentException("The user's alias could not be located in the feed store. The user cannot be added at this time.");
}
int feedStorePersonnelIdValue;
if (Int32.TryParse(feedStoreEntry.PersonellID, out feedStorePersonnelIdValue))
{
user.EmployeeId = feedStorePersonnelIdValue;
}
else
{
throw new ApplicationException("DATABASE BUG CHECK: An entry was found in the feed store for this user but the personnel ID could not be parsed.");
}
MSIUser userEntry = entities.MSIUsers
.FirstOrDefault((u) => u.EmployeeID == feedStorePersonnelIdValue);
if (Object.ReferenceEquals(userEntry, null))
{
userEntry = Mapper.Map<MSIUser>(user);
userEntry = entities.MSIUsers.Add(userEntry);
}
else
{
Mapper.DynamicMap<User, MSIUser>(user, userEntry);
entities.MSIUsers.Attach(userEntry);
}
userId = userEntry.MSIUser_ID;
entities.SaveChanges();
}
return userId;
}
Remove the call to Attach, its already attached to the context.
Related
I'm discovering ASP.NET Core MVC and I'm on my first project. Creating a cool web shop.
I'm currently wondering how to implement faulty information checking for example in the controller
Let's say there a product page, whenever users clicks on a product they will hit the function below.
As you can see the function accepts an int parameter named id, it will search in the database for the id that fits the productId, but I'm wondering how do I add error checking here? Like for example if the id does not exist in database return to page XX?
Also feel free to give suggestions to the function if you don't like it.
I've already tried to do a simple if and else statement
if(productvm == null)
{
then
return RedirectToPage("Index")
}
else
return View("ProductPage", productVm);
but it didn't seem to hit the if statement
[Route("ProductPage/{id}")]
public IActionResult ProductPage(int id)
{
Product product = _uow.Products.SelectProduct(id);
var stockViewModels = new List<StockViewModel>();
foreach (Stock stock in product.Stock)
{
stockViewModels.Add(new StockViewModel()
{
Id = stock.Id,
Description = stock.Description,
IsAvailable = stock.IsAvailable,
Quantity = stock.Quantity,
});
}
ProductViewModel productVm = new ProductViewModel
{
Name = product.Name,
Id = product.Id,
Description = product.Description,
Price = product.Price,
Stocks = stockViewModels,
};
if (productVm == null)
{
return RedirectToPage("Productslist");
}
else
{
return View("ProductPage", productVm);
}
}
I basically want an error handling the controller if the id is not found in the database then execute XX
The way how I test the function is to change the ID when browsing the page with an ID that does not exist in the database, then I get this error:
https://i.imgur.com/1amWx43.png
and I want to handle it
I think your problem is that you have new the productVm object before the if, so it will never be null, for your case, you should get check the product object and not the productVm, for example:
Product product = _uow.Products.SelectProduct(id);
if (product == null)
{
return RedirectToPage("Productslist");
}
else
{
return View("ProductPage", productVm);
}
Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");
when i try to save the the new Student object. it works fine but when i am trying to modify the data, it doesn't update the data. Instead none error is thrown on SaveChanges.
i am using code first approach (using mysql provider) and here it is the complete source.
https://www.dropbox.com/s/e34frntq8u5gsmh/SchoolManagementSystem.rar?dl=0
my tired code is this :
public ActionResult Create(Student student, HttpPostedFileBase Image)
{
try
{
if (ModelState.IsValid)
{
student = db.Students.Find(student.ID);
if (student.ID > 0)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
}
else
{
if (student.Basic == null) student.Basic = new BasicInformation();
if (Image != null && Image.ContentLength > 0)
{
student.Basic.PictureUrl = Image.FileName;
string path = Server.MapPath(("~/Images/"));
Image.SaveAs(path + Image.FileName);
}
db.Students.Add(student);
db.SaveChanges();
}
return RedirectToAction("StudentList");
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
return View(student);
}
This seems to be the modify scenario :
if (student.ID > 0)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
}
Eliminate this line of code :
db.Entry(student).State = EntityState.Modified;
and instead add code that updates the object with the new modified values.
Hope this helps.
By this line
student = db.Students.Find(student.ID);
you overwrite the student that enters the method and you lose all changes. You can do two things in stead:
Only remove the line. Your code will now attach the modified student as EntityState.Modified.
Fetch the original student from the database and copy the modified values to it:
var studentOrg = db.Students.Find(student.ID);
db.Entry(studentOrg).CurrentValues.SetValues(student);
(both SaveChanges calls can be moved to one just before return RedirectToAction...)
Option 1 will generate an update statement containing all Student's fields, but not have a roundtrip to get the original record.
Option 2 has this roundtrip, but only updates modified fields. This (option 2) can be beneficial when changes are audited or when concurrency should be minimized.
Is the essence of Project, the creation of which is necessary to check whether there is already an entity with the same name. When editing needs such as checking, but keep in mind that the old and the new name of the entity can be matched.
You also need to display an error message. For this I use interface IValidatableObject, but do not know how to tell the Validate method the object is currently being edited or created
DbContext.ValidateEntity takes the IDictionary<Object, Object> items as the second parameter. You can pass any data there and the data you pass will be passed to IValidatableObject.Validate in the ValidationContext.Items
Assuming you refer to check EF cant do for you.
This is actually difficult to check. You are checking an entity after it has been added to the context. It should not check itself and needs to consider other items in context that are not yet saved. As well as the DB. There are several 3 combinations plus an self recognition. Record a an entity record in LOCAL when ID is blank/new ie multiple new inserts needs careful coding. (Consider using temp IDs)
the not yet saved entries should be in context
Context.Set<TPoco>().Local
and get data from DB and keep in a temp list. BUT dont put in context.
Or use a SECOND context.
var matchingSet = Context.Set<TPoco>().AsNoTracking() // not into context...
.Where(t=>t.field == somevalue).ToList();
So what about logical and actual duplicates on the DB. Logical duplicates are duplicates on a field with no unique index that from a business perspective should be unique.
If you want to check those...
You need to read the DB.... BUT if these records are currently being changed, you CAN NOT just put them into the Context. You would overwrite them.
But what if the values the logical key values have changed?
Something caused a logical dup on a record on the DB may no longer be a dup once saved or vice verse. Is that still a dup or not ?
So you need to decide how you match LOCAL versus loaded records.
Ie check LOCAL and matching DB records and decidr what to do if a record is in both, only local or only db.
LOCAL ONLY and DB Only is easy.
But in both... That is your business process decision.
Problem is solved using method ModelState.AddModelError (string, string) in actions Edit and Create.
[HttpPost]
[HandleError(View="AjaxError")]
public ActionResult Edit(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
int projectId = (from p in db.Projects
where p.Name == data.CurrentObject.Name
select p.ProjectID).FirstOrDefault();
if (projectId == data.CurrentObject.ProjectID)
{
db.Projects.Attach(data.CurrentObject);
db.ObjectStateManager.ChangeObjectState(data.CurrentObject, EntityState.Modified);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
}
data.ObjectToEdit = data.CurrentObject;
return Projects(data);
}
[HttpPost]
[HandleError(View = "AjaxError")]
public ActionResult Create(ProjectsViewData data)
{
if (ModelState.IsValid)
{
if (!ContainsProject(data.CurrentObject.Name))
{
db.Projects.AddObject(data.CurrentObject);
db.SaveChanges();
return Projects(data);
}
else
{
ModelState.AddModelError("Name", Localizer.ProjectAlreadyExists);
}
}
data.ObjectToAdd = data.CurrentObject;
return Projects(data);
}
Helper method:
private bool ContainsProject(string projectName)
{
if (projectName != null)
{
projectName = Regex.Replace(projectName.Trim(), "\\s+", " ");
List<string> projects = new List<string>();
var projectNames = (from p in db.Projects
select p.Name.Trim()).ToList();
foreach (string p in projectNames)
{
projects.Add(Regex.Replace(p, "\\s+", " "));
}
if (projects.Contains(projectName))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
I am trying to add an autoincrement to a simple model via an IDocumentStoreListener. I have found that the documentation regarding implementation of this feature is fairly sparse (any pointers would be greatly appreciated). I have been trying to follow this blog post but it appears to be out of date. When i try to implement
store = new EmbeddableDocumentStore
{
RunInMemory = true
}
.RegisterListener(new AuditableEntityListener(() => "Test User"))
.Initialize();
I get a build error stating "Cannot convert lambda expression to type Raven.Client.IDocumentStore because it is not a delegate type".
I managed to get it to build by using this code
store = new EmbeddableDocumentStore
{
RunInMemory = true
}
.RegisterListener(new AuditableEntityListener(store ))
.Initialize();
The code for the listener is as follows
public class PublicIdStoreListener : IDocumentStoreListener
{
HiLoKeyGenerator generator;
IDocumentStore store;
public PublicIdStoreListener(IDocumentStore store)
{
this.store = store;
generator = new HiLoKeyGenerator(store, "verifications", 1024);
}
public void AfterStore(string key, object entityInstance, RavenJObject metadata)
{
throw new NotImplementedException();
}
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
var verification = entityInstance as VerifyAccountModel;
if (verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
}
return false;
}
}
However, when i run the application it hits the PublicIdStoreListener when any document is stored, not just the VerifyAccountModel, which causes the application to throw an exception.
I was wondering if anyone can point me in the right direction on this as I am confused as to how this is actually supposed to be implemented. Thanks in advance.
EDIT
I updated the documentlistener to the following
public bool BeforeStore(string key, object entityInstance, RavenJObject metadata)
{
if (entityInstance.GetType() == new VerifyAccountModel().GetType())
{
var verification = entityInstance as VerifyAccountModel;
if (verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
}
}
return true;
}
UPDATE
I figured out that i cant attach the store via RegisterListener in the same line that it is instantiated. It has to be done afterwards otherwise the store is still null when passed in. Thank you for your help.
I am not sure if there's a way to register the listener to only fire for certain types, but you can certainly structure your code to only process VerifyAccountModel entities.
var verification = entityInstance as VerifyAccountModel;
if (verification == null)
return false; // We can't do anything, just let it pass through
Also, my understanding is that you should return true when you make a change, false if no change was made. This determines whether the entity needs to be re-serialized. If that is correct, the whole thing might be restructured as follows.
var verification = entityInstance as VerifyAccountModel;
if (verification != null && verification.PublicId == "0")
{
verification.PublicId = generator.GenerateDocumentKey(store.Conventions, entityInstance);
return true; // change made, re-serialize
}
return false; // no change made