I'm working on a project that at can end up with multiple UI versions / variants, but so far I've got two subprojects in my solution Web - containing Web interface with ASP.NET MVC. Service project is place where I have my database context and models defined.
My Goal is to have minimum or possibly no references to EF specific code in my Web project. I want it to be independent so when I switch the dlls with service backend ( from let say SQL to XML or MySQL ) I shouldn't make multiple modifications in my MVC project.
This is how it looks :
My Questions are:
- so far I've found no example of using Database.SetInitializer in other place than Global.asax. I'd like to put database re-creation if model changed in my factory-like DatabaseContextProvider class or in service class that pulls out data from context and provides it to the UI with DTOs. Are there any cons of that location ?
- I would like to have the context's connectionString to be configurable with Properties/Settings.settings file - is that reasonable ?
To avoid the coupling, I would prefer not to set the initializer outside the Assembly that contains the DataContext. So, I added a static constructor for the DataContext. This way every project referencing this Assembly will enjoy the initializer without explicitly setting it, and the initializer is set only once per process.
static MyDataContext()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDataContext, Configuration>());
}
The connection string will of course be taken from the application configuration file.
You would need a mechanism to call the Database.SetInitializer method before the very first usage of the DbContext. That is why its usually called in the Global.asax file.
You can create a class with an initialization method in your tm.Service project and call it in the Application_Start method and put the Database.SetInitializer in that initialization method.
Its OK to supply the connection string from a setting file.
I put it on the DbContext constructor and works for me.
public myDbContext() : base(connectionToDatabase) {
Database.SetInitializer<myDbContext>(null);
}
The solution above will work, but it is not as efficient as the following code:
protected void Application_Start()
{
Database.SetInitializer<myDbContext>(null);
}
In my case I don't have a reference of my DAL on the UI and for that reason what I did is, I create a EntityFramework config and register my setting using using reflection.
protected void Application_Start()
{
EntityFrameworkConfig.RegisterSettings();
}
public static class EntityFrameworkConfig
{
public static void RegisterSettings()
{
// Use the file name to load the assembly into the current
// application domain.
Assembly a = Assembly.Load("MyAssembly");
// Get the type to use.
Type myType = a.GetType("MyType");
// Get the method to call.
MethodInfo myMethod = myType.GetMethod("MySettingsMethod");
// Create an instance.
object obj = Activator.CreateInstance(MyType);
// Execute the method.
myMethod.Invoke(obj, null);
}
}
public void Configurations()
{
//Other settings
Database.SetInitializer<myDbContext>(null);
}
Updated
With Entity Framework 6, now you can use the NullDatabaseInitializer
Database.SetInitializer(new NullDatabaseInitializer<MyDbContext>());
Microsoft made it possible, for EF6 onwards, to configure one initializer per database context in the config file of the application. See the last section on this Microsoft page: https://msdn.microsoft.com/en-us/data/jj556606.aspx
This, like the "Global.asax" approach, has the advantage that e.g. unit test projects can use a different initializer for the same database context.
Click Global.asax page and you find a Application_Start() method.Inside this method past this following code.For support this code use namespace using System.Data.Entity;
Database.SetInitializer<namespace.modelclass>(null);
Related
I search many posts in stack and another sites about this error but it wasn't my answer
this is my DbCintext
[Table("AspNetUsers")]
public class ApplicationUser : IdentityUser { }
public class EMSContext : IdentityDbContext<ApplicationUser> {
public EMSContext (DbContextOptions<EMSContext> options) : base (options) { }
public DbSet<Ambulance> Ambulances { get; set; }
}
and this is mu StartUp.cs
services.AddControllers();
services.AddDbContext<EMSContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
but when I want to generate api controller I got this error
no parameterless constructor defined for type dbcontext
The error is clear, you don't have a parameterless constructor.
But that said, the tooling shouldn't need it since you are calling AddDbContext and passing in your connection string. The tooling scaffolds controllers/etc. by actually running your program and then requesting the DbContext from the host's service provider. It does this by looking for methods with very specific names.
My guess is that your main Program.cs is not following the prescribed upon pattern and thus cannot construct your Host nor locate its ServiceProvider.
Your options are as follows (my personal choice is #2):
Add a paremeterless constructor like the message says
Make sure your main entry point uses the correct conventions for the host builder methods
Create a Design-Time context factory
Parameterless Constructor
This is the obvious answer, though it's not always possible. Furthermore it should not be necessary since the scaffolding has other means by which to read from your DbContext. Adding a constructor just for this purpose is not something I would do nor recommend.
Correct Naming / Method Signature
The scaffolding looks for a very specific method signature when it attempts to run your program. Basically, it is looking for something like this:
public class Program
{
public static void Main(string[] args)
=> CreateHostBuilder(args).Build().Run();
// EF Core uses this method at design time to access the DbContext
public static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(
webBuilder => webBuilder.UseStartup<Startup>());
}
Now it doesn't need to look exactly like this. The important part is the name and signature of the CreateHostBuilder method.*
If there are any errors raised while attempting to construct the service, for example because there are dependencies that cannot be resolved or configuration is missing then this method will fail. I'm pretty sure you will get either the same error or a DI-related one.**
Design Time Factory
Supposing that still doesn't work or you'd like better control you can create a Design-Time Factory for your context. The tooling will scan your project for types implementing IDesignTimeDbContextFactory<EMSContext> and then use that during scaffolding:
public class EMSContextFactory : IDesignTimeDbContextFactory<EMSContext>
{
public EMSContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<EMSContext>();
optionsBuilder.UseSqlServer("Your Connection String");
return new EMSContext(optionsBuilder.Options);
}
}
See this link for more information.
* Note: You've not specified what version of aspnet core you are running. If you aren't using the generic host (Host.CreateDefaultBuilder) but are instead using WebHost, then the expected method signature will look a bit different. That said it will match that of creating a new project with the same settings.
If you've ever written integration tests for your controllers, the WebApplicationFactory looks for similar methods/signatures. Those same signatures should satisfy the controller scaffolding. I can't seem to find documentation on either other than the link above.
** When the scaffolding runs, it executes as the 'Production' environment. DI errors can occur if service resolution/configuration fails due to being in the "wrong" environment. This is one of the reasons the context factory solution exists.
The error is exactly located in this line:
public EMSContext (DbContextOptions<EMSContext> options) : base (options) { }
No parameterless constructors for DbContext... of course, because your constructor takes one parameter DbContextOptions options,
so you have two options to fix it, fix the call to the constructor or make the constructor take 0 parameters
Here's the error I was getting, sounds very similar to yours.
I had encountered this problem when trying to create a View (the one circled in red). My issue was the Startup.cs file. I had recently done a re-name from LeaveHistory... to LeaveRequest..., and when editing my Startup.cs file I made an incorrect change.
Line 39 should read:
services.AddScoped<ILeaveRequestRepository, LeaveRequestRepository>(); // no "I"
The error I was getting mentioned that the dbContext class didn't have a parameterless constructor - so it was totally deceptive, or at least not that helpful.
So to anyone else looking at this error, don't take it on it's face! I am fairly deep into this project, and none of my other controllers have parameterless constructors, at least not written out.
Think about other clerical changes you've made - class name changes, things like that. Perhaps the error message is not actually what it seems, and you've made a small error somewhere else that is simply generating a confusing error message.
I have separated my solution in separate projects, a DAL project with entity framework and an ASP.NET MVC project.
I want to use DropCreateDatabaseIfModelChanges, but I don't know where to put it to make it work.
I've tried to put it in the web.config of the MVC project and the app.config of the DAL project (both by making use of the context element), I've tried putting it in the global.asax (Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BreakAwayContext>());), I've tried a custom initialization class, but none of these seem to work.
If possible, I don't want to make use of migrations. How can I make it work?
You could create a class to implement CreateDatabaseIfNotExists and call Database.SetInitializer function in Application_Start().
-DbInitializer
public class MyDbInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
//Data initializing...
}
}
-Application_Start
protected void Application_Start()
{
Database.SetInitializer(new MyDbInitializer());
}
The database will be create when running the application.
And if you would like to do a automatic migration of database, use MigrateDatabaseToLatestVersion class
public class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
}
}
-Application_Start
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext,Configuration>());
Howerver, I recomand that using migraion commands will be more flexible. See this walkthru: Overview of Entity Framework Code First Migrations with example, by Bhavik Patel.
I guess by 'database initialization' you actually mean 'updating the database schema'.
Set the EfRepository as start up project of the solution
Open the Package manager console Choose EfRepository as default project
Run the following commands:
Enable-Migrations -ConnectionStringName "EfDataRepository"
Add-Migration Initial -ConnectionStringName "EfDataRepository"
Update-Database -ConnectionStringName "EfDataRepository" -Script -SourceMigration:0
This will give you a .sql script. Execute it against your database (and usually store it as part of the solution - either Create.sql or some kind of a migration .sql, depends on whether you already have a schema or you are creating it from scratch).
Of course, replace EfDataRepository with the data connection name from your .config file.
I have an object (ClientConfiguration) that I use on almost every page on my site, as well as in many methods that exist in related projects that get compiled into the website.
What I am doing now is creating and populating the object on each page load, and storing it in the HttpContext. This works great for anything in the UI project; and for anything in the dll projects, I pass the ClientConfiguration to any methods that may need to use it.
What I would rather do is have a "global" property that is shared among all of the related projects so I don't have to pass it around.
Is there a good way to accomplish this?
After you add System.Web.dll as reference in your other library projects, you can access the object in HttpContext directly, no need to pass as parameter.
This depends a bit on where initial configuration is being stored (xml file, database or something else) but you’ll see the point.
If these are global configuration settings that are same for all application users you can create a class like this
public class Config
{
public static ClientConfiguration Current
{
get
{
if (HttpContext.Current.Application["clientconfig"] == null)
{
//Fill object from database
}
return HttpContext.Current.Application["clientconfig"] as ClientConfiguration;
}
set
{
//store object in database
//invalidate what is stored in application object
//so that it will be refreshed next time it's used
HttpContext.Current.Application["clientconfig"] = null;
}
}
}
This will store the ClientConfiguration in global Application object and make it available in all pages so you don’t have to create it in page load.
You can just use it like this
private void Foo()
{
ClientConfiguration config = Config.Current;
}
If you have multiple projects that need to share same data then it’s best to store the object in database or in shared XML file and create new class library project so that you can just include reference to the Config class.
I am developing a client system that needs a small local database.
I want to avoid installation of SQL Server Express and have decided to go with SQL Server 4.
I use Entity Framework 5 for data access and have created my custom context.
Everything works fine in development where I can use app.config to either set specific file location or dynamic Data Source=|DataDirectory|\MyDatabase.sdf.
But on deploy I want the database to be located in the users documents folder:
\My Documents\ApplicationName\MyDatabase.sdf
How can I do that?
All I need is actually to be able to set custom connection string in code!
This is what I tried so far:
private MyApplicationDataContext(string connectionString)
: base(connectionString)
{
}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
//var connectionString = string.Format("provider=System.Data.SqlServerCe.4.0;provider connection string=\"Data Source={0}\"", path);
var connectionString = string.Format("Data Source={0}", path);
return new MyApplicationDataContext(connectionString);
}
As you can see I tried two kinds of connection strings but both caused exceptions.
Keyword not supported: 'provider'.
and
The provider did not return a ProviderManifestToken string.
Ah, I finally got it right!
I include the adjusted code if someone else has the same problem.
The trick was to set the connection string on the Database.DefaultConnectionFactory
private MyApplicationDataContext()
{ }
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Set connection string
var connectionString = string.Format("Data Source={0}", path);
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", connectionString);
return new MyApplicationDataContext();
}
In EF 6 this can be done with DbConfiguration:
App.config:
<entityFramework codeConfigurationType="MyDbConfiguration, MyAssembly">
</entityFramework>
And inside your assembly create a class like:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
The SqlCeConnection instance is used to connect to Sql Ce database file. If I want to connect to MSSQL database, I will use SqlConnectionStringBuilder and SqlConnection instances to build my DbConnection instance.
// The ctor for `DbConnection`.
private MyApplicationDataContext(DbConnection conn) : base(conn, true) {}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Connection string builder for `Sql ce`
SqlCeConnectionStringBuilder sb = new SqlCeConnectionStringBuilder();
sb.DataSource = path;
// DbConnection for `Sql ce`
SqlCeConnection dbConn = new SqlCeConnection(sb.ToString());
return new MyApplicationDataContext(dbConn);
}
For EF 6
I arrived at this answer thanks to Matthias's post, but included some worthwhile info from Entity Framework Code-Based Configuration (EF6 onwards).
To specify a custom database location you will need to do some configuration. Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration. Given that my project required the database location to be set dynamically, I went with the code-based configuration, which is described below.
(Note that the config file takes precedence over code-based configuration. In other words, if a configuration option is set in both code and in the config file, then the setting in the config file is used.)
According to EF documentation (above link) there are 4 approaches to implement your custom configuration,which include: Using DbConfiguration, Moving DbConfiguration, Setting DbConfiguration explicitly, and Overriding DbConfiguration.
Using DbConfiguration
Code-based configuration in EF6 and above is achieved by creating a subclass of System.Data.Entity.Config.DbConfiguration. The following guidelines should be followed when subclassing DbConfiguration:
Create only one DbConfiguration class for your application. This class specifies app-domain wide settings.
Place your DbConfiguration class in the same assembly as your DbContext class. (See the Moving DbConfiguration section if you want to change this.)
Give your DbConfiguration class a public parameterless constructor.
Set configuration options by calling protected DbConfiguration methods from within this constructor.
Following these guidelines allows EF to discover and use your configuration automatically by both tooling that needs to access your model and when your application is run.
Example (modified from Matthias's answer):
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
//var directory Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var directory = #"C:\Users\Evan\Desktop\TestFolder"; // Directory may or may not already exist
Directory.CreateDirectory(directory); // create directory if not exists
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
Note that the config file does not need to be altered unless there are existing configuration settings that override your custom configuration. Also, I changed the directory to illustrate that While EF is capable of creating a new database if it doesn't already exist, it will not create parent directories, which is why I included this line: Directory.CreateDirectory(directory). Given that this approach worked for my project I didn't explore the remaining 3 configuration methods, but you can find info on them in the link provided above and I will include the documentation here as a reference in case the link breaks.
Moving DbConfiguration
There are cases where it is not possible to place your DbConfiguration class in the same assembly as your DbContext class. For example, you may have two DbContext classes each in different assemblies. There are two options for handling this.
The first option is to use the config file to specify the DbConfiguration instance to use. To do this, set the codeConfigurationType attribute of the entityFramework section. For example:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
...Your EF config...
</entityFramework>
The value of codeConfigurationType must be the assembly and namespace qualified name of your DbConfiguration class.
The second option is to place DbConfigurationTypeAttribute on your context class. For example:
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}
The value passed to the attribute can either be your DbConfiguration type - as shown above - or the assembly and namespace qualified type name string. For example:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}
Setting DbConfiguration explicitly
There are some situations where configuration may be needed before any DbContext type has been used. Examples of this include:
Using DbModelBuilder to build a model without a context
Using some other framework/utility code that utilizes a DbContext
where that context is used before your application context is used
In such situations EF is unable to discover the configuration automatically and you must instead do one of the following:
Set the DbConfiguration type in the config file, as described in the
Moving DbConfiguration section above
Call the static DbConfiguration.SetConfiguration method during
application startup
Overriding DbConfiguration
There are some situations where you need to override the configuration set in the DbConfiguration. This is not typically done by application developers but rather by thrid party providers and plug-ins that cannot use a derived DbConfiguration class.
For this, EntityFramework allows an event handler to be registered that can modify existing configuration just before it is locked down. It also provides a sugar method specifically for replacing any service returned by the EF service locator. This is how it is intended to be used:
At app startup (before EF is used) the plug-in or provider should
register the event handler method for this event. (Note that this
must happen before the application uses EF.)
The event handler makes a call to ReplaceService for every service
that needs to be replaced.
For example, to repalce IDbConnectionFactory and DbProviderService you would register a handler something like this:
DbConfiguration.Loaded += (_, a) =>
{
a.ReplaceService<DbProviderServices>((s, k) => new MyProviderServices(s));
a.ReplaceService<IDbConnectionFactory>((s, k) => new MyConnectionFactory(s));
};
In the code above MyProviderServices and MyConnectionFactory represent your implementations of the service.
You can also add additional dependency handlers to get the same effect.
Note that you could also wrap DbProviderFactory in this way, but doing so will only effect EF and not uses of the DbProviderFactory outside of EF. For this reason you’ll probably want to continue to wrap DbProviderFactory as you have before.
You should also keep in mind the services that you run externally to your application - e.g. running migrations from Package Manager console. When you run migrate from the console it will attempt to find your DbConfiguration. However, whether or not it will get the wrapped service depends on where the event handler it registered. If it is registered as part of the construction of your DbConfiguration then the code should execute and the service should get wrapped. Usually this won’t be the case and this means that tooling won’t get the wrapped service.
I'm trying to inject specific class into my WCF service but it doesn't work and I can't understand why. I'm VERY NEW to MEF and patterns, just trying to make it work. Watched series of videos to understand what it is about but bootstraping won't work in my case since it is not Silverlight
http://channel9.msdn.com/blogs/mtaulty/mef--silverlight-4-beta-part-1-introduction
Here is my Web application's Global.asax code. This is non MVC, just regular ASP.NET app:
private void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
var catalog = new WebScopedCatalog(new DirectoryCatalog(Server.MapPath("~\\bin")));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
FIRST, I'm not sure I even bootrstrap it properly.. SECOND, I'm using http://www.timjroberts.com/2011/02/web-scoped-mef-parts/ as a guidance for web-scoped parts. I need that because some injected objects supposed to live during request only.
Now, I have following class:
[Export(typeof(ITest))]
[WebPartCreationPolicy(WebCreationPolicy.Session)]
public class Test : ITest
{
public string TestMe()
{
return "Hello!";
}
}
And my service looks like:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MobileService
{
[Import]
public ITest MyTestClass { get; set; }
public MobileService()
{
int i = 10;
}
When breakpoint hits at i=10 - I have NULL inside MyTestClass. Clearly, MEF does not initialize this class for me. Am I missing something?
EDIT:
When I examine catalog - I see my class Exported but I don't see it imported anywhere...
EDIT 2:
Daniel, Thank you. It makes sense. It still feels litle like a magic for me at this point. Yes, WCF creates this service. Than I have MembershipProvider and various Utility classes that ALL need the same import. And I'm not creating neither of those classes so I can't have Mef creating them. (Service created by WCF, MembershipProvider created by who-knows-what, Utility classes have static methods..) I wanted to use Mef instead of storing my stuff in HttpContext.Current. My container object doesn't have SatisfyImports. It has SatisfyImportsOnce. What if I do it in many different constructors? Will Mef share same singleton or it will be creating new instance every time?
At this point I wonder if I should even use Mef in this particular scenario..
MEF won't satisfy imports for objects it doesn't create. What is it that creates MobileService? If it's not MEF, then the import won't be satisfied by default. Even if it is MEF, the import wouldn't be satisfied in the constructor- you can't set properties on an object you create until it is done being created (ie the constructor has finished).
You can call container.SatisfyImportsOnce(mobileService) to satisfy the imports of a part. You should try to avoid doing this everywhere you need a MEF import. If you can, you should export a part and import it from another part so that MEF handles the creation for you. However, it looks like this part may be created by WCF so you can't have MEF create it, and in that case it would be OK to use SatisfyImportsOnce.
Response to EDIT2: If you use the same container each time, MEF will by default only create one instance of the part with the export. You can set the CreationPolicy to NonShared (in either the export or import attribute) to change this.
As for whether it makes sense to use MEF the way you are trying to, I don't know enough about writing web services to give you any advice on that.
The web-scoped part creation policy that I wrote won't help with WCF services.
I've posted a new blog post that documents an approach to composeable WCF services:
http://www.timjroberts.com/2011/08/wcf-services-with-mef/
Basically, you need to implement a custom Instance Provider that is MEF-aware and can compose the WCF service object when it is created.
I wrote about how to compose your WCF in a generic way that its configuration based here:
Unit Testing, WCF and MEF
I actually built my solution on the code samples Tim Robert's provided in his post. only that I took it a bit further and instead of using code in the global asax i moved the configuration into the web.config so its easier to maintain.