Entity Framework - trying to insert null values when creating a new object - entity-framework

I'm having the same problem that a few of you have had - when trying to insert a new object, EF inserts null values for some of their properties, and the insert fails.
First let me describe the structure of our DB. Its an event management system, in which each event needs to be associated with a practice group, stored in a cache table but ultimately fetched from Active Directory. I manually created the join table - is that a problem? Anyway, so Event has a foreign key pointing to EventPracticeGroup, which has a foreign key pointing to PracticeGroupCache. PracticeGroupCache also has a RegionId pointing to the Regions table.
The problem comes when trying to insert a new EventPracticeGroup object. Below is the code I'm currently using:
var eventPracticeGroup = new EventPracticeGroup();
if (TryUpdateModel<EventPracticeGroup>(eventPracticeGroup))
{
/*
var eventId = EventScheduleRepository.GetById(Convert.ToInt32(Request.QueryString["EventScheduleId"])).EventId;
eventPracticeGroup.Event = EventRepository.GetById(eventId);
eventPracticeGroup.PracticeGroupCache = PracticeGroupCacheRepository.GetById(eventPracticeGroup.PracticeGroupCacheId);
eventPracticeGroup.PracticeGroupCache.Region = RegionRepository.GetById(eventPracticeGroup.PracticeGroupCache.RegionId);
EventPracticeGroupRepository.Add(eventPracticeGroup);
*/
var eventId = EventScheduleRepository.GetById(Convert.ToInt32(Request.QueryString["EventScheduleId"])).EventId;
var theEvent = new Event() { Id = eventId };
EventRepository.Repository.UnitOfWork.Context.AttachTo("Events",theEvent);
var practiceGroupCache = new PracticeGroupCache() { Id = eventPracticeGroup.PracticeGroupCacheId };
practiceGroupCache.Region = new Region() { Id = eventPracticeGroup.PracticeGroupCache.RegionId };
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
EventPracticeGroupRepository.Add(eventPracticeGroup);
EventPracticeGroupRepository.Save();
return RedirectToAction("Index");
}
Anyway... as you can see, I've just tried using stub objects (no help), and I've also tried actually fetching and setting the objects. The error I get is:
Cannot insert the value NULL into column 'Name', table 'XXXX.dbo.Regions'; column does not allow nulls. INSERT fails. The statement has been terminated.
Obviously name is not a key field. I have checked the EDMX XML - only the Id (primary key columns) have StoreGeneratedPattern set to Identity, as they should (they are int32 identity columns). Not a single foreign key has StoreGeneratedPattern set to identity.
if I set Regions.Name to allow nulls, PracticeGroupCaches.Description throws the same error. It seems that every linked object gets set to null. I did have a look with the debugger, when I used the now commented out code, nothing was null and everything had a value. I even got the RegionRepository to return all of the regions, just to see if one of them somewhere had a null name. None did. There are only 2 in my test DB. Our object context is shared per HTTP request.
Please can anyone help. At this point I would settle for using the dirtiest workaround as long as it worked.
Regards,
Jonathan.

Look what happens when you call this line:
EventPracticeGroupRepository.Add(eventPracticeGroup);
You are adding a new eventPracticeGroup to the context. But eventPracticeGroup has the other related objects:
eventPracticeGroup -> PracticeGroupCache -> Region
And you create new objects for those:
var practiceGroupCache = new PracticeGroupCache() {
Id = eventPracticeGroup.PracticeGroupCacheId };
practiceGroupCache.Region = new Region() {
Id = eventPracticeGroup.PracticeGroupCache.RegionId };
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
When you add the eventPracticeGroup to the context this whole object graph gets added which means that EF considers all three objects as new which have to be added to the DB. Since you only fill the Id properties other string properties (like Name or Description) are null. Because they are not nullable in the database the INSERT command fails.
But I guess that you don't want to insert the related entities into the DB anyway but only the eventPracticeGroup. So you need to attach them to the context before you add the new object, something like:
var practiceGroupCache = new PracticeGroupCache() {
Id = eventPracticeGroup.PracticeGroupCacheId };
EventRepository.Repository.UnitOfWork.Context.AttachTo(
"PracticeGroupCaches",practiceGroupCache);
practiceGroupCache.Region = new Region() {
Id = eventPracticeGroup.PracticeGroupCache.RegionId };
EventRepository.Repository.UnitOfWork.Context.AttachTo(
"Regions",practiceGroupCache.Region);
eventPracticeGroup.PracticeGroupCache = practiceGroupCache;
EventPracticeGroupRepository.Add(eventPracticeGroup);
BTW as a side note: About this EventRepository.Repository.UnitOfWork.Context.XXX take a look at Ladislav Mrnka's answer here: EF 4.0 IsAttachedTo extension method and error An object with the same key already exists

Try to Add: [DatabaseGenerated(DatabaseGeneratedOption.None)]
On your Id field, Like:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }

It seems like you already found the problem and the solution. In your DB schema it seems like the columns don't allow NULL values. So either change all these columns to allow NULL or don't insert null (this is what you currently are trying to do)

Related

"The property $propertyName is part of the object's key information and cannot be modified." on navigation property with shared composite key

To preface this question: my problem is not because I'm directly setting a key property in my model entity object (which is the cause of the issue in other search results for the same exception message).
I'm making heavy use of composite keys in my application, here's a simplified version of my current DB schema (key fields in *asterisks*):
Tenants( *TenantId*, ... )
Categories( *TenantId*, *CategoryId*, ... )
Documents( *TenantId*, *DocumentId*, CategoryId, ... )
The Documents table has FK relationships with both Tenants and Categories, both using the same Documents.TenantId column. The Documents.CategoryId column is NULLable.
When I do something like this, I get the exception:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
doc.Category = newCategory; <-- exception is thrown on this line, without calling dbContext.SaveChanges() at all.
I believe the exception is because setting Category on the Document instance causes the TenantId property to be set indirectly by EF (because it's part of the Documents -> Categories FK association.
What is the solution?
Workaround Update
I'm able to hack it by creating the new Category entities then saving them, to get the IDENTITY values back, then setting the Document properties directly:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
dbContext.SaveChanges();
doc.CategoryId = newCategory.CategoryId
dbContext.SaveChanges();
But ideally I'd like this to work in a single call to SaveChanges() and using the Entity Model Navigation Properties instead of scalar attribute properties.
For this initial problem, I worked-around it using the "Workaround Update" I posted to my original posting.
However this problem happened again for a different entity type (again, with a composite key involved in a foreign-key) and I noticed that EF throws the exception even if you call dbContext.Entry() on any entity in the graph while the new entity is in the Added state - but it does not throw the exception again if you re-call Entry() or even SaveChanges(), and in fact it saves the new entities correctly in spite of the initial exception - so I'm thinking this might just be a bug in EF.
Here's essentially what I have now:
Tenant tenant = GetTenant( 123 );
Document doc = tenant.Documents.First();
Category newCategory = new Category();
newCategory.TenantId = 123;
dbContext.Categories.Add( newCategory );
doc.CategoryId = newCategory.CategoryId
try {
dbContext.Entry( doc );
}
catch(InvalidOperationException) {
}
dbContext.SaveChanges();
It's ugly, but works - and avoids having to call SaveChanges twice.

Update an entity but not the null value properties

I have a customer which I want to update in the repository.
var customer = new Customer{ Name = "Test" }
The customer has still more properties which are null, because I have not loaded them before to the client. Thus these properties have all their default values like null or 0.
What do I have to do with latest EF 6, that only the property Name is updated and the other properties from the customer are not overwritten?
1.) How would I have to query/update the customer?
2.) What would happen if the customer has a collection of and he has changed some properties of some meetings - but not all properties - how would then be the override behavior?
UPDATE pseudo code
Open context
Get customer
Close context
Open context
Update customer.name
SAveChanges
Close context
The custom.name is not saved, why?
You can't do it natively without getting the whole entiy first, which is obviously 2 db hits.
There is however an extension you can use that does what you want:
https://github.com/loresoft/EntityFramework.Extended/wiki/Batch-Update-and-Delete
Assuming you already have your model generated and it has a name of "MyEntities" and you should have a customer ID if you are updating an existing. Here is a solution using c#.
using(MyEntities db = new MyEntities()) {
//this will retreieve the customer based on ID
Customer cust = db.Customers.FirstOrDefault(c => c.CustomerID == custID);
//you can update each column
cust.Name = "Test";
//save the changes to the entity
db.SaveChanges();
}

how to insert parent child while doing entityframework.bulkinsert?

I am using Entityframework 6, I am trying to insert a parent-child kind of data in the database.
I am using Entityframework.BulkInsert to insert data. I have autoIncrement int primary key in all the tables
My object is as follows :
var parentObjects= new List<parentObject>();
var childObjects= new List<childObject>();
for (int i = 0; i <= 100; i++)
{
var parentObj= new parentObject()
{
Name="p1",
Address="a1"
};
childObjects= SeedInitializer.ChildItems.OrderBy(x => new Random().Next()).Take(2).ToList();//this gets 2 child objects
foreach (var childObj in childObjects)
{
childObj .ParentObject= parentObj;
//childObj .CommissionPlanId = i; //tried this still not working
parentObj.ChildObjects.Add(childObj );
}
parentObjects.Add(parentObj);
}
//when I do a quickwatch on parentObjects, i see child objects in each parentObject, but
//with the last id of parentObject
context.BulkInsert(parentObjects, 1000);
context.SaveChanges();
On save only 2 records are created in the childObject are created with a wrong parentObject id i.e. 0
I am not able to understand why child items are not getting created, while parent objects are getting created. Can someone help me understand where I am doing the mistake ?
Disclaimer: I'm the owner of EntityFramework.BulkInsert
You cannot.
This feature has never been implemented.
Disclaimer: I'm the owner of Entity Framework Extensions
However, this new library (not free), can easily handle this kind of scenario.
The BulkSaveChanges work exactly like SaveChanges (handle parent/child) but way faster!
All methods are supported:
Bulk SaveChanges
Bulk Insert
Bulk Delete
Bulk Update
Bulk Merge
Example
// Easy to use
context.BulkSaveChanges();
// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);
I do not think there is an easy way to accomplish this task, because in order to insert the children, you have to actually finish inserting the parents and get their ids. Normal EF inserts have the advantage that each INSERT will also embed a SELECT to fetch just generated identifier, so that it can use to push it for children (if any).
One possible solution is the following:
Add a Guid RefProperty to the ParentObject type which is also persisted
Add a Guid BatchId to the ParentObject type which is also persisted
Add a Guid RefProperty to the ChildObject type which is not persisted
Save the whole structure by using the following (mainly pseudocode) sequence
var batchId = new Guid();
parentObjects.ForEach(item => item.BatchId = batchId);
// set RefProperty for all parents and children to reflect proper parentation
TransactionScope scope = null;
try
{
context.BulkInsert(parentObjects, 1000);
var newParents = context.ParentObjects.Where(_ => _.BatchId = batchId);
var refPropMap = newParents.ToDictionary(_ => _.RefProperty, _ => ParentId);
var childObjects.ForEach(item => item.ParentId = refPropMap[item.RefProperty]);
context.BulkInsert(childObjects, 1000);
DataAccess.SaveChanges();
scope.Complete();
}
catch (Exception exc)
{
scope?.Dispose();
}
Note: this is not tested
This is quite ugly, but it should do the trick: minimize round-trips to SQL Server and still be one single transaction.
In order to make the SELECT faster, an index on ParentObject table should be placed on BatchId including (covering) its key.
Alternative: change design for these tables to not use auto-increments, but UNIQUEIDENTIFIER columns. This way, all identifiers can be set before making the inserts.

Entity Framework refuses to save value on new column

I just changed our database, adding a new column to a table and setting it as a foreign key. We're using EF4 Database first, so I updated the model from the DB, added my new field to the DTOs and everything looked great until I tried to save data to it.
The new column is called DiaryEventId, and because it's a FK there's also a virtual property on the object called DiaryEvent. Here's what the code looks like:
public void SaveDocument(Guid CaseId, string diaryText, string ActivityType)
{
Guid eventTypeId = RepositoryHelper.GetDiaryEventFromCache("Document Uploaded", _commonQueryContext);
//this wanders off and created a diary event object, adds it to
//the context and returns its ID
Guid diaryId = RepositoryHelper.AuditEvent(CaseId, diaryText, commonUpdateContext);
Entities.DocumentMetadata docData = new Entities.DocumentMetadata()
{
Id = Guid.NewGuid();,
ActivityType = activityType,
DiaryEventId = diaryId
};
_commonUpdateContext.DocumentMetadatas.Add(docData);
_commonUpdateContext.SaveChanges();
}
This compiles and runs fine, and when you step through it it appears to function as expected - diaryId is generated and set on the object. But in the DB, it always appears as null.
I've tried calling SaveChanges after adding the diary event (to make sure the key has something to connect to) and I've tried adding the actual DiaryEvent object to the virtual property rather than just the ID - both have the same effect.
When I watch what's going on in SQL Profiler, I can see that the INSERT statement simply ignores my new column completely - it's not listed in the insert columns at all.
Really got no idea what's going on. Any ideas?
Try modifying the object after adding it to the context, just as a test.
_commonUpdateContext.DocumentMetadatas.Add(docData);
Entities.DocumentMetadata docData = new Entities.DocumentMetadata()
{
Id = Guid.NewGuid();,
ActivityType = activityType,
DiaryEventId = diaryId
};
_commonUpdateContext.SaveChanges();
Or is this perhaps a typo?
Entities.DocumentMetadata docData = new Entities.DocumentMetadata()
{
Id = Guid.NewGuid();, <-------------typo?
Beyond that, it will require a bit of info on how you have your entities mapped out. Is there a navigation property between DiaryEvent and DocumentMetadata? Is the key defined as nullable (I'm assuming it is)? Is ActivityType getting persisted correctly?

Update a single property of a record in Entity Framework Code First

How can I update a single property of a record without retrieving it first?
I'm asking in the context of EF Code First 4.1
Says I have a class User, mapping to table Users in Database:
class User
{
public int Id {get;set;}
[Required]
public string Name {get;set;}
public DateTime LastActivity {get;set;}
...
}
Now I want to update LastActivity of a user. I have user id. I can easily do so by querying the user record, set new value to LastActivity, then call SaveChanges(). But this would result in a redundant query.
I work around by using Attach method. But because EF throws a validation exception on Name if it's null, I set Name to a random string (will not be updated back to DB). But this doesn't seem a elegant solution:
using (var entities = new MyEntities())
{
User u = new User {Id = id, Name="this wont be updated" };
entities.Users.Attach(u);
u.LastActivity = DateTime.Now;
entities.SaveChanges();
}
I would be very appriciate if someone can provide me a better solution. And forgive me for any mistake as this is the first time I've asked a question on SO.
This is a problem of validation implementation. The validation is able to validate only a whole entity. It doesn't validate only modified properties as expected. Because of that the validation should be turned off in scenarios where you want to use incomplete dummy objects:
using (var entities = new MyEntities())
{
entities.Configuration.ValidateOnSaveEnabled = false;
User u = new User {Id = id, LastActivity = DateTime.Now };
entities.Users.Attach(u);
entities.Entry(user).Property(u => u.LastActivity).IsModified = true;
entities.SaveChanges();
}
This is obviously a problem if you want to use the same context for update of dummy objects and for update of whole entities where the validation should be used. The validation take place in SaveChanges so you can't say which objects should be validated and which don't.
I'm actually dealing with this right now. What I decided to do was override the ValidateEntity method in the DB context.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = base.ValidateEntity(entityEntry, items);
var errors = new List<DbValidationError>();
foreach (var error in result.ValidationErrors)
{
if (entityEntry.Property(error.PropertyName).IsModified)
{
errors.Add(error);
}
}
return new DbEntityValidationResult(entityEntry, errors);
}
I'm sure there's some holes that can be poked in it, but it seemed better than the alternatives.
You can try a sort of hack:
context.Database.ExecuteSqlCommand("update [dbo].[Users] set [LastActivity] = #p1 where [Id] = #p2",
new System.Data.SqlClient.SqlParameter("p1", DateTime.Now),
new System.Data.SqlClient.SqlParameter("p2", id));