Hi,
I am using EntityFramework for my ASP.NET MVC website but have some problems with the update.
This is how my update code looka like :
using (BissEntities context = new BissEntities())
{
if (adCategoryFilter.Id < 1)
context.AddToAdCategoryFilter(adCategoryFilter);
else
context.Refresh(System.Data.Objects.RefreshMode.ClientWins, adCategoryFilter);
if (context.SaveChanges() > 0)
return true;
}
return false;
When executing the context.Refresh i get the following exception :
The element at index 0 in the collection of objects to refresh has a null EntityKey property value or is not attached to this ObjectStateManager.
Stacktrace : at System.Data.Objects.ObjectContext.RefreshCheck(Dictionary`2 entities, Object entity, EntityKey key)
at System.Data.Objects.ObjectContext.AddRefreshKey(Object entityLike, Dictionary`2 entities, Dictionary`2 currentKeys)
at System.Data.Objects.ObjectContext.RefreshEntities(RefreshMode refreshMode, IEnumerable collection)
at System.Data.Objects.ObjectContext.Refresh(RefreshMode refreshMode, Object entity)
at Biss.Models.FilterModel.UpdateCategoryFilter(AdCategoryFilter adCategoryFilter) in C:\Users\Snowman\Documents\Visual Studio 2010\Projects\Biss\Biss\Models\FilterModel.cs:line 86
This is not the first time I get this problem. First I thought that it might have to do with the relations in the database but after these was removed from the effected table the same exception remained.
Where does the adCategoryFilter come from?
The adCategoryFilter is instansiated(new) and then filled with data from the ViewObject(from the website). It does have the required data like filter Id (to map the filter to correct row in db).
Pleas explain why Im getting this problem and how I could solve it.
BestRegards
Because your using ASP.NET MVC, your working in a stateless environment. That means, once a request has finished processing, there is no more "Entity Framework Memory", or "The Graph".
So, you need to explicitly tell EF you wish to add or update.
Here's how you do it:
using (BissEntities context = new BissEntities())
{
if (adCategoryFilter.Id < 1)
context.AdCategoryFilters.AddObject(adCategoryFilter);
else {
var stub = new AdCategoryFilters { Id = adCategoryFilter.Id };
context.AdCategoryFilters.Attach(stub);
context.AdCategoryFilters.ApplyCurrentValues(adCategoryFilter);
}
context.SaveChanges();
}
That is referred to as the stub technique.
In short, you create a new entity with the same entity key as the entity you are trying to UPDATE (in your case, the entity key is "Id").
You then "attach" this stub (so it's tracked by the EF internal graph), then override the values on this stub with your entity to UPDATE, then save changes.
I can't use UpdateModel, as i have a multi-layered architecture and use POCO's, custom viewmodels, etc - so i have created a custom "UpdateModel" method on my service/repository - which does a (more complicated) version of the above.
Also try not to use "if Id < 1, it's an add" with ASP.NET MVC - as if you forget to bind the ID on the view, it will be passed as 0, so even though you could be doing an update, your above code will try and do an add.
Instead be more explicit - have seperate action methods for Add/Update.
HTH.
Instead of refreshing, try retrieving the object and updating its properties using something like an auto-mapper (or UpdateModel in MVC controller)
The EntityKey is a separate thing to the id property, with some other stuff going on under the hood. Your newly created object is missing this stuff, which is where the problem is coming from.
The pattern goes a little like (not a C# guy so please excuse syntax):
var context = new MyEntities();
var originalObject = context.MyObjectSet.Single(x => x.Id == viewmodel.Id);
UpdateModel(originalObject);
context.SaveChanges();
The crucial difference is that the newly retrieved object has got the EntityKey all set correctly. You can validly use the id property to detect a new/existing object, but there is more to the EntityKey than just that property.
Related
I guess I just don't understanding EF tracking. I have the context added via dependency injection via:
builder.Services.AddDbContext<OracleContext>(options => options.UseOracle(OracleConnectionString, b => b.UseOracleSQLCompatibility("11"))
.LogTo(s => System.Diagnostics.Debug.WriteLine(s))
.EnableDetailedErrors(Settings.Dev_System)
.EnableSensitiveDataLogging(Settings.Dev_System)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
I set the tracking behavior to NoTracking here (at least so I thought).
I have a .NET Controller that has the context in its constructor. It passes this context to my class constructor containing my methods. Pretty much everything works fine... except for one:
I have a method that does a context.RawSqlQuery to get a list of objects. I iterate over these objects calling two separate methods from a different class that was generated the same way (using the injected context). This method first does a EF query to verify the object does not already exist, if it does it returns it and we move on - no issues. On the query to check if it exists I also added .AsNoTracking() for SnGs. However, if the object does not exist, and I try to make a new one... every time I do an context.add I get
"The instance of entity type 'Whatever' cannot be tracked because another instance with the key value '{MfrId: 90}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
I have tried adding
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking - no change
I have tried adding context.Entry(NewItem).State = EntityState.Detached; before and after the call - no change.
I tried a loop in the context that gets all tracked objects and sets them to detached - no change.
What am I missing here? First - why is it tracking at all? Second - any suggestions on how to get passed this? Should I just give up using dependency injection for the context (suck... lots of rework for this)?
As requested - here is the class & method that is failing (non related stuff removed):
public class AssetMethods : IAssetMethods
{
public OracleContext db;
public AssetMethods(OracleContext context)
{
db = context;
}
public CcpManufacturer? CreateNewManufacturer(CcpManufacturer NewMan, string ActorID)
{
...blah blah non DB validation stuff removed...
//Check if it exists already
CcpManufacturer? OldMan = db.CcpManufacturers.Where(m=>m.MfrName == NewMan.MfrName).AsNoTracking().FirstOrDefault();
if (OldMan != null) {
return OldMan;
}
//Who done did it
NewMan.CreatedBy = ActorID;
NewMan.CreationDate = DateTime.Now;
NewMan.Isenabled = true;
//save
db.CcpManufacturers.Add(NewMan);
db.SaveChanges(); //fails here
//Prevent XSS Reflection
return db.CcpManufacturers.Find(NewMan.MfrId);
}
}
this method is called from this code. The OM is also using the injected context
List<MasterItem> Items = OM.RawSqlQuery(Query, x => new MasterItem { MFR_NAME = (string)x[0], MODEL_NUMBER = (string)x[1], LEGAL_NAME= (string)x[2]});
foreach (MasterItem item in Items)
{
CcpManufacturer? Man = new() {
MfrName = item.MFR_NAME,
Displayname = item.MFR_NAME
};
Man = AM.CreateNewManufacturer(Man,System.Id); //if its new.. it never get passed here because of the discussed error...
if (Man == null || Man.MfrId == 0)
{
continue;
}
.... other stuff
}
So the mfr id is added to a new object that's passed to a pretty much identical methods to create a item (where the mfr id is attached). Now - if I detach THAT item - I am ok. But why is it tracking when I have it turned off pretty much everywhere?
Yes, you found your problem.
Turning off Tracking affects what EF does when querying for entities. This means when I tell EF to read data from the DB and give me entities, it will not hang onto references of those entities.
However, entities you tell a DBContext to ADD to a DbSet and related entities will be tracked, regardless of your tracking setting.
So if I do something like:
var entity1 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
var entity2 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
The references to entity1 and entity2 will be 2 distinct references to the same record. Both are detached, so the DbContext isn't tracking either of them. I can use and attach either of them to perform an update, but that entity would be from that point considered Attached until I explicitly detach it again. Attempting to use the other reference for an update would result in that error. If I query specifying NoTracking after I have attached and updated that first entity reference, I will get back a new untracked entity reference. The DbContext doesn't return it's tracked reference, but it doesn't discard it either.
The exact same thing happens if I add a new entity then query for it specifying NoTracking. The query returns an untracked reference. So if you try and attach it to update a row, EF will complain about the reference it is already tracking.
I don't recommend diving down the rabbit hole of passing around detached entities unless you're keen to spend the time to really understand what is going on behind the scenes and prepared for the pretty deliberate and careful handling of references. The implications aren't just things not working as expected, it's having things work or not work on a completely situational basis which can be a nasty thing to debug and fix, even when you know what to look for.
I am using entity framework 5.0. I am in a rocess od changing my app from ObjectContext to DbContext model. DbContext should be according to microsoft the recommended approach. I use database forst approach and I have generated model form database.
But, at a very first simple task there is a problem. Namely simple update of a record is broken.
Let's have a simple table Item, for ilustration only:
Item
(
ItemId int NOT NULL, -- Primary key
Name nvarchar(50) NOT NULL,
Description NVARCHAR(50)
)
I have noticed that using DbContext does not support updating a record not as ObjectContext does.
In my application I have a simple update method.
public void UpdateItem()
{
MyContext context = new MyContext();
Item item = new Item();
item.ItemId = 666;
context.Items.Attach(item);
// From this point onward EF tracks the changes I make to Item
Item.Description = "Some description";
context.SaveChanges();
}
Using ObjectContext this method correctly updates a record. Using SQL profiler I can see that it generates something like this (greatly simplified!!!)
UPDATE Item
SET Description = 'Some description'
WHERE ItemId = 666
If, however I try to do the same thing in DbContext I get the exception:
System.Exception: Items.aspx.cs - logged from CustomError() ---> System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
And no database UPDATE is issued to Sql server.
I guess that DbContext validates all the properties and the property Name is null. This by design. I do not intend to modify it, I do not even know what is it and I do not need to know what is it.
Only the property Description was changed. Clearly ObjectContext does not track changes correctly.
How can this problem be resolved?
I have researched the issue and found the something on updating records.
For example this link: https://stackoverflow.com/a/15339512/4601078
db.Users.Attach(updatedUser);
var entry = db.Entry(updatedUser);
entry.Property(e => e.Email).IsModified = true;
// other changed properties
db.SaveChanges();
But this is horrible code. For every property on should add a line like:
entry.Property(e => e.Email).IsModified = true;
This produces ugly unreadable code, an I suspect lamda expression are not stelar in performance.
Even worse are those who propose to make a roundtrip to DB to fetch existing records with all properties populated, update it and the save changes. This is a no go with regard to performance.
So, how to tackle with simple entity updates or is this DbContext just another item in microsofts collection of dead ends which serve no real purpose?
DbContext doesn't really track changes by watching properties, it compares the values to previously known values. And validation always works on the entire entity so the only real choice if you want to do things this way is to disable validation during this operation. See Entity Framework validation with partial updates
If you know for sure that the changes you apply are valid, or you have custom code to validate them, you can turn off validation by EF altogether:
db.Configuration.ValidateOnSaveEnabled = false;
This works OK as long as you do it your way: attach a new entity with a known Id (aka a stub entity) and then modify its properties. EF will only update the properties it detects as having been modified (indeed by comparing original and current values, not, as ObjectContext did, by change notifications). You shouldn't mark the entity itself as modified.
If you don't want to turn off EF's validation, but neither want to mark individual properties as modified, I think this could be a useful alternative (brought to my attention by Alex's answer).
As I've mentioned in a couple other questions, I'm currently trying to replace a home-grown ORM with the Entity Framework, now that our database can support it.
Currently, we have certain objects set up such that they are mapped to a table in our internal database and a table in the database that runs our website (which is not even in the same state, let alone on the same server). So, for example:
Part p = new Part(12345);
p.Name = "Renamed part";
p.Update();
will update both the internal and the web databases simultaneously to reflect that the part with ID 12345 is now named "Renamed part". This logic only needs to go one direction (internal -> web) for the time being. We access the web database through a LINQ-to-SQL DBML and its objects.
I think my question has two parts, although it's possible I'm not asking the right question in the first place.
Is there any kind of "OnUpdate()" event/method that I can use to trigger validation of "Should this be pushed to the web?" and then do the pushing? If there isn't anything by default, is there any other way I can insert logic between .SaveChanges() and when it hits the database?
Is there any way that I can specify for each object which DBML object it maps to, and for each EF auto-generated property which property on the L2S object to map to? The names often match up, but not always so I can't rely on that. Alternatively, can I modify the L2S objects in a generic way so that they can populate themselves from the EF object?
Sounds like a job for Sql Server replication.
You don't need to inter-connect the two together as it seems you're saying with question 2.
Just have the two separate databases with their own EF or L2S models and abstract them away using repositories with domain objects.
This is the solution I ended up going with. Note that the implementation of IAdvantageWebTable is inherited from the existing base class, so nothing special needed to be done for EF-based classes, once the T4 template was modified to inherit correctly.
public partial class EntityContext
{
public override int SaveChanges(System.Data.Objects.SaveOptions options)
{
var modified = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified | EntityState.Added); // Get the list of things to update
var result = base.SaveChanges(options); // Call the base SaveChanges, which clears that list.
using (var context = new WebDataContext()) // This is the second database context.
{
foreach (var obj in modified)
{
var table = obj.Entity as IAdvantageWebTable;
if (table != null)
{
table.UpdateWeb(context); // This is IAdvantageWebTable.UpdateWeb(), which calls all the existing logic I've had in place for years.
}
}
context.SubmitChanges();
}
return result;
}
}
I'm struggling with Entity Framework code first and merging.
I have an MVC controller with a generic repository. A view model gets posted up and I convert that into the type that EF knows about
var converted = AutoMapper.Mapper.Map<RoutineViewModel, Routine>(result);
_routineRepository.Update(converted);
In the repository I have:
/*
Routines.Attach(item);
ChangeTracker.Entries<Routine>().Single(x => x.Entity.Id == item.Id).State = EntityState.Modified;*/
var match = Routines.Single(x => x.Id == item.Id);
var entity = Entry(match);
entity.CurrentValues.SetValues(item);
I commented out the first bit because it was throwing an error about already tracking the entity even though a check like this:
if (ChangeTracker.Entries<Routine>().Count(x => x.Entity.Id == item.Id) != 0)
returned false
The problem I'm having is that the Routine object has an ICollection property of Steps. When I set the values of the tracked entity to match that of the poco the ICollection changes aren't propagated down. Looking around this site there looks to be a few nasty looking recursive calls. Is this really how it works or am I missing something?
Is there any easy way to say, here is the source object (untracked), copy everything about it into the tracked object?
Just to be clear I don't think that getting the object first and updating the properties on that should be done outside of the repository. That seems to not only force you to pass your data models across domain boundaries but seems like instead of an equivalent SQL like statement being (update x,y where id = 1), to (insert into temp table where id = 1, for reach row in temp table, update x..... now for each row in table update table x = tempx where id = 1)
Edit --
So the problem is with the setValues not being a recursive call. The routine object has 2 simple properties (id and name) and one complex (ICollection ). If the item coming in has the name changed and some steps changed, setValues picks up the name change but doesn't apply to the children. Is there some other way to do this? It seems a little creaky to me that I have to hand roll this functionality
From what i can tell you are creating your entity, populating properties and then attaching it to the DB. This is kinda the wrong way round with EF.
If you want to attach an object which is already in the DB but isnt being tracked, you can use attach but only changes made after the attach call are recorded to be committed to the DB. If you want to use attach make sure you make your changes after calling that method.
In addition EF only allows you to attach an object which is not currently in the object graph. So if you try to attach the same object twice (or one with the same key) you will be given an error such as the one you are seeing.
Having two models, Site and Link, where a site has many links, how do I delete a link from inside a method of Site, which doesn't have access to the object context?
I've tried something like:
public void DeleteFirstLink() {
var link = LinkSet.First();
LinkSet.Remove(link);
}
but it seems that is not really deleting the link, but breaking the association. Since there's a database constraints it throws this error:
A relationship is being added or deleted from an AssociationSet 'Sites_have_Links'. With cardinality constraints, a corresponding 'Links' must also be added or deleted.
How do I actually delete the link from the database?
Assuming that your ObjectContext is not alive when you call the DeleteFirstLink() method, you can make it work by spinning up a context inside the method, attaching the Site entity, and then deleting the link:
public void DeleteFirstLink()
{
using (ProjectEntities db = new ProjectEntities())
{
db.AttachTo("[your site EntitySet name - SiteSet?]", this);
var link = LinkSet.First();
db.DeleteObject(link);
LinkSet.Remove(link);
db.SaveChanges();
}
}
You can't delete anything from the database without an object context. All actions are queued in the state manager of the object context, and those are persisted to the database when you call ObjectContext.SaveChanges(). Without SaveChanges, no DB changes.
First of all, it would be great if you could post a bit more information about your class structures. Does the Site class have an ObjectContext object? Then as a quick solution you could pass it into the delete method and use the context.DeleteObject() method, and call SaveChanges afterwards.
However, as a long-term solution, I will still recommend using the UnitOfWork pattern and I will post the link to the article explaining it again. The implementation might be different, but in general it might solve most of your problems (similar to this one).
The beauty of this approach is that if you use it correctly, you can build a small framework, that you can later reuse in all of your EF projects.
To work with entities so the modifications are reflected in the database you MUST ADD/ATTACH these entities in object context (in terms of EF5 in database context) and then use method SaveChanges to commit changes.
Yes, in EF4 to remove a record from phisical SQL table (not a link) you need to use method DeleteObject of object ObjectContext and then SaveChanges, i.e.
using(ObjectContext context = new ObjectContext)
{
/* Find the removed record in object/database context (this will attaches
* the record to object/database context)
* It is recommened to use FirstOrDefault() instead of First()
* becase this method can return null if there is no record to delete
* instead generation of exception in case of using First()
*/
Link linkToDelete = context.Links.FirstOrDefault();
if(linkToDelete != null)
{
context.DeleteObject(linkToDelete);
context.SaveChanges();
}
}
Fortunately now there is EF5 that allows to remove from parent collection but only if relation is one-to-many.
using(DatabaseContext context = new DatabaseContext)
{
Link linkToDelete = context.Links.FirstOrDefault();
if(linkToDelete != null)
{
context.Links.Remove(linkToDelete);
context.SaveChanges();
}
}
In any case DO NOT forget to call SaveChanges!