I have a web app, using asp.net MVC Core, along with entity framework core. Part of this application has a simple setup table. This is just a table, which will only ever have one record, with a number of fields in, so the administrator can easily change how the web app works.
The setup table is loaded into a static "Settings" class, so anywhere in the application can read it (views and controllers)
What I want to know is when or how is the best place to load this. At the moment I'm loading it in Startup.Configure, after the routes have been set.
For the most part, this works fine. But if I add a field to the setup table, its impossible to migrate (I'm using code first). I get errors about invalid fields. Typically the errors look like this:
An error occurred while calling method 'BuildWebHost' on class 'Program'. Continuing without the application service provider. Error: One or more errors occurred. (Invalid column name 'EmailApiKey'.
Invalid column name 'EmailBaseUrl'.
Invalid column name 'EmailSenderAddress'.
Invalid column name 'EmailSenderDomain'.
Invalid column name 'EmailSenderName'.)
Unable to create an object of type 'ApplicationDbContext'. Add an implementation of 'IDesignTimeDbContextFactory<ApplicationDbContext>' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time.
To get around this, I have to remove the code thats loading the setup table.
This makes me think this is not the best place/way to load settings (seeing as though it obstructs with database migrations).
So - is there a better way to do this? Essentially, I want to:
load a setup table once when app first started.
Have this accessable everywhere in the application (views and controllers).
If a user changes the setup, the static version should be automatically updated.
I would lazy-load the settings on first access rather than at application startup. An advantage is that if the lazy-load fails, you'll just try again next time it's accessed.
If a user changes the setup, the static version should be automatically updated.
You could cache it in the ASP.NET Cache or MemoryCache. You can set an expiration so that it will be refreshed periodically in case a user has made changes. Or if the settings are in a SQL Server database, you can use the SqlCacheDependency class.
Related
I'm developing a web application which will be use by 3 departments, and each of them will have its own database, because if a problem occurs (like a bad manipulation by a humain... which occured last week...) and I have to restore the database for one department, I don't want to impact the 2 others.
What I want to do is when my application start for Dept-A I want the application to use Database-A, and so on for Dept-B and Dept-C.
I know it is possible in the Page_Init code to set the datasource to the right database to use in the code for the current page, but in my ASPX pages all my SqlDatasource has a ConnectionString pointing to the web.config, that's what is blocking me. It would be easy if upon start I can set dynamically the database name to use in the webconfig connection string, but sadly it seems not possible.
For now my solution will be to deploy my app to 3 different website, each one will have the same code but their webconfig will contains the right database for them.
This will work but I just want to be sure that there is no easier solution.
Thanks!
Use a multi-tenant app. That way, you can set a different connection string per tenant.
It requires little modifications, for a simple app. You could also use a framework like ABP.
It seems like I always have a variety of problems doing this, and usually I end up nuking the db out of frustration and rebuilding, but obviously there has to be some way to do this.
I have an existing asp.net mvc web app living with its sql db in azure. Works fine has some data that can be replaced but, again, the point is to learn how to update model without destroying the database.
In VS2017 I add one property public string ScreenShot { get; set; }
I make some small changes to my mvc and web api controllers to handle this extra property. I update my localdb via packmanager console and add-migration addprop and update-database. Works fine, run it locally, no probs.
Goto publish, goto settings, check update database. Click publish.
It hangs for like 5 or 6 min and I get:
Warning : A project which specifies SQL Server 2016 as the target platform may experience compatibility issues with Microsoft Azure SQL Database v12. when publishing
I try publishing several times and get the same thing. Google, look around, scratch my head, try again and it seems to publish. Site opens, and somehow I have lost my bootstrap theme. In fact in my Content folder I now have 4 files i believe are new: bootstrap-theme. (css,css.map, min.css, min.css.map) (same prefix , different suffix) as well as what I think are virgin versions of those without theme in the name, and seems to be the default mvc theme of black and white.
When I goto my app and I get an generic error and checking elmah I get:
System.InvalidOperationException: The model backing the 'TaskTrackerContext'
context has changed since the database was created. Consider using Code
First Migrations to update the database (http://go.microsoft.com/fwlink/?
LinkId=238269).
Well I really thought thats what I did. Almost forgot one thing! Maybe this is where my problem lies: I actually had to run two migrations-in addition to the model change I dropped a column that had never been used (scaffolding a controller for a DTO version of one of my models added it to my context which created a table).
I did create a new branch before making any of these change so I could just revert back but at some point I have to make this work and have to understand how to do it without destroying my db and remaking fresh.
A check in SQL object explorer shows it added the ScreenShot column to my table but didn't remove the unused table.
This works for me :
In visual Studio, go to publish settings and then select the option
Execute Code First Migration
Please see the screenshots below
Kind regards
I'm working out an implementation of ELMAH in a web application that logs exceptions to a SQL Server. That's inconsequential, however. My goal is to include the application name in the log so I can easily identify the offending application at a glance when checking error reports. I've searched far and wide spending hours trying to find a solution. I've included Application Name=[myApp] in my SQL Server connection string, but that only helps me to identify the app during a SQL Server trace and not in my actual error logging.
Has anyone done this? Here is an example of a log record in my database. I would like to see the Application Name listed as well. This happens to be the ELMAH implementation of the table, but it could just as well have been a custom table with the same field.
I would think this should be as simple as adding a value to the web.config or something, but I can't seem to find any solution like that or any other. I should probably also note that I'm using IIS 7.5 and .NET 4.0 in case that has anything to do with it. :)
Perhaps the responses to this question will help...
PS. Don't look at the accepted answer but the follow up where the <errorLog> element example is given...
As it is my practice to always fill out the assembly info for all my applications, I have elected to populate this field from My.Application.Info.Title which I just figured out how to access programmatically. To set this field in ELMAH, I set the ApplicationName value of my SqlErrorLog object like so:
Dim se As New Elmah.SqlErrorLog(ConnectionStrings("Elmah").ConnectionString)
se.ApplicationName = My.Application.Info.Title
The assembly info can be set by opening the project properties -> Application tab -> click Assembly Information... -> set "Title" value.
Now when you call the Log function for this object, it will create a database entry including the Application Name.
I have a database called ApplicationName_Development running on SQL Server 2008 R2 Developer edition on my development box.
I added .NET membership tables to the database with no problem. When I tried to get Code First working I received the following error message:
The server encountered an error
processing the request. The exception
message is "Model compatibility cannot
be checked because the database does
not contain model metadata. Ensure
that IncludeMetadataConvention has
been added to the DbModelBuilder
conventions.
After some googling, I discovered that I had to delete the database and let EF create the database. That's fine but I lost all my .NET membership tables. I can go back in and add the membership tables again but if my model changes and EF needs to recreate the database then I have to add the membership tables in again.
How do I get around this?
This is how code-first work. Main idea of code first is that you do not touch your database because it is responsibility of the model to create the database. If you want to customize your database you must create custom IDatabaseInitializer and add your custom SQL.
public class MyDbInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
protected override void Seed(MyContext context)
{
// Here run your custom SQL commands
context.Database.ExecuteSqlCommand("CREATE TABLE ....");
}
}
Now you only need setup your cutom intializer on the startup of your application:
Database.SetInitializer<MyContext>(new MyDbInitializer());
If you don't want to do it this way you must manually maintain your database and set initializer to null.
Found a easier workaround here. I hope this helps.
http://www.paragm.com/ef-v4-1-code-first-and-asp-net-membership-service/
Another option could be to use the System.Web.Management namespace. I've had great success with the code below:
string connectionString = ConfigurationManager.ConnectionStrings["MyDatabaseContext"].ConnectionString;
string database = "MyDatabaseName";
SqlServices.Install(database, SqlFeatures.All, connectionString);
It will just create the database and after that you can add users with the standard membership API.
Here's another possibility.
If you look at the MvcMusicStore sample - there's a SampleData class that is responsible for seeding the database on a rebuild. The SampleData class inherits from DropCreateDatabaseIfModelChanges, and overrides the Seed method. This class is passed to the Database.SetInitializer in the Application_Start method in global.asax.
I was getting the same error as you until I changed the parent class of SampleData to CreateDatabaseIfNotExist.
Then you can override the Seed method to insert any data you desire at startup, without it blowing away the database.
While you are developing, create 2 databases and two connection strings. One for SqlMembership (using aspnet_regsql) and one for your EF Application. If you would like to merge them into a single DB in production, just change the connection string in web.config.release to be the same. Then, EF model changes will just drop your apps db and not your membership DB.
By treating your authentication component separately, you will naturally decouple your authentication system from your application system. Then, if you wish to change membership providers, you will be better setup.
As the system grows, you will likely need to support non-pure models without EF code first, so this is a good template for going down that path.
I found the easiest way without playing with anything else was the following.
I ran the application first time with DropAndRecreatedatabase always in the Initilizer.
This created my database for the first time.
Following this I changed this to DropCreateDatabaseIfModelChanges.
I currently hold my resources in a database instead of resx files. I would like to be able to test whether all keys used in the application are held in the database to avoid runtime errors.
I have a custom ResourceProviderFactory to accomplish retrieving the resources.
Resources could be in aspx view pages - GetLocalResourceObject("ResourceKey") or GetGlobalResourceObject("ResourceClass", "ResourceKey").
They could be in a controller - HttpContext.GetGlobalResourceObject("ResourceClass", "ResourceKey").
I also call the resources from the core assemblies of my application without using the ASP.NET resource factory (using a singleton instead) - CoreResources.Current.GetResource("ResourceClass", "ResourceKey")
What would be the best way for me to ensure that all resources are in the database without having to waiting for a runtime exception?
The only approach I can think of is writing an app that scans your source code files and extracts all the resource keys you are using. You can then check the existence of each key in your database.
Unfortunately it is not so trivial... I don't think there is an easier way.
I would however change your ResourceProviderFactory to not throw an exception in case of a missing key. Just return some text like "Resource missing: 'xxx'" and log it so that it can be added.