Whenever I do something like the following:
public class MyDto
{
[Key]
public int ID { get; set; }
public int ParentID { get; set; }
public String Name { get; set; }
}
MyDataContext dataContext = new MyDataContext();
MyParentDto myParentDto; // <-- Existing parent DTO querried from the server. Has a relation to MyDto on MyDto.ParentID == MyParentDto.ID.
List<MyDto> myDtos = new List<MyDto>();
myDtos.Add(new MyDto
{
Name = "First MyDto!"
});
myDtos.Add(new MyDto
{
Name = "Second MyDto!"
});
// Some time later.
foreach (var myDto in myDtos)
{
myDto.ParentID = myParentDto.ID;
dataContext.MyDtos.Add(myDto);
}
dataContext.SubmitChanges(OnMyCallback)
I get the following vague exception, but my data submits just fine:
System.ServiceModel.DomainServices.Client.DomainOperationException: Submit operation failed. An entity with the same identity already exists in this EntitySet.
The stack trace ends with:
System.ServiceModel.DomainServices.Client.EntitySet.AddToCache(Entity entity)
System.ServiceModel.DomainServices.Client.Entity.AcceptChanges()
Both MyDto instances are set to Detached before they are added to dataContext and New afterwards. If I reduce the number of added MyDto instances to one, I get no error. If I call SubmitChanges in between the two adds. Again, both of the MyDto instances are added to the database just fine, but the client crashes with the Exception. What is going on? Thanks.
Edits:
// On the server
[Insert]
public void InsertMyDto(MyDto a_myDto) // <- Yes I prefix. :p
{
try
{
MyEntity myEntity = new MyDto
{
ParentID = a_myDto.ParentID,
Name = a_myDto.Name
}
ObjectContext.MyEntities.AddObject(myEntity);
ObjectContext.SaveChanges();
}
catch (Exception)
{
throw; // <- Never hits this spot.
}
}
// Call back
public void OnMyCallback(SubmitOperation a_submitOperation)
{
if (a_submitOperation.HasError)
throw a_submitOperation.Error; // <- It doesn't matter if I have this or not, the client crashes anyway.
// Other stuff that is not hit because it throws the exception above.
}
I found that the solution to my problem is to save the ID back to the dto when the entity is saved. Like this:
[Insert]
public void InsertMyDto(MyDto a_myDto) // <- Yes I prefix. :p
{
try
{
MyEntity myEntity = new MyDto
{
ParentID = a_myDto.ParentID,
Name = a_myDto.Name
}
ObjectContext.MyEntities.AddObject(myEntity);
ObjectContext.SaveChanges();
a_myDto.ID = myEntity.ID; // <- Solution
}
catch (Exception)
{
throw; // <- Never hits this spot.
}
}
Have you tried setting the parent instead of it's ID?
foreach (var myDto in myDtos)
{
myDto.Parent = myParentDto;
} //Assuming myParentDto is already in the context, if not add it first
Edit: I'm taking a wild guess here but could you check the HashCode of the objects right before the Exception occurs? You could also try overriding the GetHashCode() method to return something random every time just to test those are the exact entities involved in the exception.
Related
I recently started to work with Blazor and Entity Framework and ran into a problem I don't know how to solve properly:
I use Blazor server + webassembly and code-first approach for DB.
I have 2 entities with relation one to many and want to load child object with parent included. If I get all child objects for a table, everything is fine; however, if I get only one child by using Where method with Include, app starts a recursion. It gets parent, then all its children, all their parents and so on, until I get "out of memory" exception in client app.
I turned on Newtonsoft.Json.ReferenceLoopHandling.Ignore, and it helped me for table view, but it doesn't work for loading only one child.
Parent object:
public partial class Project
{
public long ProjectId { get; set; }
public string Name { get; set; }
public ICollection<Environment> Environments { get; set; }
}
Parent access layer:
public IEnumerable<Project> GetAllProjects()
{
try
{
return _context.Projects.ToList();
}
catch
{
return new List<Project>();
}
}
public Project GetProjectData(long id)
{
try
{
Project project = _context.Projects.Find(id);
return project;
}
catch { throw; }
}
Child object:
public partial class Environment
{
public long EnvironmentId { get; set; }
public string Name { get; set; }
public long ProjectId { get; set; }
public Project Project { get; set; }
}
Child access layer:
public IEnumerable<Environment> GetAllEnvironments() // this one works fine
{
try
{
return _context.Environments
.Include(e => e.Project)
.ToList();
}
catch
{
return new List<Environment>();
}
}
public Environment GetEnvironmentData(long id) // this one starts endless recursion
{
try
{
Environment env = _context.Environments
.Where(e => e.EnvironmentId == id)
.Include(e => e.Project)
.FirstOrDefault();
return env;
}
catch { throw; }
}
For now I'm loading parent object manually, but it would be good to figure out how to do it automatically with Include.
Think about what you want the JSON to look like, and you'll see you need to break the cycle in your JSON serializer by supressing the serialization of one of the navigation properties, probably Environment.Project.
Looks like the solution was really simple, I didn't check for null for 'Project' property of 'Environment' object. After I added this check to the page problem is gone.
I'm still not sure why the page was consuming a lot of memory after it had encountered the 'Object reference not set to an instance of an object' error.
#if (env == null || env.Project == null)
{
<strong>Loading...</strong>
}
else
{
// page markup
}
I am trying to return a collection of customers, which works but I would like to make my call efficient so that each of the records brought back should have a Boolean return to see if that customer has children (addresses).
whats the most efficient call that will return me all the customers including a flag for each to determine if children are present
below is my current code for just retrieving customers (relationship between customer and address is through customerID on address table)
using entity framework 6.1
public static List<Customer> GetCustomers()
{
try
{
using (var context = new MyContext())
{
return (from c in context.Customers
select c).ToList();
}
}
catch (Exception ex)
{
throw new CustomerException(MethodBase.GetCurrentMethod().Name, ex);
}
}
I would perhaps do something like this.
public class CustomerDto
{
public string Name { get; set; }
public int AddressCount { get; set; }
}
var result = from w in context.Customers select new { Name = w.Name, AddressCount = w.Addresses.Count };
var ret = new List<CustomerDto>();
foreach (var customer in result)
{
var newCustomerDto = new CustomerDto();
newCustomerDto.Name = customer.Name;
newCustomerDto.AddressCount = customer.AddressCount;
}
return ret;
This is a simple example, and should serve as a starting point for you.
I don't have VS open, so just check the code, but it looks good to me. I forgot to mention that if it is only a flag that you are after, instead of .Count you can use .Any
Use this code
public static List<Customer> GetCustomers()
{
try
{
using (var context = new MyContext())
{
return (from c in context.Customers
where c.Address.Any()
select c).ToList();
}
}
catch (Exception ex)
{
throw new CustomerException(MethodBase.GetCurrentMethod().Name, ex);
}
}
I have an MVC application that uses Entity Framework 5. In few places I have a code that creates or updates the entities and then have to perform some kind of operations on the updated data. Some of those operations require accessing navigation properties and I can't get them to refresh.
Here's the example (simplified code that I have)
Models
class User : Model
{
public Guid Id { get; set; }
public string Name { get; set; }
}
class Car : Model
{
public Guid Id { get; set; }
public Guid DriverId { get; set; }
public virtual User Driver { get; set; }
[NotMapped]
public string DriverName
{
get { return this.Driver.Name; }
}
}
Controller
public CarController
{
public Create()
{
return this.View();
}
[HttpPost]
public Create(Car car)
{
if (this.ModelState.IsValid)
{
this.Context.Cars.Create(booking);
this.Context.SaveChanges();
// here I need to access some of the resolved nav properties
var test = booking.DriverName;
}
// error handling (I'm removing it in the example as it's not important)
}
}
The example above is for the Create method but I also have the same problem with Update method which is very similar it just takes the object from the context in GET action and stores it using Update method in POST action.
public virtual void Create(TObject obj)
{
return this.DbSet.Add(obj);
}
public virtual void Update(TObject obj)
{
var currentEntry = this.DbSet.Find(obj.Id);
this.Context.Entry(currentEntry).CurrentValues.SetValues(obj);
currentEntry.LastModifiedDate = DateTime.Now;
}
Now I've tried several different approaches that I googled or found on stack but nothing seems to be working for me.
In my latest attempt I've tried forcing a reload after calling SaveChanges method and requerying the data from the database. Here's what I've done.
I've ovewrite the SaveChanges method to refresh object context immediately after save
public int SaveChanges()
{
var rowsNumber = this.Context.SaveChanges();
var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings);
return rowsNumber;
}
I've tried getting the updated object data by adding this line of code immediately after SaveChanges call in my HTTP Create and Update actions:
car = this.Context.Cars.Find(car.Id);
Unfortunately the navigation property is still null. How can I properly refresh the DbContext immediately after modifying the data?
EDIT
I forgot to originally mention that I know a workaround but it's ugly and I don't like it. Whenever I use navigation property I can check if it's null and if it is I can manually create new DbContext and update the data. But I'd really like to avoid hacks like this.
class Car : Model
{
[NotMapped]
public string DriverName
{
get
{
if (this.Driver == null)
{
using (var context = new DbContext())
{
this.Driver = this.context.Users.Find(this.DriverId);
}
}
return this.Driver.Name;
}
}
}
The problem is probably due to the fact that the item you are adding to the context is not a proxy with all of the necessary components for lazy loading. Even after calling SaveChanges() the item will not be converted into a proxied instance.
I suggest you try using the DbSet.Create() method and copy across all the values from the entity that you receive over the wire:
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
this.Context.Entry(newEntry).CurrentValues.SetValues(obj);
return newEntry;
}
UPDATE
If SetValues() is giving an issue then I suggest you try automapper to transfer the data from the passed in entity to the created proxy before Adding the new proxy instance to the DbSet. Something like this:
private bool mapCreated = false;
public virtual TObject Create(TObject obj)
{
var newEntry = this.DbSet.Create();
if (!mapCreated)
{
Mapper.CreateMap(obj.GetType(), newEntry.GetType());
mapCreated = true;
}
newEntry = Mapper.Map(obj, newEntry);
this.DbSet.Add(newEntry;
return newEntry;
}
I use next workaround: detach entity and load again
public T Reload<T>(T entity) where T : class, IEntityId
{
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id);
}
my supermarket model contains a StockItem class and an Alert class which contains a StockItem field:
public class StockItem
{
public int ID { get; set; }
public string Name { get; set; }
public int CurrentQuantity { get; set; }
public int MinQuantity { get; set; }
}
public class Alert
{
public int ID { get; set; }
public int Message{ get; set; }
public virtual StockItem StockItem { get; set; }
}
I Have a function that fetches all StockItems with one DbContext:
using (var db = new MyDbContext())
{
return db.StockItems.ToList();
}
And another function that process these items, and adding new Alerts in a another DbContext:
foreach (var item in items)
{
if (item.CurrentQuantity < item.MinQuantity)
{
using (var db = new MyDbContext())
{
db.Alerts.Add(new Alert(){StockItem = item, Message = "Low Quantity"});
db.SaveChanges();
}
}
}
The problem is: When an Alert is Saved, a new Stock Item (with a different id) is added to the database, although it is already there!
any solutions?
I think you should Attach the stockitem first.
Try this:
foreach (var item in items)
{
if (item.CurrentQuantity < item.MinQuantity)
{
using (var db = new MyDbContext())
{
db.StockItems.Attach(item);
db.Alerts.Add(new Alert {StockItem = item, Message = "Low Quantity"});
db.SaveChanges();
}
}
}
using (var db = new MyDbContext())
{
var items = db.StockItems.ToList();
foreach (var item in items)
{
if (item.CurrentQuantity < item.MinQuantity)
{
db.Alerts.Add(new Alert {StockItem = item,
Message = "Low Quantity"});
db.SaveChanges();
}
}
}
In this case you dont need to do attach. EF can only track changes in its own life cycle, in your first case when you do,
using (var db = new MyDbContext())
{
return db.StockItems.ToList();
}
You are disposing MyDbContext, so EF makes all stock items as independent (detached items), and when you add them to different context, context assumes that it is a new item and it will insert the one.
The best way will be to keep Context alive throughout the changes you want to make. Also note, keeping context alive for longer time does not mean you will keep database connection open all the time. EF will open and close database connection automatically only when you are executing query and you are calling save changes.
Otherwise you have to attach as Ben suggested.
I have a problem with something that seems to be a bug in Entity Framework 4.1: I have added a handler on ObjectContext.SavingChanges which updates a property "LastModified" whenever an object is added to or modified in the database. Then I do the following:
Add two objects to the database, and submit (call SaveChanges())
Modify the first object that was added
Extract the two objects ordered by LastModified
The resulting objects are returned in the wrong order. Looking at the objects, I can see that the LastModified property has been updated. In other words, the SavingChanges event was fired properly. But looking in the database, the LastModified column has not been changed. That is, there is now a difference between EF's cached objects and the rows in the database.
I tried performing the same update to LastModified in an overridden "SaveChanges" method:
public override int SaveChanges()
{
SaveChangesHandler();//updating LastModified property on all objects
return base.SaveChanges();
}
Doing this caused the database to be updated properly and the queries returned the objects in proper order.
Here is an entire test program showing the error:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace TestApplication
{
class Program
{
private PersistenceContext context;
private static void Main(string[] args)
{
var program = new Program();
program.Test();
}
public void Test()
{
SetUpDatabase();
var order1 = new Order {Name = "Order1"};
context.Orders.Add(order1);
var order2 = new Order {Name = "Order2"};
context.Orders.Add(order2);
context.SaveChanges();
Thread.Sleep(1000);
order1 = GetOrder(order1.Id); // Modified 1.
order1.Name = "modified order1";
context.SaveChanges();
List<Order> orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order2.Id);//works fine - this was the oldest object from the beginning
Thread.Sleep(1000);
order2 = GetOrder(order2.Id); // Modified 2.
order2.Name = "modified order2";
context.SaveChanges();
orders = GetOldestOrders(1);
AssertEquals(orders.First().Id, order1.Id);//FAILS - proves that the database is not updated with timestamps
}
private void AssertEquals(long id1, long id2)
{
if (id1 != id2) throw new Exception(id1 + " != " + id2);
}
private Order GetOrder(long id)
{
return context.Orders.Find(id);
}
public List<Order> GetOldestOrders(int max)
{
return context.Orders.OrderBy(order => order.LastModified).Take(max).ToList();
}
public void SetUpDatabase()
{
//Strategy for always recreating the DB every time the app is run.
var dropCreateDatabaseAlways = new DropCreateDatabaseAlways<PersistenceContext>();
context = new PersistenceContext();
dropCreateDatabaseAlways.InitializeDatabase(context);
}
}
////////////////////////////////////////////////
public class Order
{
public virtual long Id { get; set; }
public virtual DateTimeOffset LastModified { get; set; }
public virtual string Name { get; set; }
}
////////////////////////////////////////////////
public class PersistenceContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public PersistenceContext()
{
Init();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public void Init()
{
((IObjectContextAdapter) this).ObjectContext.SavingChanges += SavingChangesHandler;
Configuration.LazyLoadingEnabled = true;
}
private void SavingChangesHandler(object sender, EventArgs e)
{
DateTimeOffset now = DateTimeOffset.Now;
foreach (DbEntityEntry entry in ChangeTracker.Entries()
.Where(entity => entity.State == EntityState.Added || entity.State == EntityState.Modified))
{
SetModifiedDate(now, entry);
}
}
private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity)
{
if (modifiedEntity.Entity == null)
{
return;
}
PropertyInfo propertyInfo = modifiedEntity.Entity.GetType().GetProperty("LastModified");
if (propertyInfo != null)
{
propertyInfo.SetValue(modifiedEntity.Entity, now, null);
}
}
}
}
I should add that the SavingChanges handler worked fine before we upgraded to EF4.1 and using Code-First (that is, it worked in EF4.0 with model-first)
The question is: Have I found a bug here, or have I done something wrong?
I'm not sure if this can be considered a Bug. What seems to happen is that the way you manipulate the LastModified property does not trigger INotifyPropertyChanged and thus the changes do not get populated to your Database.
To prove it use:
order2.Name = "modified order2";
((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.GetObjectStateEntry(order2).SetModifiedProperty("LastModified");
To utilize this knowledge in your SavingChangesHandler:
private void SavingChangesHandler(object sender, EventArgs e)
{
DateTimeOffset now = DateTimeOffset.Now;
foreach (DbEntityEntry entry in ChangeTracker.Entries()
.Where(entity => entity.State == EntityState.Added || entity.State == EntityState.Modified))
{
SetModifiedDate(now, entry);
if (entry.State == EntityState.Modified)
{
((IObjectContextAdapter) this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity).SetModifiedProperty("LastModified");
}
}
}
Edit:
I looked into this a little more and you are correct. For some reason MS decided to not fire PropertyChanged events when using PropertyInfo.SetValue anymore. Only one way to find out if this is a bug or a design decision: File a bug report / Post to msdn Forums.
Though changing the property directly via CurrentValue seems to work fine:
private static void SetModifiedDate(DateTimeOffset now, DbEntityEntry modifiedEntity)
{
if (modifiedEntity.Entity == null)
{
return;
}
modifiedEntity.Property("LastModified").CurrentValue = now;
}