DbContext only validate changed properties - entity-framework

I'm experimenting with updating properties on an entity without getting the entity first from the database.
The trouble is I only wish to update some properties and the entity validator complains that the non-nullable values have not been filled even though I'm not updating those.
Is my only option to turn of the validator?
I'd rather not turn of the validator, as I'd like to validate the properties I'm updating.
TestContext context = new TestContext();
LearningResource learningResource = new LearningResource();
learningResource.LearningResourceID = 132;
DbEntityEntry<LearningResource> entry = context.Entry(learningResource);
context.LearningResources.Attach(learningResource);
entry.State = EntityState.Unchanged;
learningResource.Title = "alex";
entry.Property(e => e.Title).IsModified = true;
//Only seems to work if I do this.
//context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();

That is "a feature". You must turn off global validation and validate every changed property separately.
var result = entry.Property(e => e.Title).GetValidationResult();
I also don't understand why this doesn't happen out of the box.

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.

How to update detached entities without providing all fields?

I want to update some entities without loading those into memory first. I know this should work:
var myEntity = new MyEntity
{
PkId = 123
};
myContext.Entry(myEntity).State = EntityState.Modified;
myEntity.ProcessTime = DateTime.Now;
myContext.SaveChanges();
But upon calling SaveChanges I am facing some DbEntityValidationException stating some fields are required. I worked with EF4 (ObjectContext), but this never happened. Also, it is stating only 3 required fields although there are 8+ required fields.
Tried this too before SaveChanges (no luck):
myContext.Entry(myEntity).Property(e=>e.ProcessTime).IsModified = true;
If I use _context.Configuration.ValidateOnSaveEnabled = false; then the SaveChanges does not throw exceptions, but worse; it updates the db-row with default clr values!
How can I do this?
I am using:
EF 6.1.3 (Database First)
Oracle Data Provider NET 12c Release 4
Visual Studio 2012
Windows 7 x64
Finally, I found this utterly disgusting (and wrong) way which worked:
var myEntity = new MyEntity
{
PkId = 123
};
// If you skip this, the validation error occurs again.
// If you keep it, you might be killed.
myContext.Configuration.ValidateOnSaveEnabled = false;
var dbEntry = myContext.Entry(myEntity);
// Let it think everything is just fine.
dbEntry.State = EntityState.UnChanged;
// If you have some fields already having updated value (before attaching),
// Inform it that it has been modified
// dbEntry.Property(r => r.YourProperty).IsModified = true;
// Change your field value.
// If you change a field, the state is already modified
// So, you don't need to tell it. Just change.
myEntity.ProcessTime = DateTime.Now;
// Call to save it and hope you did not forget to init any field value of any
// entity to be inserted in this context. If you did (mistakenly), it will remain
// silent and kill you later
myContext.SaveChanges();

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 == ...);

Entity Framework Update - Overwrite values or not

I am using Entity Framework 4.1 to perform CRUD operations against my database. I have turned off the following properties:
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.AutoDetectChangesEnabled = false;
My method to update a user object
public void Edit(User user)
{
_context.Entry(user).State = System.Data.EntityState.Modified;
_context.SaveChanges();
}
Ive retrieved:
User.Forename = Joe
User.Surname = Bloggs
Ive passed the user object to my edit method with
User.Forename = Joe
User.Surname = Bloggs
If I pass my user object to my Edit method but i haven't changed any of its properties, as above. Will the properties be over written in the database with the same value or will Entity Framework know the value hasn't changed?
Since you explicitly set the state to Modified, EF does send an update statement to the database even if none of the property values have changed.
If you don't want EF to update the database with the same values, you'll have to add logic to track whether the values have changed since you are setting AutoDetectChangesEnabled to false.