Entity Framework map model class to table at run time - c#

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.

Related

EF Core and DDD: Store ValueObjects in the same table as Entities, also use parametrized constructors to set property values on Entities

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.

Net Core: Generic Repository Primary Id Key Performance in Entity Framework

We are reviewing two different methods in generic repository patterns.
Currently, want to map primary keys to Ids. The purpose of this is to map to the Generic Repository Interface which utilizes Id. Two solutions are provided below.
What are performance implications of .FindPrimaryKey().Properties. Does it cause a schema lock on database table in trying to find the primary key? Does it cause any application slowness?
How does it compare in performance vs Partial Class Method Solution 2?
What option is better performance-wise?
Note: Architects demand the use of repository pattern at the workplace, so implementing it. Know there is debate surrounding this issue, but not my call.
Scaffolded Model Example:
namespace Datatest
{
public partial class Property
{
public int Property { get; set; }
public int DocumentId { get; set; }
public string Address { get; set; }
}
}
Sample Generic Base Repository for all tables:
public T Get(int id)
{
return Table.Find(id);
}
public async Task<T> GetAsync(int id)
{
return await Table.FindAsync(id);
}
public T Single(Expression<Func<T, bool>> predicate)
{
return All.Single(predicate);
}
public async Task<T> SingleAsync(Expression<Func<T, bool>> predicate)
{
return await All.SingleAsync(predicate);
}
public T FirstOrDefault(int id)
{
return All.FirstOrDefault(CreateEqualityExpressionForId(id));
}
Solution 1: FindPrimaryKey()
Generic Repository in C# Using Entity Framework
use EF FindPrimaryKey()
var idName = _context.Model.FindEntityType(typeof(TEntity))
.FindPrimaryKey().Properties.Single().Name;
Solution 2: Partial classes Mapping
Net Core: Create Generic Repository Interface Id Mapping for All Tables Auto Code Generation
public partial class Property: IEntity
{
[NotMapped]
public int Id { get => PropertyId; set => PropertyId = value; }
}
Regarding the first approach (using EF Core metadata services):
First, EF Core is ORM (Object Relational Mapper), with most important here is Mapper.
Second, it uses the so called code based model, which means all the mappings are provided by code and not the actual database (even though the model is created by reverse engineering of an existing database).
In simple words, EF Core creates at runtime a memory data structure containing the information (metadata) about classes and properties, and their mappings to database tables, columns and relationships. All that information is based on pure code model - the entity classes, conventions, data annotations and fluent configuration.
All EF Core runtime behaviors are based on that metadata model. EF Core uses it internally when building queries, mapping the query results to objects, linking navigation properties, generating create/update/delete commands and their order of execution, updating temporary FK property values after getting the real autogenerated principal key values etc.
Hence the metadata model and discovering services (methods) use optimized data structures and are (has to be) quite efficient. And again, no database operations are involved.
So the first approach is quite efficient. The performance impact of obtaining the PK property name via metadata service is negligible compared to actual query building, execution and materialization.
Also the performance of the first approach is similar to EF Core Find method which you are using in another method. Note that when calling Find method you just pass the PK value(s) and not the properties. So the method implementation should somehow know how to build the Where expression, right? And what it does internally is very similar to the suggested snippet.
Regarding the second approach:
It's simply not comparable because it doesn't work. It's possible to use base class/interface, but only if the actual property name is mapped - like all classes have Id property, and it's mapped to different column name in the database tables using [Column] data annotation or HasColumnName fluent API.
In your example, the Id property is [NotMapped] (ignored). Which means EF Core cannot map to the table column. The fact that your are mapping it to another property via code (property getter/setter) doesn't matter. EF Core is not a (de)compiler, it can't see your code, hence cannot translate a LINQ query using such properties to SQL.
Which in EF Core 2.x leads to either client evaluation (very inefficient, reading to whole table and applying the filter in memory), or exception if client evaluation is configured to do so. And in EF Core 3.0+ it will always be an exception.
So in case you don't remove properties like PropertyId and map the property Id (which would be hard with "database first" models), the second "approach" should be avoided. And even if you can map the actual Id property, all you'll save would be a few milliseconds. And again, when using Find you don't bother about performance, why bother with methods that uses the same (or similar) approach.

Difference between DbSet<T> property and Set<T>() function in EF Core?

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());

OnModelCreating only called once even for new contexts

I have multiple SQL server tables that are the same, but differ in content. In writing a code first EF6 program I am trying to reuse the same db context for each and pass in the table name to the context constructor.
However, while the constructor is being called every time, the OnModelCreating method is only being called once despite the db context being created from new every time. How do I reset this?
I have tried using AsNoTracking and I read along the lines of disabling ModelCaching but couldn't find out how to do this or whether this was the best approach. MSDN even says 'this caching can be disabled by setting the ModelCaching property on the given ModelBuidler[sic],' but it's not there.
This is my DB Context:
public partial class MissingContext : DbContext
{
private string tableName = "";
public MissingContext(string tableName) : base("name=MissingContext")
{
this.tableName = tableName;
}
public virtual DbSet<MissingData> MissingDataSet { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MissingData>()
.ToTable(this.tableName);
}
}
This is my code to use it:
List<MissingData> missingData=null;
string[] inputTables="TABLEA;TABLEB;TABLEC".Split(';');
foreach (string table in inputTables)
{
logger.Info($"Processing {table}");
missingData = null;
MissingContext db = new MissingContext(table);
var query = from d in db.MissingDataSet.AsNoTracking()
select d;
missingData = query.ToList();
}
In running, table always has the correct TABLEA, TABLEB, TABLEC and it is passed in to the db context constructor, however the OnModelCreating is only called once for the very first loop item so the SQL generated by the query object always selects from TABLEA:
SELECT
[Extent1].[id] AS [id],
[Extent1].[OrganisationName] AS [OrganisationName]
FROM [dbo].[**TABLEA**] AS [Extent1]
*apologies if any code looks wrong, I rename some variables as they are business sensitive.
OnModelCreating will be called only once that's default behaviour.
According to OnModelCreating documentation.
Typically, this method is called only once when the first instance of a derived context is created. The model for that context is then cached and is for all further instances of the context in the app domain. This caching can be disabled by setting the ModelCaching property on the given ModelBuidler, but note that this can seriously degrade performance. More control over caching is provided through use of the DbModelBuilder and DbContextFactory classes directly.
I think the issue here is that per-table is not how contexts are designed to be created. The design of Entity Framework is that (in the most general case), each table will have one class expressing any row in that table. For a different table, it is expected that another class will be defined. Then, you have your derived DbContext.
One DbContext may service arbitrarily many tables, by creating the appropriate DbSet for each of your table classes. If you desire them to work in different contexts for some reason, the design of EF would expect a different class derived from DbContext with it's own DbSets.
A caveat: If you're connecting to merely different databases with the same tables, meaning the columns exactly correspond etc., you can use the same DbContext, supplying the constructor with differing connection strings as appropriate.

Entity Framework not changing EnityState on Update

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.

Categories