So, newbie NHibernate user; trying to wrap my brain around it.
I'm contemplating how to handle deployment, and later injection of add-ons to a web app (which may require their own persistence classes).
I was thinking that using SchemaExport for the deployment would work pretty well, but I was wondering if there's a way too get NHibernate to tell me in a common, code-based way that a schema export has been done already, or not. Basically, I want to do smething like in this pseudocode:
if(!_cfg.HasSchemaForType(typeof(MyType))
ExportSchema(typeof(MyType));
else
UpdateSchema(typeof(MyType));
where the two functions would internally use SchemaExport or SchemaUpdate, respectively.
EDIT: Guys, I appreciate the answer so far, but they're missing the point a bit. What I'm trying to set up is a way for the application to allow for the addition and removal of add-ons which may require changes to the db. I'm not talking about versioning my own code or the like (at least, not as its primary function). So the question is less about when I deploy the app, and more about when I add or remove a plug-in. Has theis plugin (hence the pseudo-code type check) been deployed before? If so, run the update. If not, run the export. Make sense?
I think that what you are looking for is SchemaUpdate.Execute instead of using SchemaExport. SchemaUpdate will create the schema if it doesn't already exist, or update it if required and desired.
That works for me using both MSSQL and SQLite.
new SchemaUpdate(config).Execute(false, true);
Yes there is, in 3.0 at least
public static bool ValidateSchema()
{
NHibernate.Tool.hbm2ddl.SchemaValidator myvalidator = new NHibernate.Tool.hbm2ddl.SchemaValidator(m_cfg);
try
{
myvalidator.Validate();
myvalidator = null;
return true;
}
catch (Exception ex)
{
MsgBox(ex.Message, "Schema validation error");
}
finally
{
myvalidator = null;
}
return false;
}
For the update part, do.
public static void UpdateSchema()
{
NHibernate.Tool.hbm2ddl.SchemaUpdate schema = new NHibernate.Tool.hbm2ddl.SchemaUpdate(m_cfg);
schema.Execute(false, true);
schema = null;
} // UpdateSchema
No, NHibernate doesn't do what you're asking. I imagine it would be possible to write some code that exported the schema and then compared it to the database schema. But it would probably be easier to export into a temporary database and use a 3rd party tool, such as redgate SQL Compare, to compare the schemas.
Even if it did what you're asking, I don't see how that would help with deployment because its purpose is to create a database from scratch.
Edited to add: Assuming each plugin has its own set of tables, you could determine if the schema has been deployed using one of several methods:
Attempt to load one of the plugin objects and catch the exception.
Examine the database schema (using SMO for SQL Server) to check if the table(s) exist.
Create a record in a table when a plugin is deployed.
The purpose of schema export is to generate the complete schema from scratch. Really useful if you haven't deployed your application yet.
After the first deployment I would highly recommend using a migrations tool which will help you with further extensions/modifications of the schema. If you think a bit more ahead you will notice that you even require data manipulation (e.g. removing wrong data which has been generated due to a bug) as your application evolves. That's all a migration tool can help you with.
Take a look into:
Migrator.net
Here is a list of more migration tools for .net answered in a SO question:
.net migrations engine
The original idea of migrations originated from Ruby on Rails and has been "cloned" into other frameworks over the past. That's why it's definitely good to read about the original idea at http://guides.rubyonrails.org/migrations.html too.
If you have VS Team Suite or the Database Developer edition, it can sync and track changes and then make a deployment script that will create all the right objects for you. Also RedGate has a Schema Compare product that does the same thing if I'm not mistaken.
Related
I am looking for a way to handle this challenge: we are a geographically dispersed dev team using ASP.NET Web API and Angular to build a web app.
The thing that causes the grief is the fact that not all team members use the same database setup for their dev work. Yes, I know - I can use web.config transforms to set the proper connection strings for test, staging and production (and I'm already doing this) - but this is not what I'm talking about.
Due to reasons beyond our control at this time, we have
some developers working on a local SQL Server instance using server=(local);database=OurDB as their connection string
other developers using a central developer SQL Server in their location, using something like server=someserver.mycorp.com;database=OurDB
and a few exotic cases with yet other settings
Now every time someone commits a change to the Git repo, and happens to also change something in the web.config, his connection string is committed to the repo. So when I then go pull that latest commit, my settings to my local DB server are overwritten by this other guy's settings.
I am looking for a way to handle this - I was hoping I might be able to
hook into the Git pull process and automagically update the web.config connection string to my local needs whenever I pull something
somehow reference a connection string (or external config file) based on e.g. my currently logged in user's name or something like that
But I can't seem to find any way of doing this. I was wondering if I need to build a VS extension to handle this - any starters for that? Has anyone done something like this before and could share his code? (or has it up on Github)
The web.config configuration system used in ASP.NET is not flexible enough to support the more advanced scenario you have described. So, why use it? You could store the configuration in files within the repository, one per developer. Or they could be stored outside the repository or otherwise ignored.
The real trick is that most older applications don't have a single root that retrieve the configuration, so you have to refactor your application to utilize a flexible configuration system. For your staging/production environments you probably still want to use the config in web.config. The following code can give you a basic idea of one way to structure it:
public class MyApplicationConfiguration
{
public string MainConnectionString { get; set; }
}
public class ConfigurationRetriever
{
public MyApplicationConfiguration GetConfiguration()
{
// You might look for the absence or presence of an environment variable to determine this
bool isLocalDevelopment = IsApplicationLocalDevelopment();
var config = new MyApplicationConfiguration();
if(isLocalDevelopment)
{
config.MainConnectionString = Environment.GetEnvironmentVariable("MyApplication_MainConnectionString");
//or get it from a JSON file or XML file or config database
}
else
{
config.MainConnectionString = ConfigurationManager.ConnectionStrings["MainConnectionString"].ConnectionString;
}
}
}
Rather than rolling your own config building logic, you might refactor your application to leverage Microsoft.Extensions.Configuration. It's not just for .NET Core. It's for .NET Standard. So you can use it even in your legacy ASP.NET applications. For reading the web.config, you could probably use Microsoft.Extensions.Configuration.Xml. Or you can write your own adapter that pulls values out of ConfigurationManager. I did a basic test, and this worked as expected.
I am following the offical asp.net "Getting started with EF 5 using MVC 4". In that tutorial, the database is created when the migrations are performed(in my understanding). When I was looking at the EF 5 with Mvc 5 tutorial they didn't use migrations to create a database. They use database initializer. So, I was wondering could create a database for your project without using migrations in EF 5? Also, what would the difference be with both these approaches?
Code first Migrations and using Package Manager Console Commands to do upgrades can get a bit confusing at first.
You can use the initializer to CreateDatabaseIfNotExists , DropCreateIfModelChanges, DropCreateDatabaseAlways and to MigrateDatabaseToLatestVersion
See the interface IDatabaseInitializer<TContext>.
CreateDatabaseIfNotExists // is the Default initializer.
So this is why it appears EF just does things for you sometimes.
So the answer is "YES you can "Create a Database without Migrations"
But the difference is not obvious and if you would do that long term is another question.
If you are using migrations. It would Update the Db to match the code first model.
If there is NO database, then that means creating the database.
So Thats why Automated migrations and CreateDB look confusing since they can result in same outcome sometimes. But technically they are different.
So generally it is sufficient to use code first automatic "migrations" only.
Migrations can be either Automatic or "managed".
The managed migrations approach invovles generating code , tweaking the code and running PM commandlet or POwershell command to actually perform the migration.
With Automated migrations you just need set the intitializer and Access the DBContext.
There are 2 parts to the process.
a) The DB Initializer step.
do this immediately before instantiating YourDBContext.
//eg
// DONT TOUCH MY DB or i break your back!
Database.SetInitializer(new ContextInitializerNone<YourDbContext>()); // Do Nothing,
// OR
// yes migrate my db to match my code please.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourDbContext, YourMigrationConfiguration>()); // Set to migration is requested, see config class below
The Confirguration class specified when using Migration initializer looks like this
public class YourMigrationConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext{
protected YourMigrationConfiguration() {
AutomaticMigrationsEnabled = true; // run it when needed. Do not wait for my PM Command
AutomaticMigrationDataLossAllowed = true; // if the new db look means dropping tables or columns go ahead and kill my data. So use this option with caution.
}
then just trigger the migration in code when required.
Context.Database.Initialize(true); // i place this inside a method on my UoW class
Code first Db initialization strategies.
Code first migrations recommended reading
Managed Migrations
There are many articles on the web on this topic.
Is there a way to check that a DbContext matches the database when the database was not created by EF code first?
I am looking for similar functionality to Database.CompatibleWithModel but there is not metadata.
There is currently no way in EF to do this; however, you may be able to use the DDL script as a starting point for verifying that all the artifacts exist in the database. To get this script, use
string ddlScript = ((IObjectContextAdapter)myContext).ObjectContext.CreateDatabaseScript();
Some tools may be able to use this script to do a schema compare against your database. This will tell you if your model is compatible.
Have you tried using Entity Framework Power Tools.
you can use the tools to Reverse Engineer Code First - Generates POCO
classes, derived DbContext and Code First mapping for an existing
database.
And then maybe you can compare the reversed engineered information with what you already have.
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.
What method do you use to get a compile time error when the database schema changes occur in an ASP.NET project?
For example, if you have a GridView bound to a DataSource, I can only get runtime errors when a schema change occurs, not a compile time error. Intellisense works fine on the code behind using datasets, LINQ, etc, but I cant seem to get a compile time error on an ASP.NET page when I change the schema.
Any advice?
Create a unit test that verifies the correctness of you data access layer, and make sure it covers all your DB-related code. Not everything can be caught at compile time...
One way I can think of easily achieving this behavior would be to databind to a dynamic DAL. There are some tools that can help do this DAL generation, I'd recommend taking a look at SubSonic.
Once you have something like SubSonic in place you can bind to the resulting business objects. These business objects will automatically change in the case of a schema change in the database and this will break your binding code which will result in a compile time error.
Update
Assaf's recommendation to use Unit Tests is also a good idea. It doesn't solve your stated problem but it is definitely something that should be in place and is a great tool for flagging these type of problems.
We use a modest system (xml to c++) to create schemas from an independent description, this system also creates names for tables and columns that we use inside the code, when there is a change in the schema the names change, as the names we originally used are not there anymore and the compiler will flag an error.
You could probably configure a lot of the DAO generation tools to do something similar.
One solution would be to version your database and map an application build to a specific version (maybe in a properties file). In the entry point of your app, you can compare the expected version to the actual version and handle the error accordingly.
I'm not sure whats the equivalent in ASP.net of Migrations in Rails or dbdeploy in Java for versioning your database. But any DB versioning tool that makes schema changes incremental and versioned and tracks the version in a Version table will suit the purpose.
But if you want a compile time error while building your app, you might as well upgrade your schema to the latest version as part of your build process, avoiding the possibility of schema changes in the first place.