EF4 entity set: how to get the current count from TPH objects? - c#

I have this very simple TPH Model.
When I add an object using context.TIImport.AddObject(tiObj)
and then call context.TIImport.Count() - the value doesn't include the new object?
I want to be able to add multiple objects before hitting context.SaveChanges()
var context = new CodeFirstContainer();
var g = new TIGuarantee
{
Id = 1,
AccountNumber = "123",
Amount = 123
};
context.TIImports.AddObject(g);
var il = new TIImportLoan
{
Id = 2,
AccountNumber = "123",
Amount = 123
};
context.TIImports.AddObject(il);
var i = context.TIImports.Count(); // = 0
context.SaveChanges();
var j = context.TIImports.Count(); // = 2
}
Is there any way to tell how many objects are in the collection WITHOUT calling SaveChanges?

You can do this by going into the ObjectContext.ObjectStateManager and check all entities with EntityState as Added to the right Collection and include that with your count. Why you want to do this however is not clear. When you perform a read, these entities wont be included untill you actually persist them to the underlying datastore with SaveChanges(); If your aim is not to make the changes final, maybe you should look into transactions then?

Related

Best practice to update large entities in EF Core update

I have a table with around 50 columns.
So with EF Core that means every columns is a property.
Now the user have an edit button on the UI to edit around 40 of the 50 properties.
I need to update the entity like this:
oldEntity.Property1 = newEntity.Property1;
oldEntity.Property2 = newEntity.Property2;
Of course the properties have correct names and sometimes are inhertited etc.
What is the best way to do it without writing all properties down?
// if greaterThan 4 records then below code will perform bulk update.
public async Task PerfromOperations(IEnumerable<Employees> emps){
foreach(var emp in emps){
_dbcontext.Employee.Update(emp);
}
_dbcontext.SaveChangesAsync();
}
According to the comment of Ghassen, you can attach the newEntity with the Key of the oldEntity in a new DbContext
// entites
SampleObject oldEntity = new SampleObject() { Property1 = "oldEntity", Property2 = "oldEntity2" };
SampleObject newEntity = new SampleObject() { Property1 = "new Value", Property2 = "new Value 2" };
// save oldEntity
using var db = new ApplicationContext();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Add(oldEntity);
db.SaveChanges();
// retrieve the ID of the oldEntity
var oldEntityId = db.SampleObject.Where(x => x.Property1 == "oldEntity").Select(x => x.Id).FirstOrDefault();
// use new DbContext(!) and overwrite oldEntity with newEntity
using var db2 = new ApplicationContext();
newEntity.Id = oldEntityId;
db2.Update(newEntity);
db2.SaveChanges();
Maybe not elegant, but might be good enough.

How can I edit or add to a particular field without pull the all object

How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}

How to work around NotMapped properties in queries?

I have method that looks like this:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = db.Organizations.Select(org => new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
SiteCount = org.Sites.Count(),
DbSecureFileCount = 0,
DbFileCount = 0
});
return results;
}
This is returns results pretty promptly.
However, you'll notice the OrganizationViewModel has to properties which are getting set with "0". There are properties in the Organization model which I added via a partial class and decorated with [NotMapped]: UnsecureFileCount and SecureFileCount.
If I change those 0s to something useful...
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount
... I get the "Only initializers, entity members, and entity navigation properties are supported" exception. I find this a little confusing because I don't feel I'm asking the database about them, I'm only setting properties of the view model.
However, since EF isn't listening to my argument I tried a different approach:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = new List<OrganizationViewModel>();
foreach (var org in db.Organizations)
{
results.Add(new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
});
}
return results;
}
Technically this gives me the correct results without an exception but it takes forever. (By "forever" I mean more than 60 seconds whereas the first version delivers results in under a second.)
Is there a way to optimize the second approach? Or is there a way to get the first approach to work?
Another option would be to load the values back as an anonymous type and the loop through those to load your viewmodel (n+1 is most likely the reason for the slowness).
For example:
var results = db.Organizations.Select(org => new
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
}).ToList();
var viewmodels = results.Select( x=> new OrganizationViewModel
{
Id = x.Id,
Name = x.Name,
DbSecureFileCount = x.DbSecureFileCount,
DbFileCount = x.DbFileCount,
SiteCount = x.SiteCount
});
Sorry about the formatting; I'm typing on a phone.
You are basically lazy loading each object at each iteration of the loop, causing n+1 queries.
What you should do is bring in the entire collection into memory, and use it from there.
Sample code:
var organizationList = db.Organizations.Load();
foreach (var org in organizationList.Local)
{
//Here you are free to do whatever you want
}

Editing an object in a list<objects> without creating a new instance of that object?

I'm making a database in EF4.1 Code First. I have a table, MedicalPlan, with a one-to-many relationship to a CoverageLevel. CoverageLevel primary key is incrementing. When I create the MedicalPlan I declare the coveragelevels and it creates those tables, like so:
medicalPlan.CoverageLevels = new List<CoverageLevel>();
medicalPlan.CoverageLevels.Add(new CoverageLevel() { Cost = 1200, Description = "single" });
medicalPlan.CoverageLevels.Add(new CoverageLevel() { Cost = 1500, Description = "spouse" });
medicalPlan.CoverageLevels.Add(new CoverageLevel() { Cost = 1100, Description = "family" });
I also have an update function in which I would update a medical plan. I would also like the functionality to update the MedicalPlan's CoverageLevels. In pseudocode, something like:
in medicalPlan edit first item in CoverageLevels() { Cost = 1500 };
The kicker is that I don't actually want to replace the CoverageLevel, as it has a unique auto-incrementing primary key, so if I create a new one it will have a different primary key than the original. Is there a way to do this in the way that I am attempting?
You can mutate entities or related entities in an EF context, and saving changes should properly handle all the updates for you without needing to create new entries:
// get EF Context
var firstCoverage = myMedicalPlan.CoverageLevels.FirstOrDefault();
if (firstCoverage != null) firstCoverage.Cost = 1500;
// save changes
Once you have loaded a MedicalPlan from the database you should be able to work with it and any related entities using Linq or accessing directly via the property on MedicalPlan as if it were a normal collection of .NET objects.
Calling Save would persist back to the database.
E.g.
var medicalPlan = GetMedicalPlanFromDataContext(); //example method
medicalPlan.CoverageLevels.First().Cost = 1500;
//OR
medicalPlan.CoverageLevels[0].Cost = 1500
medicalPlan.Save();
Or probably more likely..
var medicalPlan = GetMedicalPlanFromDataContext();
var coverage = medicalPlan.CoverageLevels.Where(x=>x.Description == "family").Single();
coverage.Cost = 1500;
medicalPlan.Save();

Entity Framework - Avoiding child duplicates if they are not persited yet

Actually I use DbContext, but I just tested it with ObjectContext as well.
// Engine 1
Engine engine = new Engine();
Manufacturer manufacturer = new Manufacturer();
engine.Manufacturer = manufacturer;
// Engine 2
engine = new Engine();
engine.Manufacturer = manufacturer // Engine 2 has the same manufacturer like Engine 1
context.SaveChanges();
I use identity columns (int), where new IDs are generated. In debug mode I see that the ID for engine is "0". Well, if I implement context.SaveChanges right after the Engine 1 block, the new manufacturer is being saved to DB. With EntityKey or Any check, I can reference the new manufacturer to Engine 2 without any problems. But without immediate SaveChanges(), two entries of same manufacturer are being saved to DB (the code above). Is EF unable to reference internally like normal objects? As you see above, manufacturer is one and the same object, so I wonder if it's possible to obtain a successfull insert without pre-save the child/manufacturer.
EDIT: I think I found the problem
MachineEntities context = new MachineEntities();
context.Configuration.AutoDetectChangesEnabled = true;
// Engine 1
Engine engine1 = new Engine();
engine1.Name = "engine1";
Manufacturer manufacturer = new Manufacturer();
manufacturer.Name = "manufacturer1";
engine1.Manufacturer = manufacturer;
// Engine 2
Engine engine2 = new Engine();
engine2.Name = "engine2";
manufacturer = new Manufacturer();
manufacturer.Name = "manufacturer1";
engine2.Manufacturer = manufacturer;
// Add Engine 1
if (context.Manufacturers.Any(m => m.Name == engine1.Manufacturer.Name))
{
// The manufacturer's name is identical, so use the one in the context instead the assigned one.
engine1.Manufacturer = context.Manufacturers.Single(m => m.Name == engine1.Manufacturer.Name);
}
else
{
// The manufacturer is not known, add it to the context
context.Set<Manufacturer>().Add(engine1.Manufacturer);
}
// Add Engine 2
if (context.Manufacturers.Any(m => m.Name == engine1.Manufacturer.Name))
{
// The manufacturer's name is identical, so use the one in the context instead the assigned one.
engine2.Manufacturer = context.Manufacturers.Single(m => m.Name == engine2.Manufacturer.Name);
}
else
{
context.Manufacturers.Add(engine2.Manufacturer);
}
context.SaveChanges();
context.Dispose();
"Any" or any comparison won't give me any results. It only gives me those entities, which are already persisted in DB, but not the fresh new added ones. So it's duplicated. The local ones are ignored as I see in debugger, and the one in "Result View" are the one, where the command is executed against. So the new added entities are located in "Manufacturers.Local".
I just tried the following:
var a1 = new Activity{WorkflowId = 1, Title = "test"};
var a2 = new Activity{WorkflowId = 1, Title = "test2"};
var d = new WorkflowDisplay{WorkflowId = 1, Title = "Test"};
a1.WorkflowDisplay = d;
a2.WorkflowDisplay = d;
// Any of the next three lines can be commented out, but if
// they are all commented, nothing happens.
AddToWorkflowDisplays(d);
AddToActivities(a1);
AddToActivities(a2);
SaveChanges();
... and I only saw a single WorkflowDisplay added. So I'm pretty sure this has something to do with your specific implementation. Have you overridden the GetHashCode or Equals methods of any of your entities, or done any similar sort of customization of the auto-generated code?

Categories

Resources