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.
Related
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.
I need to get the list of changed fields only, the datastore is ssce so no triggers are available
Is there any support in EF to get a list or to build a generic component ?
Depending on the type of context and generated entities you can do it in several different ways.
In case of objects inherited from Entity or POCO you can use ObjectStateManager
in case of Self-Tracking entities you can use Tracker from entity itself.
please provide more details on the way how you generated context and how you make changes
EDITED(2):
you can query ObjectStateManager for changed entries simply like this:
var changed = ctx.ObjectStateManager.GetObjectStateEntries().Where(e=>e.State != EntityState.Unchanged);
EDITED(1):
The following example from MSDN demonstrate how to query for changes:
int orderId = 43680;
using (AdventureWorksEntities context =
new AdventureWorksEntities())
{
var order = (from o in context.SalesOrderHeaders
where o.SalesOrderID == orderId
select o).First();
// Get ObjectStateEntry from EntityKey.
ObjectStateEntry stateEntry =
context.ObjectStateManager
.GetObjectStateEntry(((IEntityWithKey)order).EntityKey);
//Get the current value of SalesOrderHeader.PurchaseOrderNumber.
CurrentValueRecord rec1 = stateEntry.CurrentValues;
string oldPurchaseOrderNumber =
(string)rec1.GetValue(rec1.GetOrdinal("PurchaseOrderNumber"));
//Change the value.
order.PurchaseOrderNumber = "12345";
string newPurchaseOrderNumber =
(string)rec1.GetValue(rec1.GetOrdinal("PurchaseOrderNumber"));
// Get the modified properties.
IEnumerable<string> modifiedFields = stateEntry.GetModifiedProperties();
foreach (string s in modifiedFields)
Console.WriteLine("Modified field name: {0}\n Old Value: {1}\n New Value: {2}",
s, oldPurchaseOrderNumber, newPurchaseOrderNumber);
// Get the Entity that is associated with this ObjectStateEntry.
SalesOrderHeader associatedEnity = (SalesOrderHeader)stateEntry.Entity;
Console.WriteLine("Associated Enity's ID: {0}", associatedEnity.SalesOrderID);
}
It's commonly a good practice to do this by using the database structure itself.
This is just another approach to the one you have now.
You can create a new field of type datetime in a table called for example ModifiedOn and update it every time that you update the row in the database.
Then when you want the changed rows after a specific time you just use:
where ModifiedOn > dateTime
It's just another suggestion on how you can approach the problem from a different angle.
I am very new To EntityFramework and having trouble to understand why first code doesn't update but second does. My aim is to update a record without querying db.
Is it too obvious?
using (CHATDBEntities db = new CHATDBEntities())
{
// update buddy
onlines buddy = new onlines();
buddy.id = 56;
buddy.last_seen = DateTime.Now;
buddy.status = (int)UserStatuses.Status.Chatting;
buddy.connected_to_id = 34;
buddy.room_id = 2;
db.SaveChanges();
}
using (CHATDBEntities db = new CHATDBEntities())
{
// update buddy
var buddy = db.onlines.First();
buddy.last_seen = DateTime.Now;
buddy.status = (int)UserStatuses.Status.Chatting;
buddy.connected_to_id = 34;
buddy.room_id = 2;
db.SaveChanges();
}
it looks like it is related to "id" which is primary identity key.
You have to retrieve an entity in order to update it, you can't just create a new one with the same id as a record from the database and expect EF to magically know that it needs to update the database. You could try attaching it to the ObjectContext but that is not how you are supposed to do updates.
Possibly you need to add your new buddy to onlines collection?
db.onlines.Add(buddy); // << this
db.SaveChanges();
The reason is because you need to select the object into the ObjectContect before you can update it. It looks like the only way to update the entity is to first read it.
Please see this other stackoverflow link which answers this.
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.
Code below does not run correctly and throws InvalidOperationExcepiton.
public void Foo()
{
DataContext context = new DataContext();
LinqEntity item = new LinqEntity(){ Id = 1, Name = "John", Surname = "Doe"} ;
context.LinqEntities.Attach(item, true);
}
By default, the entities will use all fields for checking concurrency when making edits. That's what's throwing the InvalidOperationException.
This can be setting the Update Check property for all fields to Never. This must be done on all fields to attach the entity as modified. If this is done, an additional call to context.SubmitChanges() will save the data.
Alternatively, if you know the original values, you can attach and then make the updates, but all values that are being checked must match the original values.
LinqEntity item = new LinqEntity(){ Id = 1, Name = "OldName", Surname = "OldSurname"};
context.LinqEntities.Attach(item);
item.Name = "John";
item.Surname = "Doe";
context.SubmitChanges();
I'm not sure what you mean by disconnected from the database.
It appears that you are trying to insert a new row into the LinqEntities table -- is that correct?
If that is the case you'll want to do
context.LinqEntities.InsertOnSubmit(item);
context.Submit();
OK, if you're trying to update a row with ID = 1, you'll do it like this:
DataContext context = new DataContext();
LinqEntity item = (from le in context.LinqEntities
where le.ID == 1
select le).Single();
item.Name = "John";
item.Surname = "Doe";
context.Submit();
You could also replace the Linq expression with a more concise lambda:
LinqEntity item = context.LinqEntities.Single(le => le.ID == 1);
The most important thing the DataContext does is track any changes you make, so that when you call the Submit method it will autogenerate the Insert statements for the things you've changed.
When using an ORM you typically select an object before updating it.
You can use DataContext.ExecuteCommand(...) to bypass the ORM if you do not want to do a select.