update object created in another datacontext - c#

I need your help very much. I'd like to update object created in another datacontext;
Here is my code. Insert statement works well but I can't write code for update
var dataContext = new ReconNewDataContext();
if (Id == 0)
{
var item = this;
dataContext.RequestIO.InsertOnSubmit(item);
dataContext.SubmitChanges();
Id = item.Id;
}
else
{
var item = this;
//update object
}
I've read
Linq2SQL: Update object not created in datacontext
I've tried to use .Attach(this) .Attach(this,true) .Attach(this, oldObjectFromBase) but always I've an errors.
I know I can get object from database and manually transfer data from modified object, but there will be new fields. It means that I must always append these new fields in Save() method.
Is there any "beautiful" method to update object created in another datacontext?

Check out this article: http://omaralzabir.com/linq_to_sql__how_to_attach_object_to_a_different_data_context/
Also, in your example, for update, try doing:
var item = new ObjectBeingUpdated();
//copy over properties from old object to new object, make sure pk's match
//Attach this new object
Something similar worked for my coworker I believe.
UPDATE: Check this out for more info about attach: http://blogs.msdn.com/b/dinesh.kulkarni/archive/2007/10/08/attach-if-you-have-something-detached.aspx

You could use the following pattern:
Fetch existing object from repository
Use AutoMapper to copy properties
Save your object back.

Related

data context save changes not working in C# entity framwork

I am trying to execute below query
using (var dbcontext = new EVEntities())
{
var data_header = dbcontext.Cl.Where(x => x.PKey ==
header_key).FirstOrDefault();
if (data_header != null)
{
data_header.EstimatedCost = Math.Round(estimated_cost,2);
data_header.ClaimedCost = Math.Round(claimed_cost,2);
dbcontext.Entry<Cl>(data_header).State = System.Data.Entity.EntityState.Modified;
dbcontext.SaveChanges();
Writelog("Updated");
}
}
Here Writelog write in a text file and it is working always. But the field in Cl is not getting updated. In between the data is getting updated also.
Connection String
<connectionStrings><add name="EVEntities" connectionString="metadata=res://*/xxx_Entity_Model.csdl|res://*/xxx_Entity_Model.ssdl|res://*/xxx_Entity_Model.msl; provider=System.Data.SqlClient;provider connection string="data source=xxxxxx;initial catalog=xxxxx;persist security info=True;user id=xx;password=xxxxx;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient"/></connectionStrings>
Edit1
tried raw update also
dbcontext.Database.ExecuteSqlCommand(#"UPDATE dbo.Claims
SET EstimatedCost = #e_cost, ClaimedCost = #c_cost WHERE Pkey =
#p_key",
new SqlParameter("e_cost", Math.Round(estimated_cost, 2)),
new SqlParameter("c_cost", Math.Round(claimed_cost, 2)),
new SqlParameter("p_key", claim_header_key));
same outcome. It get updated in between. No error.
You didn't give us enough information to give you the solution. Therefore I give you a method to debug the problem.
Does the DbContext think that anything must be saved?
What SQL is sent to the database?
When you call SaveChanges, DbContext checks its ChangeTracker to see if anything must be updated. Consider to write some Debug code to detect whether there are changes.
Insert just before SaveChanges:
bool changesDetected = dbContext.ChangeTracker.HasChanges;
It might be that you need to call DetectChanges() first. I'm not sure.
If there are Changes, check if the item that you think that should be updated is changed:
IEnumerable <DbEntityEntry<Cl>> entries = dbContext.ChangeTracker.Entries<Cl>;
// We're expecting exactly one entry:
DbEntityEntry<Cl> myEntry = entries.SingleOrDefault();
Assert(myEntry != null);
If null, try to find out why it is not tracked. Was it tracked after you fetched it, before you changed it? Do you have somewhere tracking switched off? Write some other debug code where you fetch some other data. Is that tracked?
If not null, then apparently your Cl is tracked. It ought to be changed:
Assert(myEntry.State == EntityState.Modified);
If not modified, fetch the original values and the current values:
DbPropertyValues originalValues = myEntry.OriginalValues;
DbPropertyValues currentValues = myEntry.currentValues;
In your debugger, check them, or write some debug code to compare the original value with the current values. Are the changed values correct?
I'm not sure if entity framework will try to update objects that are unmodified and of which the original values are not equal to the current values. We'll find out to see what SQL is created when you do the SaveChanges.
It would be nice if your database can log all communications.
You can also log what entity framework sends to your database. For this, use property DbContext.Database.Log. For example:
dbContext.Database.Log = Console.Write;
dbContext.SaveChanges();
If you can't write to Console, write a method:
private List<string> SqlCommands {get;} = new List<string>();
void LogSqlCommands(string sqlCommand)
{
this.SqlCommands.Add(sqlCommand);
}
And in your method that following debug code:
using (var dbcontext = new EVEntities())
{
this.SqlCommands.Clear();
dbContext.Database.Log = this.LogSqlCommands;
var data_header = ... etc
dbContext.SaveChanges();
}
Put a breakpoint after SaveChanges and check the generated SQL.
Hope these debugging tips help you to find the cause of your problem

RavenDB is not tracking entity changes

I have the following code that opens a session with RavenDB, gets the relevant IDs, uses those ideas to load the entities, change them, and finally save them.
List<EventDescriptor> events;
using (var session = raven.OpenSession())
{
session.Store(aggregate);
session.SaveChanges();
events = (from descriptor in session.Query<EventDescriptor>() where descriptor.AggregateId == aggregate.Id select descriptor).ToList();
}
using (var session = raven.OpenSession())
{
foreach (var #event in events)
{
var e = session.Load<EventDescriptor>("EventDescriptors/" + #event.Id.ToString());
e.Saved = true;
}
session.SaveChanges();
}
The problem however is that the changes in the entities don't seem to be tracked, and I can't delete the entities either (gives me unknown entity error), even though the object is loaded. I already tried calling SaveChanges inside the loop, but that didn't help either. I looked at the Raven documentation but I don't see what I'm doing wrong here.
Yes, we can't track changes on structs, because every time that you change them, you create a new copy
The problem was that EventDescriptor was a struct, and not a class. Changing this solved the problem. I assume it's because a struct is a valuetype and not a referencetype.

Update detached objects in EF that already exist in object state manager

I'm hoping this will be an easy one but for the life of me, I cannot find the specific answer elsewhere on SO or any other site.
I have a basic repository/unit of work pattern with Entity Framework Code First. It all works smoothly except for certain cases of Update. THe problem is I have a set of Entity Framework model objects, all prefixed with "Db" which EF returns, but I then convert them to plain DataContract Model objects to pass to the Web layer to give separation of concerns. I have a basic conversion interface that just populates a WebModel object from the DataModel object, copying field by field verbatim.
So if you retrieve a DbUser object from EF with ID of 1, then convert to a User object, then convert that BACK to a DbUser object, you end up with a DbUser with ID of 1, but it is a DIFFERENT object to the one you started with, though they have the same primary key field, the actual CLR objects themselves are different.
The following works
User user;
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
}
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser);
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
repository.Update(user);
repository.save();
}
As they are using two different unit of works/contexts, so the second block has nothing in the ObjectStateManager to compare to and can just attach the detached object in the Update() methods
This, however does NOT work
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
user = repository.Get(1);
repository.save();
var modelUser = DataConverter.Convert(user);
modelUser.Name = "new name";
user = BusinessConverter.Convert(modelUser)
repository.Update(user);
repository.save();
}
NOTE: I know logically this doesn't make much sense to convert and just convert back but go with it, I've simplified the example greatly to make it easier to put into paper, in my actual code there is a reason for doing it that way.
I get the usual error "an object with the same key already exists in the objectstatemanager...". I'm assuming because the Get() loads the object into EF and then the update sees that the object is detached, then tries to attach it and it already exists.
My Update method in my repository is as below
public override bool UpdateItem(DbUser item)
{
if (Work.Context.Entry(item).State == EntityState.Detached)
Work.Context.Users.Attach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
I made this Extension method to the DbContext to ReAttach the Entity without problems try it out:
public static void ReAttach<T>(this DbContext context, T entity) where T : class
{
var objContext = ((IObjectContextAdapter) context).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
Object foundEntity;
var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
// Detach it here to prevent side-effects
if (exists)
{
objContext.Detach(foundEntity);
}
context.Set<T>().Attach(entity);
}
Then just update your method :
public override bool UpdateItem(DbUser item)
{
Work.Context.ReAttach(item);
Work.Context.Entry(item).State = EntityState.Modified;
return Work.Context.Entry(item).GetValidationResult().IsValid;
}
You might get a manged Entity, and again verbatim map the new DbUser's properties to the managed Object:
public override bool UpdateItem(DbUser item)
{
using (var work = new UnitOfWork())
{
var repository = new UserDataRepository(work);
DbUser managedUser = repository.Get(item.PK);
//foreach DbUser property map the item to managedUser
managedUser.field1 = item.field1;
[..]
repository.Update(managedUser);
repository.Save();
}
}
If you set your context to AsNoTracking() this will stop aspmvc tracking the changes to the entity in memory (which is what you want anyway on the web).
_dbContext.Products.AsNoTracking().Find(id);
I would recommend you read more about this at http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application
Yash

Json.Encode(Model) throws "The ObjectContext instance has been disposed" exception

I have a simple razor view:
<script>var fieldList = #Html.Raw(Json.Encode(Model));</script>
This line throws The ObjectContext instance has been disposed exception. If I remove it all works fine even when I use Model later in View.
foreach (SomeCustomObject pField in Model)
{
<div>
#pField.SomeProperty
</div>
}
Controller action
ActionResult SomeAction()
{
List<SomeCustomObject> tList = new List<SomeCustomObject>();
using(EFEntities db = new EFEntities())
{
tList = db.SomeCustomObject.ToList();
}
return View(tList);
}
I presume, that it could be because object has navigation properties that no longer work. Is it possible to tell Json.Encode to use only NON navigation preperties of object?
You can't operate on model obejct outside of using block, where it is loaded. Possibly, JSON.encode does deep reflection analysis of model object and so touches some context-dependent attributes.
So, in common case, you should convert model to JSON in controller/action method, inside using block, put it to string variable and then use this variable in template.
Simplest way that I came up with was:
Create view ViewSomeCustomObjects in database, what returns all fields of SomeCustomObjects table.
Update model from database and include new view.
Use entity what is maped to view instead of table, as this is only for data display.
I REALY hope that if I change SomeCustomObjects table in database, changes will cascade trough entire solution...
Disable lazy loading on the context first with context.Configuration.LazyLoadingEnabled = false; before you start pulling stuff out of your database.
using (var context = new SomeEntityContext())
{
context.Configuration.LazyLoadingEnabled = false; // This is the fixer.
return context.SomeEntitiesWithRelations.ToList();
}

Updating a row using entity framework

I'm trying to update a field in a table just after I have added a row in a different table. (The value is just to show that the row has been imported) I thought I was using the right code here, but the bool 'Imported' field isn't updated. Here is the relevant code:
using (DbContext db = new DbContext())
{
db.Details.Add(details);
db.SaveChanges();
newID = details.DetailsID;
AccessRepository rep = new AccessRepository();
AccessDetails detailUpdate = rep.GetByID(item.AccessDetailsTableID);
detailUpdate.Imported = true;
db.SaveChanges();
}
The first SaveChanges call works, as I'm trying to add a new row, but not the second one. It successfully retrieves the data back from the repository but just doesn't update the value.
Any ideas why it might not be working?
Thanks
I think this is because your AccessRepository is using a different data context (db) to the one in scope (in your posted code)
You could try having a SaveChanges method in your AccessRepository which does the same but on the correct data context.
However, the issue with calling two saves is that you loss the single transaction benefits. So if those two updates are to be related you really should only call the SaveChanges once.
I would create an Add method and a Save method in your AccessRepository and then use something like this...
AccessRepository rep = new AccessRepository();
rep.Add(details);
AccessDetails detailUpdate = rep.GetByID(item.AccessDetailsTableID);
detailUpdate.Imported = true;
rep.Save();//this calls SaveChanges on a single data context
hope that helps
Should you use newId in GetById() method?
using (DbContext db = new DbContext())
{
db.Details.Add(details);
db.SaveChanges();
newID = details.DetailsID;
AccessRepository rep = new AccessRepository();
AccessDetails detailUpdate = rep.GetByID(newID);
detailUpdate.Imported = true;
db.SaveChanges();
}

Categories

Resources