EF4: ObjectSet.AddObject() not working - entity-framework

I am setting up a repository with Entity Framework 4, and I can't get the ObjectSet.AddObject() method working. Here is the code I am using--to keep things simple, I copied it out of the repository into my unit test method:
/* m_FilePath value is passed in by test initializer. */
// Configure a SQL CE connection string
var sqlCompactConnectionString = string.Format("Data Source={0}", m_FilePath);
// Create an Entity Connection String Builder
var builder = new EntityConnectionStringBuilder();
builder.Metadata = string.Format("res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl", "Model.Notes");
builder.Provider = "System.Data.SqlServerCe.4.0";
builder.ProviderConnectionString = sqlCompactConnectionString;
var edmConnectionString = builder.ToString();
// Create object context and object set
var context = new NotesContainer(edmConnectionString);
var objectSet = context.CreateObjectSet<Note>();
// Add a note
var entity = new Note();
objectSet.AddObject(entity);
// Build assertion
var notes = objectSet.AsEnumerable();
var count = notes.Count();
Assert.AreEqual(1, count);
The count that is returned is zero--the object set is empty, so the assertion fails. When I step through the code, the object context and object set are created, but enumeration of the object set returns no results.
What's the error in my code? Thanks for your help.

The notes.Count() is executed against your data file (which I assume is empty). Your new object won't be added until you call context.SaveChanges().
Consider:
objectSet.AddObject(new Note()); // new object present in memory
var notes = objectSet.AsEnumerable();
var count = notes.Count(); // query against DB file;
// what's in memory at this point is irrelevant
Add context.SaveChanges() after you add new Note and assert will pass. But of course, that introduces whole new range of problems with keeping DB state the same for every test run.

That is not entirely true.
e.g. Parent / Childs
When using Context.AddObject() for Parent it won't be added to until Context.SaveChanges() is called but when adding a Child it will be added without saving (when foreign keys are set of course).
Not sure if it is a bug or if it is by design in the EF ...
The only "workaround" I see at the moment:
introduce a Client table and each object (Parent, Child) has a reference to a Client
call context.SaveChanges() (Drawback: the user has no chance to cancel the operation)
Also calling Context.ParentSet.Execute(MergeOption.AppendOnly); does not have the expected effect!
Solution/Workaround: I maintain my own list of objects for adding/removing + setting the DataSource = null and then to my internal list.
Since it is necessary to set the DataSource = null it seems that there is something strange in EF ...

Related

Auto detection of changes with disconnected entities

I am making a simple editor on a web server that lets user change/add data to a single table stored on a MS SQL server.
I am using Entity Framework 6 to do this, and I am wondering how I should do to track the changes made to the entity model.
I would have hoped that I could load new data in the context, and have the context automatically diff against what's in the DB, and then call SaveChanges().
But from what I read online, it looks like I need to loop through all the data, and check myself what changed, so that I can then call Context.Entry(myEntry).State = Added or Context.Entry(myEntry).State = Modified
Is there no way for EF to automatically detect what's new, what's modified and what's unchanged?
I would recommend passing ViewModels or DTOs to the view, then map them back to the reloaded entity on a commit. EF will automatically only update values that change when setting values. Setting a value without changing the value will not trigger an update. (Where attaching an entity, and setting it's modified state will update all columns) Passing entities, while convenient, exposes more about your data structure than your UI may present, and can be tampered with before being sent back. Never trust anything coming back from the client. When serialized to a client, the data is no longer an entity, it is a JSON block of data. When sent back to the server, it isn't a tracked entity, it is a POCO with the entity's signature. No change tracking that EF entities can provide will apply on the client or survive serialization/deserialization.
For example:
Given a Child that has a name and birth date. We select a DTO to pass to the view. The view changes a name, we get the DTO back and copy all values, modified or otherwise back into the entity and call SaveChanges()
// For example, loading the child in the controller to pass to the view...
ChildDTO childDto = null;
using (var context = new TestDbContext())
{
childDto = context.Children
.Select(x => new ChildDto
{
ChildId = x.ChildId,
Name = x.Name,
BirthDte = x.BirthDate
}).Single(x => x.ChildId == 1);
}
// View updates just the name...
childDto.Name = "Luke";
// Example if the view passed DTO back to controller to update...
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
child.Name = childDto.Name;
child.BirthDate = childDto.BirthDate;
context.SaveChanges();
}
If the name changed and the birth date did not, the EF generated update statement would only update the Name. If the entity name was already "Luke", then no Update statement would be issued. You can verify this behavior with an SQL profiler to see if/when/what SQL EF sends to the database.
Automapper can help simplify this for getting the DTO back into the entity:
var mappingConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Child, ChildDTO>();
cfg.CreateMap<ChildDTO, Child>();
});
Then when reading, leverage ProjectTo instead of Select:
using (var context = new TestDbContext())
{
childDto = context.Children
.ProjectTo<ChildDTO>(mappingConfig)
.Single(x => x.ChildId == 1);
}
... and when updating the entity:
using (var context = new TestDbContext())
{
var child = context.Children.Single(x => x.ChildId == 1);
var mapper = mappingConfig.CreateMapper();
mapper.Map(childDto, child); // copies values from DTO to the entity instance.
context.SaveChanges();
}
It's important to validate the DTO prior to copying values across to the Entity, whether doing it manually or with Automapper. Automapper config can also be set up to only copy over values that are expected/allowed to change.

There is already a generated proxy type for the object layer type 'MyProject.Model.Applications'

I am using a code first approach. I am setting up a process to copy table rows from sourcedb to targetdb on daily basis. Also I need to maintain the primary key values in both. (this is required) i.e. Both dbs should have same primary key for given row.
I have created 2 different contexts by referring same classes. Both contexts are intact. I am getting all the rows from sourcedb into a list of object and passing it to another context to insert that range into targetdb. But while doing so I am getting the error as 'There is already a generated proxy type for the object layer type 'MyProject.Model.Applications'. This occurs when the same object layer type is mapped by two or more different models in an AppDomain.
I have checked some other links. But nothing has worked so far. I have also checked is it possible to share POCO object between two DbContext?.
Following is some pseudo code,
using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew, TimeSpan.FromSeconds(6000)))
{
using (var dbContext = new SourceDbContext())
{
DateTime dateToBeCompared = DateTime.UtcNow.Date.AddMonths(-11);
dbContext.Configuration.LazyLoadingEnabled = false;
dbContext.Configuration.AutoDetectChangesEnabled = false;
//get data from application related tables.
var applications = dbContext.Applications.AsNoTracking().Where(a => a.UpdatedOn <= dateToBeCompared)
.ToList();
using (var connection1 = new System.Data.SqlClient.SqlConnection("TargetDbConnectionString"))
{
connection1.Open();
using (var targetDbContext = new TargetDbContext(connection1, true))
using (TransactionScope tsSuppressed = new TransactionScope(TransactionScopeOption.Suppress))
{
targetDbContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[Applications] ON");
}
try
{
targetDbContext.Applications.AddRange(applications);
targetDbContext.SaveChanges();
}
catch (Exception ex)
{
throw;
}
using (TransactionScope tsSuppressed = new TransactionScope(TransactionScopeOption.Suppress))
{
targetDbContext.Database.ExecuteSqlCommand("SET IDENTITY_INSERT [dbo].[Applications] OFF");
}
}
connection1.Close();
}
scope.Complete();
}
Also there are some foreign key constraints. But whatever is there, common to both contexts.
Neither dbContext.Configuration.LazyLoadingEnabled = false; nor AsNoTracking() prevents EF from creating proxy objects in the source context, which in turn prevents using them in another context.
What you really need is to turn ProxyCreationEnabled off:
Gets or sets a value indicating whether or not the framework will create instances of dynamically generated proxy classes whenever it creates an instance of an entity type. Note that even if proxy creation is enabled with this flag, proxy instances will only be created for entity types that meet the requirements for being proxied. Proxy creation is enabled by default.
It also prevents lazy loading because it depends on proxy class intercepting the virtual properties. So simply replace
dbContext.Configuration.LazyLoadingEnabled = false;
with
dbContext.Configuration.ProxyCreationEnabled = false;
and the issue will be resolved. Just note that this doesn't mean the code will work correctly. The entities must have explicit FKs defined (not simply navigation properties) and the related entities must be processed first to prevent FK constraint violation.

having trouble updating detached entity

I have read quite a few posts about this, and I can't see how my situation is different, but it must be because it still doesn't update.
Basically, my method receives a detached entity in a message. I check it's key to see if it already exists. If it does not exist I add it to the database. (this works fine) If it exists I would like to update its values.
Here is my code:
InteropObject clientObject = (InteropObject)message.ItemToAddUpdate;
bool exists = context.InteropObjects.Any(o => o.GUID == clientObject.GUID);
if (!exists)
{
context.InteropObjects.AddObject(clientObject);
}
else
{
context.Attach(clientObject);
context.ObjectStateManager.GetObjectStateEntry(clientObject).SetModified();
}
context.SaveChanges();
thanks for the help!
The problem is that in the old ObjectContext API, setting the state to Modified does not set the properties of the entity to Modified. In the DbContext API this is fixed internally by a call that does do that.
In the ObjectContext API, you can get the same effect by setting the properties of the attached entity:
context.Attach(clientObject);
var ose = context.ObjectStateManager.GetObjectStateEntry(clientObject);
// Obtain an object array containing current values
var values = new object[ose.CurrentValues.FieldCount];
ose.CurrentValues.GetValues(values);
// "Overwrite" CurrentValues by these values:
ose.CurrentValues.SetValues(values);
So you set the properties by the values they already have, but this triggers the state manager to mark the property as Modified.

Entity Framework AddObject not adding Object to EntitySet

I have the following piece of code
private void DoAddPropertyType()
{
var ctx = Globals.DbContext;
var propType = new PropertyType()
{
ID = Guid.NewGuid(),
Name = "NewType",
Description = "New Property Type",
ModifiedDate = DateTime.Now
};
ctx.AddToPropertyTypes(propType);
PropertyTypes.Add(propType);
}
Globals.DbContext provides a static reference to the objectcontext initiated on startup. For some reason the ctx.AddToPropertyTypes(propType); bit does not add the entity to the context. If I breakpoint after that line and browse the ctx.PropertyTypes entity set it is not there. Any ideas?
EDIT 1:
If I add a ctx.SaveChanges() after the ctx.AddToPropertyTypes(propType) and step the actual adding appears to happen only once SaveChanges execute. This however does not suit my requirements as I want to first validate objects prior to saving and wanted to iterate through the entities in the entity set. Does any one know of an alternative approach?
So that is the point of your issue. ctx.PropertyTypes is not a real collection - it is entrance to the database and your "browsing" actually executes query to the database where your new object was not yet stored. If you want to find a new object added to the context without saving it first you must search the object inside the ObjectStateManager:
var entity = ctx.ObjectStateManager
.GetObjectStateEntries(EntityState.Added)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<PropertyType>()
.SingleOrDefault(p => p.ID == ...);

EntityCollection<TEntity>.Contains(...) returns false for an entity queried out of the EntityCollection

I have the following code snippet.
// Here I get an entity out of an EntityCollection<TEntity> i.e ContactSet. Entity is obtained and not null.
ProjectContact obj = ((Project)projectDataGrid.SelectedItem).ContactSet
.Where(projectContact => projectContact.ProjectId == item.ProjectId &&
projectContact.ContactId == item.ContactId).First();
// And the next line I just check whether ContactSet contains the queried entity that i.e. obj.
bool found = ((Project)projectDataGrid.SelectedItem).ContactSet.Contains(obj);
but found is always false. How can that be?
edit: Matt thank you for your guidance but let me make it a bit more clear since I haven't given out the full source code.
I have three tables in my database:
Project, Contact and ProjectContact and there's a many-to-many relationship between Project and Contact table through the ProjectContact table, although ProjectContact table has some extra columns other than Project and Contact table keys, and that's why I get an extra entity called ProjectContact if I use ADO.NET entity framework's entity designer generated code.
Now at some point I get a Project instance within my code by using a linq to entities query i.e:
var item = (from project in myObjectContext.Project.Include("ContactSet.Contact")
orderby project.Name select project).FirstOrDefault();
Note that ContactSet is the navigational property of Project to ProjectContact table and Contact is the navigational property of ProjectContact to Contact table.
Moreover the queried Project in question i.e. "item" has already some ProjectContacts in its item.ContactSet entity collection, and ContactSet is a standard EntityCollection implementation generated by entity designer.
On the other hand, ProjectContact overrides Equals() and GetHashCode() etc. but if I use that overriden implementation within an EqualityComparer then Project.ContactSet.Contains returns true so I'm guessing there's no problem with that but now the tricky part comes along. Assume that I have the following snippet:
using(SomeObjectContext myObjectContext = new SomeObjectContext())
{
var projectQueryable = from project in myObjectContext.Project.Include("ContactSet.Contact") orderby project.Name select project;
ObservableCollection<Project> projects = new ObservableCollection<Project>(projectQueryable.ToList());
var contactQueryable = from contact in myObjectContext.Contact select contact;
ObservableCollection<Contact> contacts = new ObservableCollection<Contact>(contactQueryable.ToList());
Project p = projects[0];
Contact c = contacts[0];
//Now if I execute the code below it fails.
ProjectContact projectContact = new ProjectContact();
projectContact.Contact = c;
projectContact.Project = p;
projectContact.ContactId = c.Id;
projectContact.ProjectId = p.Id;
projectContact.Role = ContactRole.Administrator; // This corresponds to the column in ProjectContact table and I do manual conversion within the partial class since EF doesn't support enums yet.
p.ContactSet.Add(projectContact); // This line might be unnecessary but just to be on the safe side.
// So now p.ContactSet does indeed contain the projectContact and projectContact's EntityState is Added as expected. But when I execute the line below without saving changes it fails.
bool result = p.ContactSet.Remove(projectContact); // result == false and projectContact is still in the p.ContactSet EntityCollection.
//Now if I execute the line below
myObjectContext.Delete(projectContact);
//Now projectContact's EntityState becomes Detached but it's still in p.ContactSet.
// Also note that if I test
bool exists = p.ContactSet.Contains(projectContact);
// It also returns false even if I query the item with p.ProjectContact.Where(...) it returns false.
}
Since everything occurs within the same ObjectContext I think I am missing something about EntityCollection.Remove(). But it seems still odd that ContactSet.Contains() returns false for an item obtained via direct Where query over ContactSet. In the end the question becomes:
How do you really Remove an item from an EntityCollection without persisting to the database first. Since a Remove() call after Add() apparently fails.
This looks like it should work, Some ideas:
Does ProjectContact override Object.Equals()? Or perhaps the ContactSet implements ICollection, and there may be a bug in the implementation of ICollection.Contains()?