When I call SaveChanges, nothing saved back to the database? - c#

editableCustomers = _ctx.Customers.ProjectTo<EditableCustomer>().ToList();
editableCustomers.First().Name = "changed";
var customers = _ctx.Customers.ToList();
Mapper.Map(editableCustomers, customers);
_ctx.SaveChanges();
I use Automapper to perform the mapping between the EF Entities(Customer in this case) and the Editable Model(EditableCustomer).
Why SaveChanges does not Work ?

Normally you would use AutoMapper using:
var results = Mapper.Map<DestinationType>(source);
Or in your case
var results = Mapper.Map<Customer[]>(editableCustomers);
Also AutoMapper does not change the values in the context but merely converts objects from one form to another. It currently appears you are just converting objects and doing nothing with those objects afterwards. Also the context isn't aware of those changes.
Normally to save the changes I would rather use code like this:
var customer = _ctx.Customers.Single(c => c.Id == modifiedEditableCustomer.Id);
customer.Name = modifiedEditableCustomer.Name;
_ctx.SaveChanges();
Where modifiedEditableCustomer is one of your EditableCustomer (you could loop through them).
Here is another way to iterate over the Customers and save the changed objects. If the object has changed (you'll need to determine this) then you can re-attach the object and set it's state to modified.
foreach (var customer in customers)
{
if (HasCustomerBeenModified(customer))
{
_ctx.Customers.Attach(customer)
_ctx.Entry(customer).State = System.Data.Entity.EntityState.Modified;
}
_ctx.SaveChanges();
}

Instead of Automapper, you can use this way:
var editableCustomers = _ctx.Customers.ProjectTo<Customer>().ToList();
editableCustomers.First().Name="changed";
var customers = _ctx.Customers.ToList();
foreach (var customer in customers)
{
customer.Name = editableCustomers.First(x => x.CustomerId == customer.CustomerId).Name;
}
_ctx.SaveChanges();

Related

Returning Id's from a list in Entity Framework

I'm having an issue with Entity Framework, when I execute SaveChanges, the context has quite a few objects associated with it, some of the objects are updated and some are added, afterwards I want to use the Id's of all these items (the Id's for the added items are only assigned on insert in the database). After the save changes a list of all the objects is empty.
I've seen samples on the site where the object is updated after the save so I suspect it might be how I'm getting the list of objects in the first place
Here's my code:
// Lots of processing to create or update objects
using (var localContext = this.context)
{
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
// The list has 5 items in at this point
localContext.SaveChanges();
// This list is now empty
DoSomethingWithList(updatedEvents);
}
Thanks in advance for any help.
The variable updatedEvents is a Linq query. Linq queries aren't executed immediately. By the time it is executed in your code it won't find any updated object anymore. Putting .ToList() after the Linq query will execute it immediately.
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity).ToList();
first your "using" statement is odd.
it should be
using (var context = new MyContext()) //passing in optional connection string.
{
}
Then the way you access your entities seem odd or i have no clue what you are doing there...
var updatedObjects = localContext.ChangeTracker.Entries().Where(e => e.Entity is GenerationEvent && (e.State == EntityState.Modified || e.State == EntityState.Added));
var updatedEvents = updatedObjects.Select(e => (GenerationEvent)e.Entity);
Seems like you are asking the context for all items which are considered "Add" or "Updated"
Then you are accepting the changes to the context. eg SaveChanges().
I fully expect "updatedEvents" to be empty, after save-changes is called.
Change you stuff... to something like
using (var context = new MyContext()) //passing in optional connection string.
{
LIst<EntityType> listOfChangedEntities = //ToDo:either passed in or generated
context.EntityType.AddRandge(listOfChangedEntities);
context.SaveChanges();
//after SaveChanges has been done all the entities in the
//listOfChangedEntities will now have there id's
//for update, fetch the entities... change them and Update them
}
I suspect that you are trying to create some sort of generic code to handle any type of Entity without specifying its type. Your code is not suited for this as it is, if this is what you are trying to do, I would modify the question to ask what you are trying to achieve. But the above is the Normal way of getting the Id's of the entities which have been inserted.
The other examples you are passably talking about is where they use foreign keys and navigation properties to automatically associate related entities, but your code looks way off from that.
UPDATE
routine
public static DoWork()
{
var context = new MyContext();
List<GenerationEvent > internalEntityType = new List<GenerationEvent ();
foreach(var item in SomeList)
{
var newItemEntity = new GenerationEvent();
newItemEntity.Name = "Test";
context.GenerationEvent.Add(newItemEntity);
//add to internal list
internalEntityType.Add(newItemEntity )
}
context.SaveChanges();
var first_id = internalEntityType.FirstOrDefault().Id;
//first_id will not be 0 it will be the Id the database gave it.
}

add list of objects to Entity database

I am new to EF. I created entity models from database.
I have tables CurrencyMaster([FromCurrency],[ToCurrency],[ActiveStatus]) and CurrencyConversion([ID],[FromCurrency],[ToCurrency],[Date],[CurrencyFactor])
I am looping for the CurrencyMaster records and accordingly DownloadCurrencyRates will get me the List<CurrencyRate> objects.
I just want to add these objects to entity database.
I tried something like this
public DownloadStatus DownloadUpdateCurrency(DateTime toDate, DateTime fromDate)
{
CurrencyEntities db = new CurrencyEntities();
var curMasters = db.CurrencyMasters.Where(x => x.ActiveStatus == 0);
foreach (var item in curMasters)
{
var curcRatesList = DownloadCurrencyRates(fromDate, toDate,
item.FromCurrency, item.ToCurrency);
//I know this is a bad code
curcRatesList.Select(x =>
{
db.AddToCurrencyConversions(
new CurrencyEntity.CurrencyConversion {
Date = x.date,
CurrencyFactor = x.value,
FromCurrency = item.FromCurrency,
ToCurrency = item.ToCurrency
}
);
return true;
});
}
db.SaveChanges();
return DownloadStatus.DownloadSuccess;
}
How can I do the same in a proper way?
Is there any way I can do this without looping for curcRatesList?
I am using .NET 3.5, and not sure about EF version.. I didn't try executing code(I need some other setup for that), but I am quite sure that what I am doing is not correct.. So I am posting here..
The procedure is correct. There is no bulk insert capability in EF that would allow to add a whole list of entities in a single method call. You must loop over the items and add them one by one.
As a side note: I would just use an ordinary foreach loop instead of that strange Select trick (which misuses the Select method, but it will work). Or - if curcRatesList is of type List<T> - you can use the Foreach method of List<T> instead of Select.

EF 4.1 Insert/Update Logic Best Practices

I'm inserting a lot of data, wrapped in a transaction (like 2 million+ rows) at a time, using EF 4.1. Now I'd like to add UPDATE logic. Keep in mind, change-tracking is disabled given the volume of data. Off the top of my head, I'd do something like this:
// Obviously simplified code...
public void AddOrUpdate(Foo foo)
{
if(!db.Foos.Any(x => someEqualityTest(foo)))
{
db.Foos.Add(foo);
}
else
{
var f = db.Foos.First(x => someEqualityTest(foo));
f = foo;
}
db.SaveChanges();
}
Any ideas on how possibly to improve on this?
I would keep the inserts separate from the updates.
For inserts, I would recommend using SqlBulkCopy to insert all records which don't already exist and it's going to be way faster.
First, the Bulk Insert method in your DbContext:
public class YourDbContext : DbContext
{
public void BulkInsert<T>(string tableName, IList<T> list)
{
using (var bulkCopy = new SqlBulkCopy(base.Database.Connection))
{
bulkCopy.BatchSize = list.Count;
bulkCopy.DestinationTableName = tableName;
var table = new DataTable();
var props = TypeDescriptor.GetProperties(typeof(T))
// Dirty hack to make sure we only have system
// data types (i.e. filter out the
// relationships/collections)
.Cast<PropertyDescriptor>()
.Where(p => "System" == p.PropertyType.Namespace)
.ToArray();
foreach (var prop in props)
{
bulkCopy.ColumnMappings.Add(prop.Name, prop.Name);
var type = Nullable.GetUnderlyingType(prop.PropertyType)
?? prop.PropertyType;
table.Columns.Add(prop.Name, type);
}
var values = new object[props.Length];
foreach (var item in list)
{
for (var i = 0; i < values.Length; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
bulkCopy.WriteToServer(table);
}
}
}
Then, for your insert/update:
public void AddOrUpdate(IList<Foo> foos)
{
var foosToUpdate = db.Foos.Where(x => foos.Contains(x)).ToList();
var foosToInsert = foos.Except(foosToUpdate).ToList();
foreach (var foo in foosToUpdate)
{
var f = db.Foos.First(x => someEqualityTest(x));
// update the existing foo `f` with values from `foo`
}
// Insert the new Foos to the table named "Foos"
db.BulkInsert("Foos", foosToinsert);
db.SaveChanges();
}
Your update...
var f = db.Foos.First(x => someEqualityTest(foo));
f = foo;
...won't work because you are not changing the loaded and attached object f at all, you just overwrite the variable f with the detached object foo. The attached object is still in the context, but it has not been changed after loading and you don't have a variable anymore which points to it. SaveChanges will do nothing in this case.
The "standard options" you have are:
var f = db.Foos.First(x => someEqualityTest(foo));
db.Entry(f).State = EntityState.Modified;
or just
db.Entry(foo).State = EntityState.Modified;
// attaches as Modified, no need to load f
This marks ALL properties as modified - no matter if they really changed or not - and will send an UPDATE for each column to the database.
The second option which will only mark the really changed properties as modified and only send an UPDATE for the changed columns:
var f = db.Foos.First(x => someEqualityTest(foo));
db.Entry(f).CurrentValues.SetValues(foo);
Now, with 2 million objects to update you don't have a "standard" situation and it is possible that both options - especially the second which likely uses reflection internally to match property names of source and target object - are too slow.
The best option when it comes to performance of updates are Change Tracking Proxies. This would mean that you need to mark EVERY property in your entity class as virtual (not only the navigation properties, but also the scalar properties) and that you don't disable creation of change tracking proxies (it is enabled by default).
When you load your object f from the database EF will create then a dynamic proxy object (derived from your entity), similar to lazy loading proxies, which has code injected into every property setter to maintain a flag if the property has been changed or not.
The change tracking provided by proxies is much faster than the snapshot based change tracking (which happens in SaveChanges or DetectChanges).
I am not sure though if the two options mentioned above are faster if you use change tracking proxies. It is possible that you need manual property assignments to get the best performance:
var f = db.Foos.First(x => someEqualityTest(foo));
f.Property1 = foo.Property1;
f.Property2 = foo.Property2;
// ...
f.PropertyN = foo.PropertyN;
In my experience in a similar update situation with a few thousand of objects there is no real alternative to change tracking proxies regarding performance.

LINQ to Entities returns wrong ID for an entity's child's child

I have a table structure in MySql, set up with foreign keys:
Period { Id, Title }
Activity { Id, Title, PeriodId }
Resource { Id, FileName }
ActivityResources { ActivityId, ResourceId }
This is set up with LINQ-to-Entities and I have checked that the table mappings are correct incuding the many-to-many relationship. Normally all works well with the Period, Activity and Resource classes.
However, I am trying to use this code:
private string ListAttachments(Ctx ctx) {
var resList = new StringBuilder();
var p = ctx.Periods.Include("Activities.Resources").Single(o => o.Id == 1);
foreach (var a in p.Activities) foreach (var r in a.Resources)
resList.Append(r.Id).Append(" - ").Append(r.FileName);
return resList.ToString();
}
But it doesn't write the Resource.Id for each resource as expected; for some reason it writes the Activity.Id instead (though the FileName is correct).
Any idea what's going on?
EDIT
By the way, this works fine - but I'm still interested in the probem in the above code.
private string ListAttachments(Ctx ctx) {
var resList = new StringBuilder();
var res = ctx.Resources.Where(r => r.LessonActivities.Any(l => l.LessonId == 1)).ToList();
foreach (var r in res) resList.Append(r.Id).Append(" - ").Append(r.FileName);
return resList.ToString();
}
I don't think you can reference a child/child collections that way. The Activities navigation property on a Period object is a collection. And you can't reference a property that belongs to an instance of an object via a collection of those objects.
Put another way, Period p has an Activities collection. But a collection of type Activity does not have a Resources collection. Only a single Activity has a Resources collection.
What I don't understand is why the first snippet works at all.
Anyway, to get to the Resources collection directly from a Period, you would need to use SelectMany(). Something like
List<Resource> resources = ctx.Periods
.Where(p=>p.Id == 1)
.SelectMany(p=>p.Activities)
.SelectMany(a => a.Resources)
.ToList();

Entity Framework - Saving Changes to Related Objects in Detached State

I'm using the Northwind database as an example for this post where I am having a problem saving detached entities back to the database using the entity framework.
I have the following two methods to get me the territories and the regions:
static List<Region> GetRegions()
{
using (NorthwindEntities entities = new NorthwindEntities())
{
entities.Region.MergeOption = System.Data.Objects.MergeOption.NoTracking;
return entities.Region.ToList();
}
}
static List<Territories> GetTerritories()
{
using (NorthwindEntities entities = new NorthwindEntities())
{
entities.Territories.MergeOption = System.Data.Objects.MergeOption.NoTracking;
return entities.Territories.ToList();
}
}
These methods both work fine and get me the collection of objects I require in the detached state.
I also have a static method called SaveEntity, which takes in both an old entity and the currently editted entity, this is as follows:
static void SaveEntity(EntityObject oldEntity, EntityObject newEntity)
{
using (NorthwindEntities entities = new NorthwindEntities())
{
entities.Attach(oldEntity);
entities.ApplyPropertyChanges(newEntity.EntityKey.EntitySetName, newEntity);
entities.SaveChanges();
}
}
This method partially works where the changes to the object are saved down to the database, but any changes to the relationship of related objects are not saved.
I have the following code calling the above methods as my example:
List<Territories> territories = GetTerritories();
List<Region> regions = GetRegions();
Region region = regions.Where(n => n.RegionID == 2).FirstOrDefault();
Territories oldTerritory = territories.Where(n => n.TerritoryID == "01581").FirstOrDefault();
Territories newTerritory = ObjectCopier.Clone<Territories>(oldTerritory);
newTerritory.TerritoryDescription = "Hello World";
newTerritory.Region = region;
SaveEntity(oldTerritory, newTerritory);
The change to TerritoryDescription is successfully saved, but the change to Region is not, in the database it still remains as RegionID=1 instead of RegionID=2.
Can anyone provide me with some insight to why ApplyPropertyChanges doesn't propogate changes to related objects?
Also, does anyone know of how I can get around this problem?
Instead of fetching regions and territories seperately, fetch both of them in the same query. Something like (I assume you want to update the entity, do not want to create a new one);
static List<Region> GetTerritoriesWithRegions()
{
using (NorthwindEntities entities = new NorthwindEntities())
{
entities.Territories.MergeOption = System.Data.Objects.MergeOption.NoTracking;
return entities.Territories.Include("Region").ToList();
}
}
Then update them as the following;
List<Territories> territoriesWithRegions = GetTerritoriesWithRegions();
Territories territory = territories.Where(n => n.TerritoryID == "01581").FirstOrDefault();
territory.TerritoryDescription = "Hello World";
Region region = territories.Where(p => p.Any(q => q.Region.RegionID == 2)).FirstOrDefault().Region;
territory.Region = region;
SaveEntity(territory);
And save them;
static void SaveEntity(EntityObject entity)
{
using (NorthwindEntities entities = new NorthwindEntities())
{
entities.Attach(entity);
entities.Context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
entities.SaveChanges();
}
}
I have coded this on notepad so there may be mistakes; if any please comment so I will update accordingly.
I think you can find answers here (Alex James is better one).
Entity Framework Updating with Related Entity
Basically, the reason is because in EF relationships are objects too and have statuses like entities (deleted, added,...), so you would need to have the original reference value in the context as well.

Categories

Resources