EF Code Migrations vs DB Scheme MS SQL and Oracle - oracle

I am using Entity Framework Code Fist with migrations. I have a problem that migrations are bound with concrete DB schema. This is not such a problem with MS SQL. But in Oracle: schema = user. So my data model is bound with a DB User that can change.
When I change default schema with modelBuilder.HasDefaultSchema("SCHEME_NAME") I have to generate a new migration but I want to be able to deploy my app to any DB user in Oracle without having to change code and recopmile the project.

Well, you have multiple options for achieving this, such as:
Using automatic migrations and making the
modelBuilder.HasDefaultSchema(dbUserName) functions use an input
parameter. But this has multiple disadvantages such as not being
able to create migrations, instead every time it is automatically
created which has limits when deploying (not being able to create
scripts from it for deploy etc.)
You can implement a custom migration step which inherits from the CreateTableOperation class but as an input it does not take ("SCHEMA_NAME.TABLE_NAME" ...) but "TABLE_NAME" and dynamically gets the schema name when it is run (see one of my post about creating a custom migration operation to get the general idea)
Retrieving the user schema name and concatenating at migrations.
If you want the fastest solution I would choose the third option, which would simply look like this:
var schemaName = MigrationHelper.GetUserSpecificSchemaName();
CreateTable(String.Format("{0}.People", schemaName),
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
Because don't forget that basically the code in these migrations run just like any other C# code, which is invoked through the Add-Migration PowerShell script method.
For implementing the GetUserSpecificSchemaName you can use ADO.NET that retrieves it from your Oracle database.

Related

Cleanest way to change database schema in EF6

Currently we are writing a page in mvc5, with an oracle sql database connected with entitiy framework 6.
We currently have two schemas in the oracle database, one for testing and the other for development. The model in entitiy framework is generated from the development database, and works perfectly with it.
The problem comes, when changing the connection string to the testing schema. When the connection string is changed the application is unable to locate the tables (as they still reference the development schemes).
Currently I can fix this, by deleting all the tables from the model, and recreating the model from the correct schema, or manually editing every file referencing the schema. Both solutions are kinda tiresome and error prone.
How is this scenario usually dealt with?
EDIT
It seems that changing the database and retaining the schema, does not produce any error. So this is only schema related.
I guess this is a perfect use case for using entity framework command interceptors. I just tried and it works perfectly, even for Entity Framework DB-First approach.
You can register a custom command interceptor like this:
DbInterception.Add(new ReplaceSchemaInterceptor(newSchema: "[my]"));
This line will replace [dbo] schema name with the [my] schema name, before the query reaches the database. Luckily, schema name is enclosed with square brackets when Entity Framework generates the command text, so it's easy to match and replace. BTW, I'm not an Oracle expert, so I'm assuming that Oracle queries also include schemas in the same format. If not, then maybe you will have to tweak the implementation a bit (to replace the schema from whatever format it is generated by EF).
ReplaceSchemaInterceptor is a class that implements IDbCommandInterceptor interface. Inside this class, you need to replace the schema with your own schema. Below is the implementation of this class:
class ReplaceSchemaInterceptor : IDbCommandInterceptor
{
private readonly string _newSchema;
public ReplaceSchemaInterceptor(string newSchema)
{
_newSchema = newSchema;
}
public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void NonQueryExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
}
public void ReaderExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
{
}
public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
{
command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
}
public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
}
public void ScalarExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
command.CommandText = command.CommandText.Replace("[dbo]", _newSchema);
}
}
And lastly, the code is not perfect. You need to add some null checks for the constructor parameters, and maybe get rid of the code duplication inside implementation methods when replacing command text (extract into reusable method?). Right now it just does what you had asked for.
With fluent mappings in Entity Framework code-first you can indicate the default schema at runtime. This is one statement in OnModelCreating in your DbContext subclass, for instance:
modelBuilder.HasDefaultSchema("dev");
You're used to regenerating the model from the database, from which I conclude that the model doesn't contain many (or any) customizations that would make model generation a painstaking operation. This also should make it relatively easy to move to code-first. So I'd recommend you do that.
In Visual Studio, you can generate a code-first model from an existing database by adding an "ADO.Net Entity Data Model" from the templates that come with Entity Framework tools for Visual Studio. (Probably pre-installed). Choose the option "Code First from database" and follow the guidelines.
If you do that, you'll find a connection string in the project containing the model. This connection string may serve as template for the connection string you will put in the config file of your executing assembly. You'll notice that it doesn't look like...
metadata=res://* ... provider=System.Data.SqlClient;provider connection string="...""
This is the connection string that belongs to a database-first edmx model. It contains a path to the metadata files that are generated as resources into the assembly. Instead, the connection string will be a simple ADO.Net connection string. With code-first, EF will generate the meta data at runtime.
If you have this, you can add an entry in your config file for the default database schema and use that to set the schema as I showed above.
It looks like something we are doing here at my workplace.
Use synonyms for your objects!
A possibility would be to create synonyms dynamically for your test tables - and remove references to schema in your files
Say the user that connects is CONNECT_USER - must be different user as the schemas you're using which are SCHEM_DEV and SCHEM_TEST.
Here is how I would do the switch (Oracle PL/SQL scripting - connected as CONNECT_USER):
begin
for x in (select * from all_tables where owner='SCHEM_DEV')
loop
--- drop synonyms on SCHEM_DEV objects
execute immediate 'drop synonym '||table_name ;
--- create synonyms on SCHEM_TEST objects
execute immediate ' create or replace synonym '||table_name||' for SCHEM_TEST.'||table_name ;
end loop;
end;
/

Model changed during database created

I have uploaded my MVC3 project , it's s simple blog , at first it works well but after couple hours! following error appears (I've made custom error to Off to see the error)
The model backing the 'SiteContext' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance. For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.
to solve this I have to manually delete my database and create again and then restore to the backup that I have created. but after after 2 hours again I get the error!
I really don't have any idea , what caused that ??
When you create a model and ask EF to create a database from it, EF would hash the model and store the hash value with the database. Whenever the context is created, EF recomputes the hash and matches it against what is stored at the database. If the model changes in any way, the resulting hash will be different and EF will throw the exception you have just seen. This is done in order to keep the model in sync with the database.
Is there any way the model could have changed during runtime?
One thing you could do to figure out the difference is to
1.Re-create the database from the model as you are doing now and get it scripted (script1.sql).
2.Wait till the error happens and delete the db and re-create it again and script it (script2.sql)
3.Try to compare the two and see whether you can spot a difference in the schemas.
This should give you an idea of what has changed in the model.
Goodluck

How do OnModelCreating and non automatic Migrations relate?

When I add a new table that has some relations to my database and then run Add-Migration I see that code is generated in the Up method to add the table and its relations.
However, I prefer to define the relation using the fluent API in the OnModelCreating method. How do these two methods interact? Can I delete the code from the Up method that defines the relation for instance?
Each of them has completely different purpose:
OnModelCreating is used for inline fluent-API definitions of your model. These definitions together with default conventions, data annotations and configuration classes forms the complete definition of the model.
Explicit migration defines what must be done to database to migrate it to the form required by your current model
Now, how those two relate? Migration has two inputs which are used to generate migration code (Up and Down methods). One input is the last migration record stored in __MigrationHistory table in the database. This record contains serialized model representing the database. This input is optional because first migration must work without it. The second input is mandatory - it is your current model which is retrieved by executing the code in your current assembly => Add-Migration will execute your OnModelCreating to get the current model and compare it with the model retrieved from the database. The result of comparison is content of Up and Down methods in the explicit migration.

"Injecting" a WHERE clause dynamically w/ PetaPoco

I'm building a multi-tenant app with a shared database using .NET MVC 3 and PetaPoco.
The tenant id (along with other info) is saved in a FormsAuth cookie on login and is available to all controllers via a BaseController property. Most tables, (i.e. apart from apart the main 'Tenants' table) include a TenantId column.
Instead of manually adding a 'WHERE TenantId = X' to all CRUD on the feature tables, is there a way I can dynamically add this to the query just before its executed? In other words, maybe maintain a list of tables, and if the query is for one of those tables, then dynamically add in the TenantId filter?
The benefit of course is that it removes the need to add in the filter manually thus reducing the chances its left out. I did find an example using NHibernate, which I doubt can be repurposed. I am using Ninject in case that makes a difference.
There is an OnCommandExecuting method on the Database class which you can override in your own sub class, and modify the sql as you wish just before it gets executed. We use this feature for converting isnull/nvl between Sql Server and Oracle.
You could just leave a marker in your sql and replace it here.

Cross Database Join with Linq - Updating T4 template to support DB name?

I'm currently running in a multi-DB SQL Server environment and using linq to sql to perform queries.
I'm using the approach documented here to achieve cross DB joins:
http://www.enderminh.com/blog/archive/2009/04/25/2654.aspx
so basically:
2 data contexts - Users and Payments
Users.dbo.UserDetails {PK: UserId }
Payments.dbo.CurrentPaymentMethod { PK: UserId }
I drag the tables onto the DBML, and in the properties window, change the Source from dbo.UserDetails to Users.dbo.UserDetails to fully qualify the DB name.
I can then issue a single data context cross DB join by doing something like:
var results = (from user in datacontext.Table<UserDetail>()
join paymentmethod in dataContext.Table<CurrentPaymentMethod>() on user.UserId equals paymentmethod.UserId
... rest of query here ...);
Now this is tickety boo and works as I want it to. The only problem I'm currently having is when schema updates etc. happen (which is relatively frequent as we're in a significant dev phase).
(and finally, the question!)
What I want to achieve (and I've marked the question up as T4 as a guess, as I know that the DBML files are T4 guided) is an automated way when I drag any table onto a data context that the Source automatically picks up the DB name (so will have Users.dbo.UserDetails instead of just dbo.UserDetails)?
Thanks for any pointers :)
Terry
Have a look at the T4 Toolbox and the LinqToSql code generator it provides (Courtesy of Oleg Sych) - You can customize the templates to generate references however you'd like, but I think the problem you're going to run into is that the database name isn't stored in the dbml file.
What you could probably do is add a filter to the generator, perhaps using a dictionary or similar, such that in your .tt file, you maintain a list of tables and the databases they belong to. That way, if your maintenance task is to delete the class from the designer and drop it on again, it will get the right database name.

Resources