I have a working Update method , which is simply changing a property value and calls SaveChanges() on db context:
public void Update(int id, string name)
{
var entity = context.Entities.Single(x => x.Id == id);
entity.Name = name;
context.SaveChanges();
}
this way changes do indeed get applied , however the EnityState remains "Unchanged". Any thoughts as to why? I am trying to avoid having to tell EF what's happening explicitly e.g. using context.Entry(entity).State = EntityState.Modified;
the problem is I am using the state in the overriden SaveChanges method:
public override int SaveChanges()
{
var context = ((IObjectContextAdapter)this).ObjectContext;
var objectStateEntries =
context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified);
...
return base.SaveChanges();
}
..when debugging, i can see that the state of my entity is Unchanged.
If you haven't disabled the change tracking of EF or proxy creation, then you shouldn't have problem with that update. EF by default tracks automatically changes when you entities meet the requirements you can find in this msdn page. If you meet those requirements and check later the type of you entity once is returned by Single extension method you will see that is a proxy class, not your real class. So, first check if you're meeting all those requirements that EF needs to track your changes automatically, you'll be fine with that code.
For either of these proxies to be created:
A custom data class must be declared with public access.
A custom data class must not be sealed
A custom data class must not be abstract .
A custom data class must have a public or protected constructor that
does not have parameters. Use a protected constructor without
parameters if you want the CreateObject method to be used to create a
proxy for the POCO entity. Calling the CreateObject method does not
guarantee the creation of the proxy: the POCO class must follow the
other requirements that are described in this topic.
The class cannot implement the IEntityWithChangeTracker or
IEntityWithRelationships interfaces because the proxy classes
implement these interfaces.
The ProxyCreationEnabled option must be set to true.
For change tracking proxies:
Each property that is mapped to a property of an entity type in the
data model must have non-sealed,
public, and virtual get and set
accessors.
A navigation property that represents the "many" end of a
relationship must return a type that implements ICollection, where T
is the type of the object at the other end of the relationship.
If you want the proxy type to be created along with your object, use
the CreateObject method on the ObjectContext when creating a new
object, instead of the new operator.
Related
Consider these simple classes. They belong to a simple application with Domain Driven Design (DDD) principles, and as such every Entity and ValueObject receives its property values through the constructor while hiding the default, parameter-less constructor. Properties will also be read-only.
public class MyClass
{
public Guid Id {get;}
public ValueObject ValueObject1 {get;}
public ValueObject ValueObject2 {get;}
public MyClass(ValueObject valueObject1, ValueObject valueObject2)
{
ValueObject1 = valueObject1;
ValueObject2 = valueObject2;
}
private MyClass(){}
}
public class ValueObject
{
public string Value {get;}
public ValueObject(string value)
{
Value = value;
}
private ValueObject(){}
}
I want to be able to create a database based on this model, using EntityFramework Core 2.2.6.
Apparently EF Core 2.2.6 can automatically feed property values for these classes through their parametrized constructors, as long as constructor parameters and class properties have the same name (case-insensitive). Great.
Now I want the ValueObjects to be stored in the same table as the MyClass. To make that happen, I am told, I should use modelBuilder.OwnsOne<> in OnModelCreating of the DBContext, instead of modelBuilder.Property<>
The DBContext configuration in OnModelCreating would look like something this:
modelBuilder.Entity<MyClass>(b => b.HasKey(mc => mc.Id));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject1,rb =>
{
rb.Property(vo => vo.Value);
}));
modelBuilder.Entity<MyClass>(b => b.OwnsOne(mc => mc.ValueObject2, rb =>
{
rb.Property(vo => vo.Value);
}));
Now it seems modelBuilder.OwnsOne<> and modelBuilder.Property<> are mutually exclusive, meaning you can't use them both together because every time I try to Add-Migration with both of them I get:
'ValueObject' cannot be used as a property on entity type 'MyClass' because it is configured as a navigation.
But if I don't use modelBuilder.Property<> and only use modelBuilder.OwnsOne<>, I get:
No suitable constructor found for entity type 'MyClass'. The following
constructors had parameters that could not be bound to properties of
the entity type: cannot bind 'valueObject1', 'valueObject2' in
'MyClass(ValueObject valueObject1, ValueObject valueObject2)'.
Which means the constructor to property binding pattern only works only if I use modelBuilder.Property<> to configure the properties on MyClass.
So my question is: how should I configure the DBContext to allow EF Core to both set property values through the parametrized constructor, and store ValueObjects in the same table as the Entity?
So here is what happened.
As #Gert Arnold pointed out:
1. You need to have private setters on all properties of your domain models. EF Core can't work with read-only properties as of version 2.2.6.
But that was not my problem. It turned out I had forgotten to include a private constructor on the equivalent of MyClass in my own project. I just wish I had seen #Ivan Stoev's comment before I spent hours of work and figured it out. The error message that EF Core gave me was too cryptic, and didn't point out the issue:
No suitable constructor found for entity type 'MyClass'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'valueObject1', 'valueObject2' in 'MyClass(ValueObject valueObject1, ValueObject valueObject2)'.
When in reality, there is no problem with that particular constructor.
2. You just have to have a private, parameter-less constructor if you want EF Core to properly use constructor binding and feed values to your properties through constructor parameters.
This is not the case. EF Core simply can't inject entities into other entities using constructor binding.
It is basically telling us that particular constructor can't be used, and because it can't find a suitable constructor to use, at all, by providing a parameter-less constructor you are giving it a way to create objects without constructor binding.
3. You should use modelBuilder.OwnsOne<> in your DbContext.OnModelCreating and NOT modelBuilder.Property<> to configure Value Objects for an Entity (in DDD) to be stored in the same database table as the Entity.
I think EF Core needs to give you a clearer message about how it is confused as to which constructor it should use when you don't have a private, parameter-less constructor. I'll bring it up with the EF Core team.
Given this kind of context:
public class FooContext : DbContext
{
public FooContext(DbContextOptions<FooContext> opts) : base(opts)
{ }
public DbSet<Bar> Bars { get; set; }
}
I can get to a Bar in two ways:
fooContext.Bars.Add(new Bar()); // Approach 1
or
fooContext.Set<Bar>().Add(new Bar()); // Approach 2
What is the difference between the two approaches?
I've tried to answer my own question by:
Inspecting the intellisense for both (only tells me that Set<T>() also creates a DbSet<T>)
Googling for "EF Core Set vs property" but that doesn't seem to be the 'right' query
Google for DbSet<T> specifically on the docs urls but no relevant results here either it seems
Reading the intro of the DbSet<T> docs which just suggests that you can get a set through either of the two methods (not if there is or isn't a difference)
Read the Set<T>() docs which has no relevant info
But I could not find any good explanation about which of the two is used for which purpose. What is the difference? Or perhaps more importantly: where and how should I be able to find this in the docs?
They do exactly the same thing. The real question is when will you use one over the other.
You use DbSet when you know the type of entity you want to play with. You simple write the DbContext name then the entity type name and you can create, read, update or delete entries for this entity with the entity methods available. You know what you want and you know where to do it.
You use Set when you don't know the entity type you want to play with. Lets say, you wanted to build a class that does your repository functions for creating, reading, updating and deleting entries for an entity. You want this class to be reusable so that you can just pass a DbContext on it and it will use the same create, read, update and delete methods. You don't know for sure what DbContext it will be used on or what DbSet the DbContext will have. Here's when you use generics so that your class can be used by any DbContext for any DbSet.
Here's an example of a class you can use for creating any entity on any DbSet in any DbContext
public class Repository<TDbContext> where TDbContext : DbContext
{
private TDbContext _context { get; }
public Repository(TDbContext context)
{
_context = context;
}
public TEntity Create<TEntity>(TEntity entity) where TEntity : class
{
if(entity != null)
{
var dataSet = _context.Set<TEntity>();
if(entity is IEnumerable)
{
dataSet.AddRange(entity);
}
else
{
dataSet.Add(entity);
}
_context.SaveChanges();
}
return entity;
}
}
And this is how to use it.
var dbContext01 = new DbContext01();
var dbContext02 = new DbContext02();
var repository01 = new Repository<DbContext01>(dbContext01);
var repository02 = new Repository<DbContext02>(dbContext02);
repository01.Create(new EntityOnDbContext01 {
Property01A = "String",
Property01B = "String"
});
repository02.Create(new EntityOnDbContext02 {
Property02A = 12345,
Property02B = 12345
});
Here's a link if you want to know more about generics. Its super awesome.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/
Unfortunately currently you won't find explanation in the official documentation, mainly because all these are functionally equivalent.
First, the generic methods of DbConext like Add<TEntity>, Remove<TEntity>, Attach<TEntity> etc. a fully equivalent of the corresponding DbSet<TEntity> methods (actually currently they are the implementation of the later, i.e. DbSet methods simply call the corresponding DbContext generic method). Which one you use is just a matter of taste.
Second, DbSet<TEntity> property and Set<TEntity> method are functionally equivalent, but do have some non functional differences.
The DbSet properties are populated once at the context creation, while Set method always performs a lookup, so DbSet property access should be faster than Set method (although not significant).
The important difference is actually the EF Core Including & Excluding Types convention:
By convention, types that are exposed in DbSet properties on your context are included in your model. In addition, types that are mentioned in the OnModelCreating method are also included.
So while you can keep your DbContext without exposed DbSet properties and work just with Set method, if you do so you have to tell explicitly EF Core which are your entity types by adding in OnModelCreating a call to modelBuilder.Entity<TEntity>(); for each entity type (this is what the documentation does mean by types that are mentioned in the OnModelCreating method).
They are the same and actually returns the same DbSet instance.
var options = //...;
using (var ctx = new FooContext(options))
{
// true
bool isSame = ReferenceEquals(ctx.Bars, ctx.Set<Bar>());
}
One use case for not including a DbSet property in your DbContext is when you want to hide an entity type from a consumer. (e.g. an entity that acts as join table for many-to-many relationship). You can then mark the entity as internal class so consumers also can't also access it using Set<>.
Also, if you don't expose a DbSet property, you need to explicitly configure the entity or you'll get the following exception:
//throws System.InvalidOperationException: 'The entity type 'Foo' was not found. Ensure that the entity type has been added to the model.'
ctx.Set<Foo>().Add(new Foo());
In a project using ASP.NET Core 2.0 and Entity Framework, I'm trying to map a known table schema (coded into class MyTableClass) to an unknown table name. This table name is given by the user at run time, so this is done outside of the OnModelCreating method of the Context class. Is there a way to do something like the following pseudocode:
void OnUserEnteredTableNameFromUI(string tableName)
{
var modelBuilder = new ModelBuilder(???); // how?
modelBuilder.Entity<MyTableClass>().ToTable(tableName);
// how to get a ref to DbSet<MyTableClass> myTable from here?
}
Since this is an interesting issue which might help other people that need some dynamic model building, here is how it can be implemented.
Let say we have a custom context with custom table name provided via constructor (as Gert Arnold suggested in the other answer):
public class CustomDbContext : DbContext
{
// …
private string customTableName;
public string CustomTableName => customTableName ?? "DefaultCustomTableName";
}
and we use it inside the OnModelCreating (it should be there, currently there is no other simple way to create model using the predefined convention sets):
modelBuilder.Entity<CustomEntity>().ToTable(CustomTableName);
The only problem is that by default the OnModelCreating is called just once per context type and is cached. Luckily EF Core is built on top of a (replaceable) services architecture. The service interface responsible for model caching is IModelCacheKeyFactory:
Creates keys that uniquely identifies the model for a given context. This is used to store and lookup a cached model for a given context.
It has a single method
object Create(DbContext context)
The returned object GetHashCode / Equals methods are used to identify the passed context instance. The default EF Core service implementation returns an object which compares the type of the context.
In order to make the custom context model working, we need to replace it with a custom service which also compares the custom state (CustomTableName in our case). The implementation could be like this (using C#7.0 value tuples):
class CustomModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create(DbContext context) => new CustomModelCacheKey(context);
}
class CustomModelCacheKey
{
(Type ContextType, string CustomTableName) key;
public CustomModelCacheKey(DbContext context)
{
key.ContextType = context.GetType();
key.CustomTableName = (context as CustomDbContext)?.CustomTableName;
}
public override int GetHashCode() => key.GetHashCode();
public override bool Equals(object obj) => obj is CustomModelCacheKey other && key.Equals(other.key);
}
The only thing remaining is to replace the existing service with the custom. It can be done inside OnConfiguring override:
optionsBuilder.ReplaceService<IModelCacheKeyFactory, CustomModelCacheKeyFactory>();
And that's all. Anytime you create context with different CustomTableName, EF Core will create a new model and map the CustomEntity to that table.
The same technique can be applied to any context containing custom model affecting state by including all custom state in CustomModelCacheKey.key tuple. Of course it could be implemented w/o value tuples, just with them the GetHashCode and Equals overrides are easier to implement. Actually instead of CustomModelCacheKey the custom service can return directly value tuple containing the context type and custom state member values.
I've seen situations where databases with identical structure but varying table names had been deployed to several sites. In that case, EF only needs the know the table name(s) at application startup.
This can be done by adding a constructor parameter to the context:
private readonly string _userDefinedTableName;
public MyContext(string userDefinedTableName)
{
_userDefinedTableName = userDefinedTableName;
}
Then, in OnModelCreating:
modelBuilder.Entity<MyTableClass>().ToTable(_userDefinedTableName);
However, in your case the name has to change any number of times at runtime. With Entity Framework, that's impossible (well, more exactly, too impractical to really contemplate it). EF compiles and stores model once per context class, because it would be too expensive to do all that for each context instantiation.
That means that OnModelCreating runs not more than once in an application and the first table name remains.
You'll have to find other ways to address table data dynamically, or change the design so the multiple tables can be converted into one fixed table.
Say I have an User entity and it haves a Password property which is not nullable:
Map((x) => x.Password).Column("PASSWORD").Not.Nullable();
In the create action, I manually set the Password value as it is a generated hash. It never goes to the View.
In the update, I try to save it, but I don't have the Password value. I get this error for Password propery:
PropertyValueException: not-null property references a null or transient value
This is my Update method:
public bool Update(UserViewModel input)
{
if (!IsValid(input))
return false;
var user = Mapper.Map<User>(input);
this.UserRepository.Update(user); // <- this is a wrapper for NH's Session.Update()
return true;
}
How can I tell NHibernate to ignore a property in an update?
Note: This is not the same as this question.
Update:
Here is how I use it: The Password property never goes to any View. Even in the Login action I have a generic LoginViewModel, only for it's view. That property is only used in the login process and it could be updated in the Reset password feature, where a new password is generated and sent to the related user e-mail.
I see 2 possibilities to achieve that
Get the entity before Update and update explicitly
// use 'Get()' because it uses the NHibernate cache
// if you already loaded the entity, it won't query the db and read it from the cache
var user = this.UserRepository.Get(input.Id);
user.PropertyToUpdate = ...;
this.UserRepository.Update(user);
In addition to that, you can use Dynamic-Update. But this will only work with entities that are bound to the Session. NHibernate will then only update the changed properties and not all while you are updating a entity. Otherwise NHibernate can't know which properties has changed and will update all. DynamicUpdate should only work when you got the entity from NHibernate. The Entity is then bound to the Context and NHibernate can track changes.
If all your entities are auto mapped you can use a ClassConvention to set DynamicUpdate to all your entities (or just filter the ones you want):
public class ClassConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.DynamicUpdate();
}
}
As another option you can use a explicit mapping override:
public class UserOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.DynamicUpdate();
}
}
Use different classes for different behaviours
You can declare different classes for the same Entity. One class for User creation or password resetting that contains the password property. And one class for simple updates that don't need the password property. FluentNhibernate allows you to map different classes for the same table. But you need a little more effort in mapping or rather in AutoMappingOverrides.
I'm new to inheritance with EF in C#/MVC, and I'm trying to update a subclass's navigation property but can't figure out how to do so. The subclass inherits from the parent class, obviously, and it has its own EF mapping specified. I have to update the DB using the parent type, otherwise nothing is pushed to the DB (no error is thrown though). Using the parent class, however, doesn't expose the child properties so I can't update them to even push to the DB.
What is the proper way to handle subclass-specific properties in EF?
This is more of a general question than one specific to my issue, but here's the code that doesn't work.
// get the existing task using the model posted from view,
// convert it to child class (GetTaskById returns parent class)
PETask task = _projectService.GetTaskById(model.Id).ConvertToET();
var proj = _projectService.GetById(model.ProjectId);
// error checking
if (ModelState.IsValid)
{
// use AutoMapper to convert the posted data to entity, passing task as
// a parameter to preserve proxies
task = model.ToEntity(task);
if (model.SOId.HasValue)
{
SalesOrder so = _salesOrderService.GetByOrderId(model.SOId.Value);
task.SalesOrderId = so.Id;
task.SalesOrder = so;
}
_projectService.Update(task);
// all information is correct, including a proxy for the SalesOrder property
// and the actual SalesOrderId, but the DB is not updated. no error is thrown.
}