Dynamics CRM SDK : Batch update specific fields in entity - c#

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.

Related

EF - Assigning Id of related entites without re-looping

Please guide me, in my code. I iterate to create a model called DataSetAsset and DataSetColumn. This will be use for creating DataSetPoint. I have this code
using (var transaction = _context.Database.BeginTransaction())
{
while (csv.Read())
{
lineNumber++;
foreach (var column in columns)
{
var dataSetAsset = new DataSetAsset
{
...
};
_context.DataSetAssets.Add(dataSetAsset);
var dataSetColumn = new DataSetColumn
{
...
};
_context.DataSetColumns.Add(dataSetColumn);
var dataSetPoint = new DataSetPoint
{
DataSetColumn = dataSetColumn,
DataSetAsset = dataSetAsset
};
// detach dataSetPoint so it wont get insert on SaveChangesAsync
_context.Entry(dataSetPoint).State = EntityState.Detached;
dataSetPoints.Add(dataSetPoint);
}
}
// this will create the dataSetAsset and dataSetColumn
await _context.SaveChangesAsync(_http, cancellation);
// upload the dataSetPoints data
var uploader = new NpgsqlBulkUploader(_context);
await uploader.InsertAsync(dataSetPoints);
await transaction.CommitAsync().ConfigureAwait(false);
}
this code was just looping through all the items in the csv and creating a dataSetAsset and dataSetColumn which is required for creating dataSetPoint. dataSetPoint could be thousands of values thats why I move it out(or detached it) and use NpgsqlBulkUploader for me to improve its performance. But what happens is since the dataSetAsset and dataSetColumn is required in creating a dataSetPoint, it keeps throwing me an error of having the value for dataSetAsset .Id and dataSetColumn.Id being 0 and lead to Primary Key errors.
What I want to know is if there's a possibility that on SaveChanges, the id for dataSetAsset and dataSetColumn will be set automatically for dataSetPoint rather than going on another loop just to set it. Those ids where set after SaveChanges but its on the navigation property and not in the dataSetPoint property. NpgsqlBulkUploader does not work similar to EF which if you assign the related entities then the Id was considered as set.
UPDATE
added a screenshot above what I currently have after SaveChangesAsync call. as you can see the DataSetColumnId value was still 0, but its navigation property it already have the value. I want that Id value to be set in DataSetColumnId.

Updating Rows in IQueryable and Save Changes

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.

Cloning an entity in NHibernate?

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();
}

LINQ to SQL trouble

i have following trouble with LINQ to SQL entities:
// Context is DataContext that was auto genereted when i create my .dbml file
var cl = Context.Classes.ToArray();
var rm = Context.Rooms.ToArray();
List<DaySchedule> s = new List<DaySchedule>();
s.Add(new DaySchedule()
{
Class = cl[0],
DayOfWeek = 0,
Pair = 1,
Room = rm[0]
});
Context.SubmitChanges();
so, after "SubmitChanges" new DaySchedules will be saved to db. BUT i didn't call InsertOnSubmit function and i don't want to save this DaySchedule.
BTW,
if i will using following code:
s.Add(new Acceron.University.DBAccess.DaySchedule()
{
Class_id = cl[0].Class_ID,
DayOfWeek = 0,
Pair = 1,
Room_id = rm[0].Room_ID
});
It will not be auto saved to db.
Could you explain is it bug or feature and how i can solve it?
It is by design. Class and Room are context-aware entities, since they were queried against the context. Anytime a context-aware entity adds children, it queues up those changes automatically to the context and marks it as inserted. So you cannot add new entities without the auto-queuing feature. I'd highly recommend not calling save changes later on.

How to update a second ObjectContext with deletions made on first one

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.

Categories