BACKGROUND:
Our core framework loads all entity framework mappings from itself, the main application and any modules we have installed by using an interface (below):
public interface IEntityTypeConfiguration : IDependency
{
}
and we have a DbContext in our core framework like this, which loads all the mappings:
public class DefaultDbContext : DbContextBase
{
private readonly Lazy<IEnumerable<IEntityTypeConfiguration>> configurations;
public DefaultDbContext(Lazy<IEnumerable<IEntityTypeConfiguration>> configurations)
: base()
{
this.configurations = configurations;
Configuration.ProxyCreationEnabled = false;
}
public DefaultDbContext(string connectionString, Lazy<IEnumerable<IEntityTypeConfiguration>> configurations)
: base(connectionString)
{
this.configurations = configurations;
Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
foreach (dynamic typeConfiguration in configurations.Value)
{
modelBuilder.Configurations.Add(typeConfiguration);
}
Database.SetInitializer(new CreateTablesIfNotExist<DefaultDbContext>());
}
}
So this way we have a single DbContext for everything.
PROBLEM:
We've run into an issue whereby when we dynamically add new modules (which have their own mappings), then EF does not load those mappings ever, even when we are sure that a new instance of DefaultDbContext has been created. So, it must be that EF is cacheing the mappings somewhere. Is there some way to clear the cache?
FINAL NOTE:
As you may have guessed, we are using an IoC, namely Autofac. If you need any further info, just ask.
Any ideas, anyone?
The model is cached for performance reasons.
The following excerpt explains what is going on
Model Caching
There is some cost involved in discovering the model, processing Data Annotations and applying fluent API configuration. To avoid incurring this cost every time a derived DbContext is instantiated the model is cached during the first initialization. The cached model is then re-used each time the same derived context is constructed in the same AppDomain.
This text also mentions a property called CacheForContextType but this didn't make it into the final release of EF5.
This second link provides a glimmer of hope but again is dated before the final release of EF5
We removed CacheForContextType in CTP5, we originally intended it to be used when folks wanted to use the same context in the same AppDomain with different models. The issue is that it would create the model on every initialization and didn't allow any way to cache a series of models and choose which one to use during each initialization. Model creation is expensive so we wanted to promote a better pattern.
The pattern we recommend is to externally create a ModelBuilder -> DbDatabaseMapping -> DbModel for each model you want to use. The DbModel should be cached and used to create context instances. The ModelBuilder -> DbModel workflow is a little messy and the class names aren't great, they will be tidied up for RTM.
Personally I think you're going to have to find a way of knowing all of your models up front ...
Solved! We found this constructor on the DbContext class:
public DbContext(string nameOrConnectionString, DbCompiledModel model);
I can't share all of the code here, but basically we're creating a new DbCompiledModel and passing that in whenever necessary.
Related
I am working on a .NET Core project using entity framework core (2.0.1).
I have a plugin architecture requiring different models to be loaded based on the plugins attached, so trying to dynamically load the models when adding the dbcontext to the DI container using the DbContextOptionsBuilder.UseModel method.
Regardless on if I define a new Model or a ModelBuilder, the EntityTypes I am adding do not appear to be doing so correctly and once added to the dbcontext do not appear to have any properties.
I am not sure if there is a convention I need to be adding for this to work, or what I may be missing. Any help would be appreciated.
_services.AddDbContext<DataContext>(options =>
{
switch (_config.DatabaseType.ToLower())
{
case "postgres":
options.UseNpgsql(_config.ConnectionString);
break;
case "sqlserver":
options.UseSqlServer(_config.ConnectionString);
break;
}
var convention = new Microsoft.EntityFrameworkCore.Metadata.Conventions.ConventionSet();
var mb = new ModelBuilder(convention);
foreach (var definition in definitionList)
{
mb.Entity(definition.Type);
}
options.UseModel(mb.Model);
});
Resolves into:
Correctly creates the 5 EntityTypes, but then once I expand one of the Entity Types it has no properties.
If I do the same using the standard convention of adding the EntityType via the override of the dbcontext onmodelbuilding, the properties are all there correctly...
Yes, there are a few conventions by default, one of which is a PropertyDiscoveryConvention which sounds like it would make sure that all properties are added by default, but also other likely important ones like a KeyDiscoveryConvention, and lots of conventions that take care of identifying the mapping attributes.
You could probably ensure that you create the right conventions but it may be easier to move your logic into the database context instead. That way you can build on top of all the standard conventions, like one would normally do, and you won’t need to take care of that yourself. So maybe something like this:
class MyContext : DbContext
{
private readonly IDbContextConfigurator _dbContextConfigurator;
public MyContext(DbOptions<MyContext> dbOptions, IDbContextConfigurator dbContextConfigurator)
: base(dbOptions)
{
_dbContextConfigurator = dbContextConfigurator;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
_dbContextConfigurator.Configure(modelBuilder);
}
}
So inside that IDbContextConfigurator you could then retrieve your configuration and configure your model directly on the model builder.
In our Web.API project, we use Entity Framework 6. We have a DataModel class, which contains the DatabaseContext. The DataModel is a [ThreadStatic] Singleton.
Like this:
public class DataModel
{
[ThreadStatic] private static DataModel _instance;
public static DataModel Instance => _instance ?? (_instance = new DataModel());
public DatabaseContext Context { get; private set; }
private DataModel()
{
Context = NewContext();
}
}
Now in one of our (partial) model classes, we use the context like this:
public partial class Job
{
public User CreatedByUser
{
get { return DataModel.Instance.Context.Users.FirstOrDefault(d => d.Username == CreatedBy); }
}
}
We search in another table in the database the corresponding user. This works, but in my opinion, this is not a beautiful solution. Especially if we plan to migrate our project to.NET Core and use dependency injection for the database context.
My Question is, is there a pattern, which solves the problem more elegant? Dependency injection won't work here because Entity Framework generates the model objects. Or would it be better, if we move this code from the partial class to e.g. a util class? But how can we inject there the context?
You generally want to avoid requiring your models to have awareness of their creation and usage context since that knowledge should be top down and not bottom up. Among other reasons, you'll run into design issues like you are experiencing now. You can try to design your way around it at the application level with extension methods, an inversion of control framework, utility methods, pocos, etc, but at the end of the day you are trying to solve a problem that only exists because your underlying database schema is inadequately designed for your intended usage.
For example, your Job table has a reference to the username of the user that created it. Why? It might be possible for usernames to change and any time you want additional critical properties of that user you will need to perform a secondary lookup (like you're doing in your partial class). If you instead have the Job table maintain a foreign key to the User table, you can just use a navigation property on the C# side to get the full user object as long as the appropriate related entities are included in the query. You wouldn't even need the partial class and your database schema would become more maintainable as an added bonus.
Sometimes it's best to simplify the database design to simplify the code.
I'm creating a new UWP application using Mvvm Light and Entity Framework Core. I'm new to these 2 technologies.
I create my model : the Article class is a simple ObservableObject with 3 properties : Id, Référence and Désignation.
My DbContext is the following :
public class UniversalTest1Context : DbContext
{
public DbSet<Article> Articles { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=UniversalTest1.db");
}
}
I'm looking for the best way to manage the DbContext and my different views.
Is it better to create one DbContext for the whole application ? I don't really like that idea.
Do I have to create a DbContext in each ViewModel ? I like this more.
When the user double tap an item in the article list view, I navigate to the article detail view and pass the article to the view model associated to the article detail view. But this already existing article is not related to the DbContext of the Article detail view model.
Is it possible to instantiate the DbContext only when needed ? My prefered option.
For this, I pass the article from the list view model to the detail view model. And then, when the user clicks save I execute something like this :
using (var db = new UniversalTest1Context())
{
db.Articles.Add(article);
await db.SaveChangesAsync();
}
Of course, this works for new articles (insert), but not for existing ones (update).
I have difficulties making up my mind here.
Many thanks in advance,
Julien
In my opinion, you should hide all storing operations by interface (like IArticleRepo or smth like that) and you should use IoC for accessing it (if some class wants to work with Store, it have to declare IArticleRepo in ctor parameters). Also, inside this interface IArticleRepo you can delcare neecessarry operations with Articles, like IArticleRepo.AddArticle(Article a). If you do so, you can choose later, whether you want to create dbcontext for each operation or not OR maybe you want to make IArticleRepo implementation as singletone using IoC container. But such decisions should not affect on other code.
As very simle sample in my code (I showing only this file, whole project is ugly and I will change it) https://github.com/tym32167/arma3beclient/blob/master/src/Arma3BE.Client.Libs/Repositories/PlayerRepository.cs#L23
Im unable to scaffold a controller (MVC5 Controller with views, using Entity Framework) in Visual studio 2013 (update 3 and 4). The error message is below:
There was an error running the selected code generator:
A configuration for type 'Library.Morthwind.Models.Catgeory' has already been added. To reference the existing configuration use the Entity<T>() or ComplexType<T>() methods
I have created the models by selecting 'Reverse Engineer Code First' from the 'Entity Framework Power Tools Beta 4' Tool menu.
Any ideas about what might cause this error?
I had the same issue today.
I had added some custom configuration for one of my Model classes to add a relationship using the fluent API. This was specified in my dbContext class in the OnModelCreating override using the following:
modelBuilder.Configurations.Add(new OrderConfiguration());
Commenting out the above line allowed the Controller scaffolding to run as expected.
VS 2013 update 2 had a problem with this and the scaffolding came up with an unhelpful error with no further information. In installed Update 3 and it gave just enough detail to track down the underlying issue.
Jeff.
I've been having this issue too. Jeff's solution works in some cases. But as my DbContext grew with more models that had keys needing to be mapped I couldn't delete the Configurations.Add() methods because I would then get errors that EF couldn't find primary keys, etc...
I did discover that by changing my DBContext derived class to use IDbSet properties instead of DbSet I could generate the controllers and views just fine. However, for me this introduced another issue, IDbSet does not support the async methods.
It appears I can either generate non-async controllers with configurations in place or async methods without configuration classes.
If your context properties are of type DbSet, try changing them to IDbSet. If you aren't generating async controller methods this may work for you.
I also had the same issue and changing my context class to use IDbSet allowed me to successfully use the scaffolding to create a new controller. But since I didn't want to give up the async methods I changed the context back to use DbSet and that worked for me.
Here is how I resolved this issue.
I have Visual Studio 2013 Update 4.
I use EF Power Tools.
Comment out all of the DbSet < ... >
Inside OnModelCreating, only leave the object you are scaffolding:
modelBuilder.Configurations.Add(new SomeTableDataMap());
At the bottom of my context class I did notice this got created:
public System.Data.Entity.DbSet SomeTableDatas{ get; set; }
Oh: I also put this in my constructor but it's for something else,
this.Configuration.LazyLoadingEnabled = false;
Seriously, this worked today, I've tried all of these solutions nothing worked for Update 4. I had this working in Update 2 and Update 3 using previous solutions. This is the most up-to-date solution for now.
Simple workaround that worked for me (after trying many other solutions suggested here and other places in vain...).
In the scaffolding dialog I just added a new DataContext e.g. TempContext. All the scaffolding worked as expected and then I could simply move the generated code in TempContext into my original DbContext and renamed the TempContext in the generated controllers to the original DbContext.
There are already multiple workarounds posted for this issue. And in this comment, I will try to provide the underlying issue why this may be failing. [With the hope to make people aware of the root cause]
Let's say, you have a DbContext (to be specific, child class of DbContext) in your application, and you are trying to use a model class (let's say Model) and the DbContext and scaffolding controllers / views.
I am guessing that the DbContext did not have a "DbSet< Model > Models {get; set;}" property on it but the DbSet was nevertheless added to the DbContext using code in OnModelCreating method.
In the above case, scaffolding first tries to detect DbSet property on DbContext (by reflection only - so that does not detect if OnModelCreating has code to add the DbSet) and given it's not, scaffolding adds a DbSet property to the DbContext and then tries to scaffold using that DbContext , however when running the scaffolding, we create an instance of DbContext and we also call OnModelCreating , and at that point, scaffolding fails because there are multiple DbSet types with the same model in the DbContext (one added by scaffolding and one configured in code in OnModelCreating).
[This happens not only for the model being used but also for related models in that model , scaffolding adds DbSet properties for all related models]
[Also, one doesn't see the added DbSet's after the scaffolding is done because scaffolding rolls back any changes if the operation did not complete successfully, like Jeff mentioned , the error message was poor initially and was improved to give some hint to the user but it's still not super clear what's going on]
This is a bug in scaffolding , a simple work around would be to use DbSet property on DbContext for all related models of your model class instead of configuring them in OnModelCreating.
I was getting a different error when trying to scaffold a controller with CRUD actions and views. In my case it was saying:
"There was an error running the selected code generator. Object instance
not set to an instance of the object."
The problem was hard to find: I created a table in SQL Server but forgot to set the Primary Key for the table. Setting the Primary key and updating Entity Framework's .edmx file solved the problem.
Hope it helps.
None of the rest of the answers worked for me. What I found out was that the issue only happened when scaffolding and adding Configurations using the Fluent API. So what I did was, instead of having separated files, each one having an Entity Configuration like this:
public class ApplicationUserMapConfiguration : EntityTypeConfiguration<ApplicationUserMap>
{
public ApplicationUserMapConfiguration()
{
ToTable("ApplicationUserMap", "Users");
HasKey(c => c.Id);
}
}
And then adding this configuration to the DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationUserMapConfiguration());
}
I just added the whole configuration inside the DbContext for every entity:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//ApplicationUser
modelBuilder.Entity<ApplicationUser>().HasKey(c => c.Id);
modelBuilder.Entity<ApplicationUser>().ToTable("ApplicationUser", "Usuario");
//Other entities...
}
Now I can scaffold perfectly. I already submited and issue on the Mvc GitHub.
Also, if another error message raises up saying:
There was an error running the selected code generator: ‘Exception has been thrown by the target of an invocation.’
You should modify your DbContext constructor to:
public YourDbContext() : base("YourDbContext", throwIfV1Schema: false) { }
None of answers of this post worked for me. I handled this issue creating new context class through plus button in Add Controller scaffolding dialog. Once VS created controller and views, I just remove the created context class and change the the generated controller code to use my existing context class.
Important: This process will add a new connection string for the new context, dont forget to remove it as well.
mine got fixed like this:
public virtual DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//--> EntityTypeConfiguration<Your Model Configuration>
modelBuilder.Configurations.Add(new EntityTypeConfiguration<CategoryMapping>());
base.OnModelCreating(modelBuilder);
}
DOn't forget Ctrl + Shift + B so your code compile (i'm not sure for single solution, but since mine is in another project in same solution, it should get compiled first)
I solved it by adding a try/catch on the code to OnModelCreating function inside the context class.
Just keep the
base.OnModelCreating(modelBuilder);
outside your try/catch
I have this code in my Controller:
public class MyController : Controller
{
private readonly IMyRepository myRepository;
public MyController() : this(new MyRepository())
{}
public MyController(IMyRepository myRepository)
{
this.myRepository = myRepository;
}
public ActionResult Index()
{
return View(myRepository.GetData());
}
}
MyRepository uses EF for data operations. Every time user loads this page instance of MyRepository is creating. That means EF context is creating and Fluent API code is executing (OnModelCreating method).
Are there any possibilities not to create EF context everytime when user loads the page?
MyRepository uses EF for data operations. Every time user loads this
page instance of MyRepository is creating. That means EF context is
creating and Fluent API code is executing (OnModelCreating method).
You're wrong. Put a breakpoint on your OnModelCreating method. This method will only get hit once, when your application loads. It will hit the breakpoint again if you rebuild the project, because this causes the binary dll to be reloaded into the app domain. However if you leave the application running and hit the controller action twice (without rebuilding in between), you will see that OnModelCreating does NOT get hit the second time. Like Serg Rogovtsev says, EF caches the model (meaning the schema) after it is created during OnModelCreating.
The only objection I have to Serg Rogovtsev's answer is that I would never create a singleton instance of the DbContext. Instead you should use one instance per HttpContext (a.k.a. per web request). If you use it as a singleton, and you have concurrency enabled, you would end up seeing DbConcurrencyExceptions creep up in your app. Use DI/IoC, and create/dispose the DbContext instance at the beginning/end of the request response cycle. That is the best practice. Don't worry about the overhead of creating a new MyDbContext() instance. After EF initializes (warms up) during the first construction, future constructions will be fairly cheap.
To answer your question: you can create a singleton of your repository or you can use DI container which will hold single instance for you.
But to the point: if you set breakpoint inside OnModelCreating you will find that it gets called only once per application instance. EntityFramework uses pretty effective model caching. So you don't have to worry about performance hit caused by creation of EF contexts.
Change your controller so you create an instance of your repository in a lazy way. You can use the Lazy < T > class for example.
In terms of performance I'd favour looking to persist data and not the context, the EF context is optimized to be created and then disposed to free up connections in the pool.
Some other EF performance methods at http://www.sql-server-performance.com/2012/entity-framework-performance-optimization/
Best practice is to dispose the EF context after you've retrieved/updated your data.