I used the code from the Override Save Changes question to implement auditing in my application. When an entity is added everything is great. However, when an entity is modified the "CreatedOn" date is never loaded.
It is always null, so the initial "CreatedOn" date gets removed from the database.
I also tried looking in the OriginalValues collection stored in the entity, and it is populated for the "ModifiedOn" date but not for the "CreatedOn" date. Both fields are populated in the database...why would one be loaded by EF and one not be loaded?
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
{
foreach (DbEntityEntry<IAuditable> entry in changeSet)
{
switch (entry.State)
{
case EntityState.Added:
entry.Entity.CreatedOn = DateTime.Now;
entry.Entity.ModifiedOn = DateTime.Now;
break;
case EntityState.Modified:
entry.Entity.ModifiedOn = DateTime.Now;
//entry.Entity.CreatedOn date always null here
break;
}
}
}
return base.SaveChanges();
}
Maybe you are using in a MVC context? If it is, it's maybe just because you don't put your entity properties CreatedOn and ModifiedOn in an hidden field of your page. I got this problem and I solve it with a partial view.
/*My Partial View View/Shared/HiddenSystemValues.cshtml */
#model Models.IAuditable
#Html.HiddenFor(model => model.CreatedOn)
#Html.HiddenFor(model => model.ModifiedOn)
...
/*In All My Pages */
...
#Html.Partial("HiddenSystemValues")
...
Related
I am trying to resolve the error 'List ActiveOrder02 does not contain a definition for Client' that appears when I put the mouse over '.Client' on the line
#Html.DisplayNameFor(model => model.ActiveOrders02.Client)
in the following View:
#model MVCDemo2.ViewModels.Order02VM
<table class="table">
<tr><th>#Html.DisplayNameFor(model => model.ActiveOrders02.Client)</th></tr>
#foreach (var item in Model.ActiveOrders02)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Client)</td>
</tr>
}
For info here are the Controller and the ViewModel:
using MVCDemo2.Models;
using MVCDemo2.ViewModels;
using System.Linq;
using System.Web.Mvc;
namespace MVCDemo2.Controllers
{
public class Order02Controller : Controller
{
private InventoryContainer db02 = new InventoryContainer();
// GET: Order02
public ActionResult Index()
{
//Create instance of the (View)Model
var o = new Order02VM();
//Retrieves DATA from the DB
var resultSet02 = db02.SampleDbTable;
// Push the retrieved DATA into the (View)Model
o.ActiveOrders02 = resultSet02.Select(x => new ActiveOrder02
{
ID = x.tableId,
Client = x.tableClient
}).ToList();
//EDIT caused by answer of #David
return View(o);
//IT WAS: return View(o.ActiveOrders02.ToList());
}
}
}
Here follows the ViewModel:
using System.Collections.Generic;
namespace MVCDemo2.ViewModels
{
public class Order02VM
{
public List<ActiveOrder02> ActiveOrders02 { get; set; }
}
public class ActiveOrder02
{
public decimal ID { get; set; }
public string Client { get; set; }
}
}
EDIT: What I'm after is by DisplayNameFor to present the name of column tableClient from SampleDbTable as a Header of column Client in the Index view.
Thank you!
Because model.ActiveOrders02 is of type List<ActiveOrder02>, not of type ActiveOrder02.
It looks like you just want to get the display meta-data of the property, not a value of a particular instance. There's a somewhat unintuitive way to do that:
#Html.DisplayNameFor(model => model.ActiveOrders02.First().Client)
Note the use of .First() on the list. One might intuitively think that this would be error-prone, because if the list is empty it would throw an exception. However, ASP.NET isn't actually going to use that to iterate over the list. This expression is being used by the framework to identify the property into which the framework will reflect to get meta-data about that property.
In most cases, putting something like that on a list would require that the list never be empty. In this particular case, however, it should work just fine.
From your code it is clear that ActiveOrders02 is a list of objects of type ActiveOrder02.
Client is a property in an element(ActiveOrder02) in ActiveOrders02. If you want the Client value in the first item in ActiveOrders02 you can go for
#Html.DisplayNameFor(model => model.ActiveOrders02[0].Client)
Of course you need to check if ActiveOrders02 is empty or not before doing this.
You instruct the view that #model is MVCDemo2.ViewModels.Order02VM, but in the controller you set o.ActiveOrders02 to List<MVCDemo2.ViewModels.Order02VM>:
o.ActiveOrders02 = resultSet02.Select(x => new ActiveOrder02
{
ID = x.tableId,
Client = x.tableClient
}).ToList(); // <--
Then you return View(o.ActiveOrders02.ToList());, essentially calling .ToList() on o.ActiveOrders02, which already is a List<MVCDemo2.ViewModels.Order02VM>.
So, you can:
return a single instance of Order02VM to the view rather than a list with one item, or...
return the list to the view, set the view's #model to List<MVCDemo2.ViewModels.Order02VM> and take the first Order02VM instance from the list (using .First(), list index 0, etc) and go from there.
As I was trying to create and edit using single view and on the same controller,the error was coming while managing ID.
I was able to manage it while EDITING by :
#Html.HiddenFor(m => m.ID)
but while creating(ADDING) a record I was not able to get ID
[ ERROR : The ID field is required ]
.The error came while checking (ID is a Primary Key in DB):
ModelState.IsValid
as I was able to manage ID by using :
[Bind(Exclude = "ID")]
but again this will create an issue while editing.SO please give me a way to add and edit using same controller.
You always include id as a hidden field on your view:
#Html.HiddenFor(m => m.Id)
Do not make the Id property required so the validation will always pass. If it is 0 or null (if your Id property is of nullable type) then you treat it as an insert. Otherwise perform an update:
if (ModelState.IsValid)
{
if (!model.Id.HasValue || model.Id == 0){
{
// Do insert here..
} else {
// Do update here...
}
}
If you want to create a Single view for create and Edit then you should first check for the ID
If a form passes the id as 0 then its Create otherwise Edit
[HttpPost]
public ActionResult Create(Model model)
{
if(model.id != 0 )
{
//Edit Code here
}
if( model.id == 0)
{
//Create Code Here
}
return RedirectToAction("","");
}
As you have already maintained the ID as hidden. You wont have any problem on passing the ID.
But Before HTTPPOST
On HTTPGET... Follow Following Code
Public Action Result Create(int id = 0)
{
if(id != 0)
{
var model = //code for selecting the data for the respective id
return view (model);
}
return view();
}
EDITED : This should work out...
Try set Id=0 when adding and you need hidden input for Id for editing (to send back Id).
When you creating new object and you set Id=0 it's mean that EF will compute Id itself.
When you editing object you must send back Id to controller as Id is properties of your object and it's used by EF to determine which object is updating.
Excluding Id from binding it's not good idea ;) If you exclude Id so how controller/EF will know which object was sent back from view?
Clarification:
In your view you should have just: #Html.HiddenFor(m => m.Id)
In your case you can/should set Id=0 in controller/action when you adding new object.
If you want add/edit object in one view, you can create empty object (and set Id=0) in your add action and pass it trough to your view... Then you always have valid object/Model in your view whatever you adding or editing.
Example of add action:
public void Create(int id)
{
// ...
return View(new YourObject {
Id = 0,
});
}
I'm trying to update an entity using Entity Framework version 6.
I'm selecting the entity from the database like so...
public T Find<T>(object id) where T : class
{
return this._dbContext.Set<T>().Find(id);
}
And updating the entity like so..
public T Update<T>(T entity) where T : class
{
// get the primary key of the entity
object id = this.GetPrimaryKeyValue(entity);
// get the original entry
T original = this._dbContext.Set<T>().Find(id);
if (original != null)
{
// do some automatic stuff here (taken out for example)
// overwrite original property values with new values
this._dbContext.Entry(original).CurrentValues.SetValues(entity);
this._dbContext.Entry(original).State = EntityState.Modified;
// commit changes to database
this.Save();
// return entity with new property values
return entity;
}
return default(T);
}
The GetPrimaryKeyValue function is as so...
private object GetPrimaryKeyValue<T>(T entity) where T : class
{
var objectStateEntry = ((IObjectContextAdapter)this._dbContext).ObjectContext
.ObjectStateManager
.GetObjectStateEntry(entity);
return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
}
Just for clarity. I'm selecting the original entry out as I need to perform some concurrency logic (that Ive taken out). I'm not posting that data with the entity and need to select it manually out of the DB again to perform the checks.
I know the GetPrimaryKeyValue function is not ideal if there's more than one primary key on the entity. I just want it to work for now.
When updating, entity framework coughs up the error below when trying to execute the GetPrimaryKeyValue function.
The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'NAME_OF_ENTITY_IT_CANNOT_FIND'
I've written many repositories before and I've never had this issue, I cannot seem to find why its not working (hence the post).
Any help would be much appreciated.
Thanks guys!
Steve
It seems like you are having issues getting the PK from the entity being passed in. Instead of trying to go through EF to get this data you could either use their Key attribute or create your own and just use reflection to collect what the key names are. This will also allow you to retrieve multiple keys if it is needed. Below is an example I created inside of LinqPad, you should be able to set it to "Program" mode and paste this in and see it work. Hack the code up and use what you may. I implemented an IEntity but it is not required, and you can change the attribute to anything really.
Here are the results:
Keys found:
CustomIdentifier
LookASecondKey
Here is the code:
// this is just a usage demo
void Main()
{
// create your object from wherever
var car = new Car(){ CustomIdentifier= 1, LookASecondKey="SecretKey", Doors=4, Make="Nissan", Model="Altima" };
// pass the object in
var keys = GetPrimaryKeys<Car>(car);
// you have the list of keys now so work with them however
Console.WriteLine("Keys found: ");
foreach(var k in keys)
Console.WriteLine(k);
}
// you probably want to use this method, add whatever custom logic or checking you want, maybe put
private IEnumerable<string> GetPrimaryKeys<T>(T entity) where T : class, IEntity
{
// place to store keys
var keys = new List<string>();
// loop through each propery on the entity
foreach(var prop in typeof(T).GetProperties())
{
// check for the custom attribute you created, replace "EntityKey" with your own
if(prop.CustomAttributes.Any(p => p.AttributeType.Equals(typeof(EntityKey))))
keys.Add(prop.Name);
}
// check for key and throw if not found (up to you)
if(!keys.Any())
throw new Exception("No EntityKey attribute was found, please make sure the entity includes this attribute on at least on property.");
// return all the keys
return keys;
}
// example of the custom attribute you could use
[AttributeUsage(AttributeTargets.Property)]
public class EntityKey : Attribute
{
}
// this interface is not NEEDED but I like to restrict dal to interface
public interface IEntity { }
// example of your model
public class Car : IEntity
{
[EntityKey] // add the attribure to property
public int CustomIdentifier {get;set;}
[EntityKey] // i am demonstrating multiple keys but you can have just one
public string LookASecondKey {get;set;}
public int Doors {get;set;}
public string Make {get;set;}
public string Model {get;set;}
}
I am currently looking for a design pattern or rather a best practice in implementing Repository<Entity>.Update() method for a ASP.NET MVC 4 application which uses Entity Framework 5 with Code First approach.
Problem:
The problem I encountered is that when an entity is queried from the database and shown on a view it may not have all the attributes populated. As a result when the repository.Update(entity) method is invoked, the entity passed to the Update() method may have un-bound properties having null values. However they may have some values in the database. As an example Customer.Misc in below code.
So the problem comes here. According to this approach all the properties which were not bound on the view are set to Null in the database after the first Update() method call.
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Misc { get; set; }
}
[HttpGet]
public ActionResult Update(int id)
{
Repository<Customer> repo = new Repository<Customer>();
return View(repo.GetById(id)); // View only binds Customer.Name
}
[HttpPost]
public ActionResult Update(Customer customer)
{
Repository<Customer> repo = new Repository<Customer>();
repo.Update(customer); // Customer.Misc is null
...
}
public void Update(TEntity entity)
{
var entry = DbContext.Entry<TEntity>(entity);
if (entry.State == EntityState.Detached)
{
ObjectContext.ApplyCurrentValues(EntitySetName, entity);
}
DbContext.SaveChanges();
}
Solutions I could think:
Bind all entity attributes on the view:
I think this is not feasible and at the same time it may lead to performance issues since all attributes get populated.
Implement a custom method to copy property values to avoid null values being copied.
EntityHelper.CopyNotNullValues(source, target) and ignore null values in the source entity. If we do this we might not be able to set any of the values to null if required.
Implement View Models and transform data back and forth with the Domain Model.
This is the best approach I could think of so far. All the attributes bound to the View Model will get populated always, on the Update POST, copy all View Model values to the Domain Model.
Really appreciate your thoughts on this.
In Entity Framework, using ChangeObjectState or ApplyCurrentValues will cause data loss. The only way to work around this issue in this case is attaching the input entity and mark the properties to be updated. See below example:
public void Update(TEntity entity, string[] updatedProperties)
{
DbContext.Entities.Attach(entity);
var entry = DbContext.Entry<TEntity>(entity);
for (int i = 0; i < updatedProperties.Length; i++)
{
entry.SetModifiedProperty(updatedProperties[i]);
}
DbContext.SaveChanges();
}
[HttpPost]
public ActionResult Update(Customer customer)
{
Repository<Customer> repo = new Repository<Customer>();
repo.Update(customer, new string[]{ "Name" }); // Only update name
...
}
It's the best solution I can think of. You wanna have least code and good performance. It's as difficult as finding an easy and well paid job.
I have an object I want to update in the database. I'm new to EF but have done a fair bit of reading. Clearly my approach is wrong, but I don't understand why. FYI the Context referenced throughout is an ObjectContext which is newly instantiated as this code begins and is disposed immediately after. Here is my Update method - the View is the object I want to update in the database and it has 4 ICollection properties whose changes I also wish to save to the database:
public void Update(View view)
{
var original = Read(view.Username, view.ViewId);
original.ViewName = view.ViewName;
ProcessChanges<CostCentre, short>(Context.CostCentres, original.CostCentres, view.CostCentres, "iFinanceEntities.CostCentres", "CostCentreId");
ProcessChanges<LedgerGroup, byte>(Context.LedgerGroups, original.LedgerGroups, view.LedgerGroups, "iFinanceEntities.LedgerGroups", "LedgerGroupId");
ProcessChanges<Division, byte>(Context.Divisions, original.Divisions, view.Divisions, "iFinanceEntities.Divisions", "DivisionId");
ProcessChanges<AnalysisCode, short>(Context.AnalysisCodes, original.AnalysisCodes, view.AnalysisCodes, "iFinanceEntities.AnalysisCodes", "AnalysisCodeId");
int test = Context.SaveChanges();
}
First I get the original from the database because I want to compare its collections with the new set of collections. This should ensure the correct sub-objects are added and removed. I compare each collection in turn using this ProcessChanges method:
private void ProcessChanges<TEntity, TKey>(ObjectSet<TEntity> contextObjects, ICollection<TEntity> originalCollection, ICollection<TEntity> changedCollection, string entitySetName, string pkColumnName)
where TEntity : class, ILookupEntity<TKey>
{
List<TKey> toAdd = changedCollection
.Select(c => c.LookupKey)
.Except(originalCollection.Select(o => o.LookupKey))
.ToList();
List<TKey> toRemove = originalCollection
.Select(o => o.LookupKey)
.Except(changedCollection.Select(c => c.LookupKey))
.ToList();
toAdd.ForEach(a =>
{
var o = changedCollection.Single(c => c.LookupKey.Equals(a));
AttachToOrGet<TEntity, TKey>(entitySetName, pkColumnName, ref o);
originalCollection.Add(o);
});
toRemove.ForEach(r =>
{
var o = originalCollection.Single(c => c.LookupKey.Equals(r));
originalCollection.Remove(o);
});
}
This compares the new collection to the old one and works out which objects to add and which to remove. Note that the collections all contain objects which implement ILookupEntity.
My problems occur on the line where I call AttachToOrGet. This method I got from elsewhere on stackoverflow. I'm using this because I was often getting a message saying that "An object with the same key already exists in the ObjectStateManager" when attaching a new subobject. Hopefully you'll understand my confusion around this when I post the code of this method below:
public void AttachToOrGet<TEntity, TKey>(string entitySetName, string pkColumnName, ref TEntity entity)
where TEntity : class, ILookupEntity<TKey>
{
ObjectStateEntry entry;
// Track whether we need to perform an attach
bool attach = false;
if (Context.ObjectStateManager.TryGetObjectStateEntry(new EntityKey(entitySetName, pkColumnName, entity.LookupKey), out entry))
//if (Context.ObjectStateManager.TryGetObjectStateEntry(Context.CreateEntityKey(entitySetName, entity), out entry))
{
// Re-attach if necessary
attach = entry.State == EntityState.Detached;
// Get the discovered entity to the ref
entity = (TEntity)entry.Entity;
}
else
{
// Attach for the first time
attach = true;
}
if (attach)
Context.AttachTo(entitySetName, entity);
}
Basically this is saying if the entity is not already attached then attach it. But my code is returning false on the Context.ObjectStateManager.TryGetObjectStateEntry line, but throwing an exception on the final line with the message "An object with the same key already exists in the ObjectStateManager". To me this is paradoxical.
As far as I'm concerned I'm trying to achieve something very simple. Something it would take 20 minutes to write a stored procedure for. A simple database update. Frankly I don't care what is attached and what isn't because I don't wish to track changes or create proxies or lazy load or do anything else EF offers me. I just want to take a very simple object and update the database using a minimal number of trips between servers. How is this so complicated? Please someone help me - I've spent a whole day on this!
Update
Here's my ILookupEntity class:
public interface ILookupEntity<TKey>
{
TKey LookupKey { get; }
string DisplayText { get; }
}
Here's how it is implemented in CostCentre:
public partial class CostCentre : IFinancialCode, ILookupEntity<short>
{
#region IFinancialCode Members
public short ID { get { return CostCentreId; } }
public string DisplayText { get { return string.Format("{0} - {1}", Code, Description); } }
#endregion
#region ILookupEntity Members
public short LookupKey
{
get { return ID; }
}
#endregion ILookupEntity Members
}
Well, I've worked through this and found a solution, but I can't say I understand it. The crucial ingredient came when I was performing a check after the comment by #Slauma. I wanted to check I was using the correct entity set name etc so I included the following lines near the top of my AttachToOrGet method:
var key = new EntityKey(entitySetName, pkColumnName, entity.LookupKey);
object temp;
if (!Context.TryGetObjectByKey(key, out temp))
throw new Exception(string.Format("No entity was found in {0} with key {1}", entitySetName, entity.LookupKey));
Bizarrely this alone resolved the problem. For some reason, once I'd called the TryGetObjectByKey then the ObjectStateManager.TryGetObjectStateEntry call actually started locating the attached entity. Miraculous. I'd love it if anyone can explain this.
By the way, I also needed to include the following code, but that's just because in my case the modelled entities are located in a separate assembly from the context itself.
Assembly assembly = typeof(CostCentre).Assembly;
Context.MetadataWorkspace.LoadFromAssembly(assembly);