I am using EF 4.5 and have to fetch some records and update a flag of the fetched record. Can I do this in same context?
using (var context = new MyDBEntities())
{
var qry = from a in context.Table1
select a;
foreach(var item in qry)
{
// Logic to fill custom entity (DTO) from qry
item.Fetched = 2; // Changing the status of fetched records in DB
context.Table1.Add(item);
}
context.SaveChanges();
}
You can. You have to, even. The context must have the objects you modify in its change tracker. That's what happens in the query (qry). Then you modify them, the change tracker detects changes when SaveChanges() is called and update statements are generated and send to the database.
The only thing is, you should not do
context.Table1.Add(item);
because that creates new Table1 records, it does not update the existing ones. Just remove the statement.
(By the way, there are other ways to make a context start change tracking, viz. attaching or adding objects to a context).
Related
Just wanted peoples opinions on a scenario:
Would it be more efficient to:
Select a record with change tracking turned off and then if it needs to be updated reattach the object to the context for update?
or -
Select a record with change tracking turned on just in case the record needs to be updated?
Or is this trivial?
My scenario is that we have a health check routine that does a select on a table every 10 seconds and very rarely needs to be updated (Only updates the record if a new version has been deployed). So should we do the health check with change tracking turned off turned on?
According to your use case, I think No-tracking queries will give big performance boost to your app.
So you can do that using AsNoTracking()
using (var context = new HelthContext())
{
var patients = context.Patients.AsNoTracking().ToList();
}
If you have an entity that you know already exists in the database but which is not currently being tracked by the context then you can tell the context to track the entity using the Attach method on DbSet as shown below.
var existingPatient = new Patient { Id = 1, Name = "Patient 1" };
using (var context = new HelthContext())
{
context.Patients.Attach(existingPatient );
// Do some more work...
context.SaveChanges();
}
Reference : Entity states and SaveChanges
It goes like this:
MyDbContext ctx = new MyDbContext();
IFooRepository repo = new FooRepository(ctx);
var items = repo.GetAvailableItem().ToList(); //this will query all item.sold = false.
// here it returns three rows
foreach(var item in items) {
item.sold = true;
}
repo.commit(); // this will call the SaveChanges() in DbContext
Thread.sleep(10000)
// Now I quickly execute a query in SQL Server Management Studio
// UPDATE Item SET Sold = 0;
var items02 = repo.GetAvailableItem().ToList(); // this will query all item.sold = false.
// here items02 also contains three rows
// HOWEVER, when I watch the value of item.sold in items02, it is all True
Is this the behavior by design?
Why? Is it because DbContext cache the entity and never refresh even if you run the same query again?
UPDATE
Here is the code in my repo:
public IQueryable<Item> GetAvailableItem()
{
var items = from x in DbContext.Item
where x.Sold == 0
select x;
return items;
}
public virtual int Commit()
{
return DbContext.SaveChanges();
}
OK. This is what happening:
Creating a new context.
Loading items from db by calling GetAvailableItem()
Context will load them, and also cache them.
Updating items via context. So: the db rows ARE updated, and the cached versions ARE updated too.
Updating items via pure sql, outside the context (through SSMS). So: the db rows ARE updated. But, since you are using the same context as before, and it has it's own version of items, and there is no way for it to know what's happening outside itself, so the cached version of items, stays how they were: ARE NOT updated.
If you want to your context know the changes outside itself, the easiest way is to create a new context and query again. Another way is to tell context explicity to re-load entities from db by yourContext.Entry<YourEntityType>(entityInstance).Reload();.
My guess is that your DbContext is not up to date with your changes that happened in the Database (you said that you are running an update during the Thread.sleep). DbContext won't pick up these updates (the data is cached).
That's why you want to have your lifescope of your context as short as possible to reduce concurrency. This is an expected behavior.
See this MSDN post
We're trying to set up a shadow copy system for auditing some of the tables in our projects database. For Any change (Add, Update, Delete) a copy of that record get's saved to it's shadow table (we're using the term Version).
If we have a table called Profiles with columns (int)ProfileId, (varchar(50))Name, (date)UpdateDate, etc... we would have another table called ProfilesVersion with columns (int)ProfileVersionId, (int)ProfileId, (varchar(50))Name, (date)UpdateDate, etc...
I'm working on the system to make the copies. In the past I have used triggers in the database to catch Insert, Update, Delete. But now we're trying to do it using Entity Framework and Linq.
I can override the SaveChanges on DbContext, and get a second copy into the Version table. However, the key Id that get's populated on the first table does not end up in the Version table.
With Entity Framework, you can have two inserts to the database with data from one entity getting applied to the second. For instance:
var res = new Resource{
SomeValue = someParameter
};
_db.Resource.Add(res);
var newProfile = new Profile{
ProfileValue = anotherParameter,
ForeignResourceId = res.ResourceId // ResourceId is autogenerated
};
_db.Profile.Add(newProfile);
_db.SaveChanges();
var forResourceId = newProfile.ForeignResourceId;
Since Profile.ForeignResourceId and Resource.ResourceId are mapped in the model, the newProfile object has the ForeignResourceId that was assigned by the database after SaveChanges(). Somehow entity framework knows to put res.ResourceId into ForeignResourceId once it has been generated from the database.
My code which dynamically copies values from one entity into the Version table does not do that. It simply copies data from the first entity into the new record for the Version entity, but doesn't setup the relationship to populate the key field with the foreign key.
public int SaveChanges(Guid userId)
{
// ... some other code
// entityEntry is DbEntityEntry, the entity with changes we want to replicate
// Create audit entity.
DbSet set = this.Set(auditTypeInfo.AuditEntityType);
IAuditEntity auditEntity = set.Create() as IAuditEntity;
set.Add(auditEntity);
// Copy the properties.
DbEntityEntry auditEntityEntry = this.Entry(auditEntity);
foreach (string propertyName in auditTypeInfo.AuditProperties)
{
// This copies just the raw value, if any
auditEntityEntry.Property(propertyName).CurrentValue = entityEntry.Property(propertyName).CurrentValue;
}
// ...
return base.SaveChanges();
}
So, following with our example, if we add a Profile record, it get's it's ProfileId, but the ProfileVersion record does not.
How in the above code can I have entity framework set that value in the 'auditentity' that we are copying to?
If I understood you case correctly, then:
This will have to do with properties for your entity. If you entity has property (which, I suppose, is a key for you entity) has DatabaseGeneratedOption.Identity (assigned at OnModelCreating), which translates to IDENTITY (1,1) at sql level, there's nothing you can do, because all of that is being handled at database, not ORM level.
What you could do in this case, use IDENTITY_INSERT, which would allow you to assign Ids, but, it means that you would also have to generate Ids manually.
In short - get rid of automatic identity generation.
I have an windows service which processes input from xml files. I need to insert new records and update the existing records every time I get a new file. I now need to implement insert\update history every time after an operation has occurred. I am required to maintain this in a separate table by displaying old value and new value. Is there any existing methodologies or techniques available for implementing this in easier way i.e something like comparing two objects and identifying modified fields. Please provide any suggestions. I am using Entityframework 5.0 and sql 2012.
There are multiple ways to do this.
Using interceptors of your persistence API framework. For eg JPA or Hibernate framework provides facade around your entity operations which runs after DML operations in database.
Event Listeners: You should be able to create event listeners inside your persistence framework which will be triggered and insert history data in your history tables after each DML operation.
Database triggers: This is indeed one of the most simplest way of maintaining history information for a given row/table.
Hope these pointers help
Anant
Specifically for EF, you can override DbContext.SaveChanges() and iterate over DbContext.ChangeTracker.Entries(). Each entry contains the current and original values for the properties of the entity.
My Entity class was derived from ObjectContext, which did not provide ChangeTracker for obtaining the modified values. However, for ObjectContext, we can obtain the modified values using DBContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified)
You can use EntityState.Deleted, EntityState.Added if required.
The below is the sample Implementation
Entities DBContext = new Entities();
var d = DBContext.StudentTableName.Where(x => x.stname == "Stock").FirstOrDefault();
if(d!= null)
{
d.Id = "345";
DBContext.StudentTableName.ApplyCurrentValues(d);
//Need to Include Audit Logging before save, or can override save function.
var entrList = DBContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var stateEntry in entrList)
{
var currentValues = stateEntry.CurrentValues;
var originalValues = stateEntry.OriginalValues;
var modifiedProperties = stateEntry.GetModifiedProperties();
foreach (string modifiedProperty in modifiedProperties)
{
var currentValue = currentValues.GetValue(currentValues.GetOrdinal(modifiedProperty));
var originalValue = originalValues.GetValue(originalValues.GetOrdinal(modifiedProperty));
if (!originalValue.Equals(currentValue))
{
//Perform the logging operation
}
}
}
// Audit Logging Performed
DBContext.SaveChanges();
}
I am trying to update a record using Entity Framework but want to avoid manually setting each property and then calling save changes. I have an instance of this class bound to a dataform that updates all its properties as a user changes them. The nicest way I can see using this would be to set the object in the context equal to the object bound to my dataform but this does not work. I tried removing the object from the context then adding the dataform bound object but it creates a completely new record (I expected this but decided it was worth a shot to try it out). I assume there is some type of flag with in the context that is used to detect if a record is a new row or not considering the fact that replacing the context object with the databound object (which has the same pk value) does not work.
using (var scope = new TransactionScope())
{
try
{
using (var context = new CIS_DEVEntities())
{
GangMemberBio bio = context.GangMemberBios.First(P => P.id == this.id);
bio = this; //this does not work.
context.SaveChanges();
}
//If the top code is successfully completed then the transaction will be commited
//if not this line will be skipped and execution will be given to the catch block
scope.Complete();
}
catch
{
}
}
Edit Idea 1
I was thinking I could create a context object on my wpf window itself and then bind the dataform to my gangmemberbio object retrieved from this context. Then when I call save changes nothing else needs to be done. I heard that having a datacontext in memory is bad though.. Or is it just bad if I use it outside the context of a using statement?
DbContext keeps track of each entity's state internally, so it knows whether to add/update/delete any given one. When you Add() an entity to the context, even though it might already have an id, it's default state is Added (might be New, not sure), which causes the existing id to be ignored and a new record inserted into the db.
What you want to do is attach the already existing entity to the DbContext, and then let the DbContext know that it's to be treated as an existing entity so that it's updated, rather than inserted.
You do that by setting the EntityState in the DbContext for the given entity to Modified (by default it's New when adding an entity to the DbContext).
Try something like this:
using (var context = new CIS_DEVEntities())
{
context.Entry(this).State = EntityState.Modified;
context.SaveChanges();
}
In your edit page, load the object from the database first which will cause your form's fields to all be populated with existing data from the database. When the form is posted then, all the values of your data object model will be set.