I have :
var c = cboCustomer.SelectedItem as Customer;
var t = cboTrailer.SelectedItem as Trailer;
using (var db = new CAPSContainer())
{
db.Attach(c); --> Tracker has now 1 entity
db.Attach(t); --> Tracker has now 2 entities
c.Trailers.Remove(t); --> Tracker has now 29! entities loaded
db.DeleteObject(t);
db.SaveChanges();
}
I am trying to understand how this loading / caching is working because I am having some other issues related to it, any ideas why the cached amount suddenly jumps?
I am using EF 5.0.
Your default configuration probably had LazyLoadingEnabled set. So child lists won't get loaded until they are accessed, see this article for a more detailed explanation:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
By the way, if you just want to remove one without loading the lot, then you can use DeleteObject as Boomer said.
Related
We have an application which was originally running on EFCore 2.2.6 and .Net Core 2.1. We have the below code to query and delete data, it uses AsNoTracking:
var deletedDiag = _unitOfWork.GetRepository<DIAGNOSIS_DETAIL>()
.GetAll(c => c.Diag.CMS_CASE_ID == model.DiagnosisModel.CASE_ID)
.Include(i => i.Diag).AsNoTracking().ToList();
if (deletedDiag != null)
{
foreach (var del in deletedDiag)
{
if (!model.SUBDETAILS.Any(a => a.DIAGNOSIS_DETAIL_ID == del.DIAGNOSIS_DETAIL_ID))
{
_unitOfWork.GetRepository<DIAGNOSIS_DETAIL>().Delete(del);
}
}
}
And Delete does:
public void Delete(TEntity entity) => _dbSet.Remove(entity);
The deletedDiag query has a include to bring in Diag as a navigational property, above this block of code another query gets the same Diag and marks it for update.
In EFCore 2.2.6 the delete works fine, it's able to update the Diag and then run the delete even though the deletedDiag has a path to it, there are no errors. In EFCore 5.0.8, we get an error stating that the entity is already being tracked when we try to delete, the fix was to get the deletedDiag to stop tracking it but setting it to null:
deletedDiag.ForEach(x => x.Diag = null);
that works, but why does it work in 2.2.6?
This is due to the capricious path EF-core has taken right from the start in lots of features and defaults. It was impossible to keep track of all changes between versions, even though they were fairly well documented.
One of those changes in v3 onward was in the area of no-tracking behavior. In EF-core 2, AsNoTracking performed identity resolution, which ensures that all occurrences of an entity with a given key in the result set are represented by the same entity instance. In EF-core 3 this was abandoned: each occurrence of the "same" entity now is a new instance.
That means that in EF-core 2, the statements _dbSet.Remove(entity) repeatedly marked the same Diag instance as deleted: i.e. attached it when not yet attached and set its state to Deleted. That's not a problem.
EF-core 3+ tries to attach different Diag instances with the same key, which fails.
Fortunately, in EF core 5 the earlier tracking behavior can be restored by using AsNoTrackingWithIdentityResolution.
var deletedDiag = _unitOfWork.GetRepository<DIAGNOSIS_DETAIL>()
.GetAll(c => c.Diag.CMS_CASE_ID == model.DiagnosisModel.CASE_ID)
.Include(i => i.Diag)
.AsNoTrackingWithIdentityResolution()
.ToList();
Good day,
I am failing to insert multiple records in EF core using InMemory database. By failing I mean it works, but it seems that it does not insert all records.
This is my code to insert:
var f1= new F1[]{ /* Where I have populated this*/}
await context.F1.AddRangeAsync(f1);
var f2= new F2[]{ /* Where I have populated this*/}
await context.f2.AddRangeAsync(f2);
var f3= new F3[]{ /* Where I have populated this*/}
await context.F3.AddRangeAsync(f3);
var f4= new F4[]{ /* Where I have populated this*/}
await context.F4.AddRangeAsync(f4);
var f5= new F5[]{ /* Where I have populated this*/}
await context.F5.AddRangeAsync(f5);
var f6= new F6[]{ /* Where I have populated this*/}
await context.F6.AddRangeAsync(f6);
var f7= new F7[]{ /* Where I have populated this*/}
await context.F7.AddRangeAsync(f7);
await context.SaveChangesAsync();
Where each collection has a minimum of 2 items and a maximum of 100.
After I run this code:
var f1= await context.F1.ToListAsync();
var f2= await context.F2.ToListAsync();
var f3= await context.F3.ToListAsync();
var f4= await context.F4.ToListAsync();
var f5= await context.F5.ToListAsync();
var f6= await context.F6.ToListAsync();
var f7= await context.F7.ToListAsync();
And breakpoint after the f7 line and hover over the collections, I can see all of them have only part of the items.
This is how I create the in memory database:
services.AddDbContext<FamousDbContext>(opt => opt.UseInMemoryDatabase("MyFamousDatabase"));
What I have tried so far:
1. I have tried to save (trigger SaveChanges) each time an item/collection is added.
2. Tried not using async to insert.
3. tried inserting only one collection and then only asking for on DbSet.
4. Tried researching documentation on EF core.
5. Tried changing the name of the in memory database.
My Assumption is that this is a limitation of InMemory Core, but I cannot confirm this without enough knowledge and not finding anything mentioned in documentation or anywhere else.
So my question is how do I get around this? Is this a limitation of using InMemory Db? Or am I doing something wrong on this one?
UPDATE:
I am using .NET core 2.2 and EF InMemory version: 2.2.4
UPDATE SOLVED:
I have managed to solve the problem, it seems it was for the reason EF core InMemory does not really support relational database, though I read it I did not think it was because of this until I went through try and error sessio, source: https://learn.microsoft.com/en-us/ef/core/miscellaneous/testing/in-memory. Because when I try to pre-populated the data each of the objects bellow "f1" has a reference to the F1 type in the following way:
public int F1Id {get;set}
public F1 F1 {get;set;}
As soon as I removed the object reference from all the objects and left only the F1Id it worked like a charm.
I am using Visual Studio 2012. I have a very simple Products table in a Local Database (cleverly named Database1). It has two fields: id and Name. I preloaded it with a few test data.
The following code should update the Name of the first Product. Internally, it does. I can retrieve all Products and see that the first one's Name is "Shirt." But the change is never flushed to the database. The record is never updated. Examining the database reveals that the name has not been changed.
My question is simple: Why are changes not being sent to the database?
using (var context = new Database1Entities())
{
var products = context.Products;
products.First().Name = "Shirt";
context.SaveChanges();
}
Thanks in advance.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
EDIT:
I tried my best to enter the full code and output here. But, no matter what I do, I continue to get the "Your post appears to contain code that is not properly formatted" error message. After 30 minutes I am giving up.
EDIT:
The code and output is here: http://pastebin.com/GmQtwAND
I discovered the issue...and its solution.
The "Copy to Output Directory" property of the database file was set to "Copy always." This, of course, meant that every time the application was built, a fresh copy was placed into the Bin directory. The fix was to set it to "Copy if newer." Doh.
In other words, the changes were being persisted in the database, but then the database was being clobbered when the application got rebuilt.
Well this could mean that you're not tracking changes, how about you try to set the State to -> Modified and see if it's going to work:
var product = products.First();
product.Name = "Shirt";
product.State = EntityState.Modified;
context.SaveChanges();
If you want to enable it for the context you can do it like this:
context.Configuration.AutoDetectChangesEnabled = true;
Hi I had developed a C# Budget application using SQL Compact and EF4, I created the EF model through the VS2010 Entity Data Model template. It is all working very well. However I am considering developing a iPhone app to support cash transactions and thought it would be better to have the back end DB supported on both platforms. After creating the SQLite DB and creating a new model I have come across a problem when trying to access referenced data via the Navigation properties in my model. I am getting a NullReferenceException when trying to display a property of a referenced table.
When using the following code I get the exception on the last line:
BudgetEntities budget = new BudgetEntities();
var accounts = budget.BankAccounts.ToList();
foreach (BankAccount a in accounts)
{
Console.WriteLine("Name:" + a.Description);
Console.WriteLine("Number:" + a.AccountNumber);
Console.WriteLine("Type:" + a.BankAccountType.AccountType); //Exception occurs here.
}
Strange thing is that the exception doesn't occur in this example. I'm not sure what is going on?
BudgetEntities budget = new BudgetEntities();
var accoutTypes = budget.BankAccountTypes;
var account = new BankAccount();
account.ID = Guid.NewGuid();
account.AccountTypeID = accoutTypes.First(t => t.AccountType.StartsWith("Credit")).ID;
account.BSB = "3434";
account.AccountNumber = "32323";
account.Description = "Test";
account.TrackingAccount = true;
budget.AddObject("BankAccounts", account);
budget.SaveChanges();
var accounts = budget.BankAccounts.ToList();
foreach (BankAccount a in accounts)
{
Console.WriteLine("Name:" + a.Description);
Console.WriteLine("Number:" + a.AccountNumber);
Console.WriteLine("Type:" + a.BankAccountType.AccountType); //Exception doesn't happen.
}
This is only a simple example and I know I could fix it by adding .Include("BankAccountTypes") to the query however I have other queries that are quite complex that are creating object which include properties from referenced object with in the query and I am not quite sure how to get around this issue for them.
EDIT:
After having a break between projects I have come back to this problem and I have finally resolved my problem. it had nothing to do with the code. It was with the data. I had converted a SQL Compact database to SQLite via a dump and load and had the syntax wrong for my Guid column data. I was inserting the Guid as '7cee3e1c-7a2b-462d-8c3d-82dd6ae62fb4' when it should have been x'7cee3e1c7a2b462d8c3d82dd6ae62fb4'
Hopefully the hair I pulled out working through this problem will grow back :)
Thanks everyone for your input.
In second example your code snippet begins with:
var accoutTypes = budget.BankAccountTypes;
This loads all bank account types to your application and you don't need lazy loading anymore (EF will automatically recognize that these entities were already loaded and fix relations with bank accounts).
First check if your account class is dynamic proxy (just check type of a in the debugger). If it is not you made some mistake in the class definition and lazy loading will not work. Next check if lazy loading is enabled on your context instance (budget.ContextOptions.LazyLoadingEnabled property).
Make sure the BankAccountType property is declared virtual in BudgetEntities.
My project is divided to PresentationLayer, BusinesLogicLayer and DataAccessLayer. Every created object goes through these layers.
In simplifying:
SetFilter.xaml.cs
FilterFactory fFactory = new FilterFactory();
Filter myFilter = fFactory.Create(someClient, time, message);
FilterBLO filterBLO = new FilterBLO();
filterBLO.Save(myFilter);
FilterBLO.cs
FilterDAO filterDAO = new FilterDAO();
using (TransactionScope transcope = new TransactionScope())
{
filterDAO.Save(myFilter);
transcope.Complete()
}
FilterDAO.cs
using(DBDataContext dbdc = new DBDataContext)
{
dbdc.Filter.InsertOnSubmit(myFilter);
changeSet = dbdc.GetChangeSet();
dbdc.SubmitChanges()
}
Filter is connected with table Client using ClientFilter table containing FilterID and ClientID. (many-to-many relationship)
If I create new 3 objects everything's ok, but if I'm getting existing Client in database (also using ClientBLO and ClientDAO so in separate TransactionScope and separate DBDataContext) I get error:
An attempt has been made to Attach or Add an entity that is not new,
perhaps having been loaded from another DataContext. This is not
supported.
(I searched other similar threads but I didn't found solution to my problem.)
And finally my question
How should I save myFilter if Client exists in database. I tried Attach() it to datacontext in FilterDAO.cs but I get the same error.
You have to obtain Client from the database with the same DataContext you use to InsertOnSubmit the Filter; then you have to set Client value in the Filter object with that object before InsertOnSubmit.