I want to save a single object to the database twice. I have this code:
using (var ss = NHibHelp.OpenSession())
using (var tt = ss.BeginTransaction())
{
var entity = new Entity();
ss.Save(entity);
ss.Save(entity);
tt.Commit();
}
But this results in only one row being added to the database. How do I insert a single object into the database twice (with two different Ids) ?
You shouldn't do this - NHibernate maintains "object identity" within it's session, so it will not differentiate between ..well.. the same object. I would really advise against this, and a better solution would be to look at a way of cloning the object (either via reflection, or a Clone method), and then saving the cloned object.
If you want to ignore my advice above, you can get it to work by evicting the entity from the session, setting it's id back to it's unsaved value (depends on your mapping, but probably 0), and then saving it again.
It might also work if you just called session.Merge(entity) twice (you probably have to reset the id to it's unsaved value after the first call).
Alternatively you could use a stateless session with session.Merge() and then you don't have to evict the entity between Save's.
You can do it in two ways:
Clone the entity, should be a deep copy.
using (var ss = NHibHelp.OpenSession())
using (var tt = ss.BeginTransaction())
{
var entity = new Entity();
var clonedEntity = entity.Clone();
ss.Save(entity);
ss.Save(clonedEntity);
tt.Commit();
}
If your ID is assigned, remember to create a new ID. Deep copy have some issues with complex entities, if you have inverted collection you need to re-reference them.
2.Open a second transaction in a new session and commit it.
var entity = new Entity();
using (var ss = NHibHelp.OpenSession())
using (var tt = ss.BeginTransaction())
{
ss.Save(entity);
tt.Commit();
}
using (var ss = NHibHelp.OpenSession())
using (var tt = ss.BeginTransaction())
{
ss.Save(entity);
tt.Commit();
}
Related
I am trying to update all rows in IQueryable, that I retrieve from a database.
Is this the correct way to conduct this? When I run an Xunit test on this, the rows seem disappear.
foreach (var item in productCustomer) // productCustomer is IQueryable
{
item.isActiveStatus = (item.ExpirationYear < 2019);
}
_dbContext.SaveChanges();
Currently using EF Core 3.1
Note: Trying to refrain from creating totally new object or remapping everything; just want to update 1 member out of the 20 members.
You need to first get the objects, before they can be tracked for changes. However, if all you're doing with the IQueryable is to update a member without touching anything else, it would be faster to just execute the UPDATE query.
Consider:
using(var context = new SampleContext())
{
var commandText = "UPDATE Categories SET IsActiveStatus = ExpirationYear < 2019 --WHERE <YOUR CONDITIONS>";
//var name = new SqlParameter("#variable", "value");
context.Database.ExecuteSqlCommand(commandText/*,<params SqlParameter[]>*/);
}
change
> foreach (var item in productCustomer)
to
foreach (var item in productCustomer.ToList())
IQueryable is nothing more that a select statement on the table that triggers a database read.
No object is stored in memory until you pull it into a List or whatever datatype makes the most sense for you.
I am new to Dynamics CRM development. I want to batch update certain fields in Entity using Batch update method in Dynamics CRM Online.
I am using below code for performing batch update:
var multipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = false,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var entity in entities.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
multipleRequest.Requests.Add(updateRequest);
}
ExecuteMultipleResponse multipleResponse = (ExecuteMultipleResponse)service.Execute(multipleRequest);
How can I specify only fields which I want to update instead of entire entity being updated?
Note: I have around 200,000 records to update using the above code. Currently it takes around 1.5 minute to update a single batch of 1000 records. So was thinking a way to update only required fields.
My recommended approach is to create a new Entity() object for the update. This way your update code doesn't need to worry about what fields were retrieved, it just takes the ones it cares about updating.
foreach (var entity in entities.Entities)
{
var newEntity = new Entity(entity.LogicalName, entity.Id);
//Populate whatever fields you want (this is just an example)
newEntity["new_somefield"] = entity.GetAttributeValue<string>("new_somefield").ToUpper();
UpdateRequest updateRequest = new UpdateRequest { Target = newEntity };
multipleRequest.Requests.Add(updateRequest);
}
You have to look at the way how the EntityCollection entities is filled up. If retrieving using RetrieveMultiple, then Pull the minimal fields may be the native Name field & PK Id field will come by default. This way not the whole entity will be updated back.
Avoid using AllColumns = true. Use ColumnSet to get minimal fields needed for validation.
ColumnSet = new ColumnSet("field_needed"),
Next, assign only the necessary fields like below inside loop.
foreach (var entity in entities.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
entity.Attributes["field_to_update"] = "field_value";
multipleRequest.Requests.Add(updateRequest);
}
My answer will help you to understand what went wrong & correcting it. Like Nicknow said, you can assign fresh entity to solve issue.
So if want to update a row in the db using the values in a received object with the same PK, how would we do that. The reason this is needed is because the objects are quite extensive and this would I create another place where we have to change field names if we update the database. So my question is how does one go about assigning an object to an object retrieved from the database using LINQ? I will show what i'm talking about to clarify.
//Foo Object Received up here, called RecFoo
using (var newContext = new FooDataContext())
{
var obj = newContext.T_Foo.Single(x=>x.Id == RecFoo.id);
//Set obj = recFoo (Directly)
newContext.SubmitChanges();
}
I know we can set each individual attribute (obj.Name = RecFoo.Name...), but is there any way that we can just take the received object using the PK id and assign it all the values that are inside of RecFoo?
EDIT: Solution
using (var newContext = new FooDataContext())
{
//var obj = newContext.T_Foo.Single(x=>x.Id == RecFoo.id);
//Set obj = recFoo (Directly)
newContext.T_Foo.Attach(recFoo);
newContext.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, recFoo);
newContext.SubmitChanges();
}
Have you tried newContext.YourEntity.Attach(YourAlreadyPopulatedObject)
In this you just tell Linqtosql that the record already exists in the database and you are updating it.
I am long time reader - first time poster - and after countless hours of research on this Entity Framework issue, I felt like I needed some help. I'm new to C# and new to programming, so please bear with me.
I have a fairly simple many-to-many relationship in my data model with Web 2.0 style "Tags". Shifts have tags and users have tags.
I am pulling a shift from a cache. When I try to make a copy of a shift, copying over the shift details and tags from the cached copy, I do the following (simplified).
Data.Shift s = new Data.Shift();
/* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */
foreach (var t in shift.Tags) { //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
s.Tags.Add(dt);
}
Even though I have explicitly set the TagID in the new Data.Tag object, on SaveChanges() a new tag is inserted into the DB, rather than just creating a relationship on the Tag that already exists in the DB. TagID is a PK identity column
When I try the following code:
foreach (var t in shift.Tags){
s.Tags.Add(t)
}
It obviously fails because the shift was cached from a different object context than the current requests' context.
Any thoughts? As far as I can tell:
Clearing the cache is not an option for me because of performance concerns -- I recognize that this whole issue would go away if I did this work within the same object context.
Also, pulling the Data.Tag from the DB in the current context is not an option because of perf concerns.
This seems like a really easy thing I am trying to do...
Edit
Ok -- I've tried updating my code with both solutions but I am running into trouble with both. Let's try the first one:
// ctx is grabbed up here from HttpContext.Current.Items
Data.Shift s = new Data.Shift();
/* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */
foreach (var t in shift.Tags) { //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
ObjectStateEntry entry;
ctx.ObjectStateManager.TryGetObjectStateEntry(t.EntityKey, out entry);
if (entry == null || entry.State == EntityState.Detached) {
ctx.Tags.Attach(t);
}
s.Tags.Add(dt);
}
This is throwing the following error:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
I am guessing that the reason I am getting is this error, is because the shift object and its tags are pulled out of a cache (obviously from different contexts). Thoughts?
It doesn't matter if you set TAgId to existing value or not. EF is stupid - it doesn't do any Upsert / Merge for you. It doesn't check if the same entity exists and if TagId is auto generated in the database it even throw away your value once you call SaveChanges.
What you have to do? You must manually tell EF that Tag is not a new entity.
Try either:
Data.Shift s = new Data.Shift();
context.Shifts.AddObject(s); // Add a new shift
foreach (var t in shift.Tags)
{ //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
context.Tags.Attach(Tag); // Attach an existing tag
s.Tags.Add(dt);
}
context.SaveChanges();
or
Data.Shift s = new Data.Shift();
foreach (var t in shift.Tags)
{ //shift object is a cached object
Data.Tag dt = new Data.Tag
{
TagID = t.TagID,
Name = t.Name,
OrgID = t.OrgID,
};
s.Tags.Add(dt);
}
context.Shifts.AddObject(s);
// Now shift and all tags are added
// Change the state of each tag to unchanged
foreach (var tag in s.Tags)
{
context.ObjectStateManager.ChangeEntityState(tag, EntityState.Unchanged);
}
context.SaveChanges();
I have a many-to-many relation Patients - PatientDevices - Devices and a basic edmx-model of it (no poco, automatic generation). PatientDevices is generated as an entity, because it has more columns than the foreign keys.
When I create two ObjectContexts and add a new PatientDevice into the first one, the second one has it also. When deleting this relation from the first one, it is still in the second context:
var entities1 = new TherapyDatabaseDevEntities();
var entities2 = new TherapyDatabaseDevEntities();
entities1.PatientDevices.AddObject(new PatientDevice
{
Patient = entities1.Patients.First(),
Device = entities1.Devices.First()
});
entities1.SaveChanges();
var relation1a = entities1.Patients.First().PatientDevices.ToList();
var relation2a = entities2.Patients.First().PatientDevices.ToList();
entities1.PatientDevices.DeleteObject(entities1.PatientDevices.ToList().Last());
entities1.SaveChanges();
var relation1b = entities1.Patients.First().PatientDevices.ToList();
var relation2b = entities2.Patients.First().PatientDevices.ToList();
relation1a and relation2a both have one entry. relation1b has no entry, but relation2b has one entry. Even if working with refreshes before the query:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients);
entities2.Refresh(RefreshMode.StoreWins, entities2.PatientDevices);
entities2.Refresh(RefreshMode.StoreWins, entities2.Devices);
var relation1b = entities1.Patients.First().PatientDevices.ToList();
// still 1 entry
var relation2b = entities2.Patients.First().PatientDevices.ToList();
Is there a possibility to bring the second context up to date or do I have to create another ObjectContext?
Edit
I found out that if I do this:
entities2.Refresh(RefreshMode.StoreWins, entities2.Patients.First().PatientDevices);
the relation gets updated properly. It's a pity that without the refresh entities2.PatientDevices does not contain the deleted object anymore, but entities2.Patients.First().PatientDevices still has it.
Is this intended behavior?
If you must have multiple contexts and work directly with your entities, have a look at the Attach and Detach methods, which as the names suggest are used to associate/dissociate an object from the context it was retrieved from. Note though that these methods only detatch the object you pass as an argument, not associated objects, so you'd probably have to walk through the connected objects detatching each one, which is messy.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
entities1.Detach(patient1);
//loop through associated entities calling Detach on each
var entities2 = new TherapyDatabaseDevEntities();
entities2.Attach(patient1);
//loop through associated entities calling Attach on each
My preference would be to use viewmodels so that you aren't editing the entities directly, but representations of them. When a user explicitly saves an object, retrieve that object and update the changes from that object only on a fresh context.
var entities1 = new TherapyDatabaseDevEntities();
var patient1 = entities1.Patients.Single(p => p.Id = 12345);
... dispose of your context, it's no longer needed, and make your changes here
var entities2 = new TherapyDatabaseDevEntities();
var patient2 = entities2.Patients.Single(p => p.Id = 12345);
patient2.Property1 = patient1.Property1;
... update with other changes (there's ways to make this code cleaner, just showing simplest example)
entities2.SaveChanges();
entities2.Dispose();
Some reference material on Attach/Detach here - http://msdn.microsoft.com/en-us/library/bb896271.aspx
Do a search on entity framework context lifetimes, there's a lot of discussion on this subject which might help you decide on a route that suits your needs.