Cleanest way to change database schema in EF6 - oracle

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;
/

Related

EF Code Migrations vs DB Scheme MS SQL and 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.

Entity Framework: Implement interface when generating from database

I'm having a few tables on SQL Server, which have similar structure - int Id and string Value.
This tables linked to main table via foreign key, so I'm wrote a bit of logic for mapping a string values to id's in models in MVC Razor. This feature requires that models used as dictionary implement simple IKeyValue interface with Id and Value, but after updating model from database I can loose interface implementation from models and must write it again.
Any way to automate this?
Are you modifying the auto-generated file? If so, you should not do this, for the exact reason you describe in your question -- it will get overwritten.
All of the classes in the generated file should be partial. You can take advantage of this by creating another class (in a different file, but in the same project), make sure it has the same declaration (and namespace), and have it implement the interface. This way the class will implement the interface, but will not be overwritten the next time you refresh the schema from the database.

Entity Framework get entity via variable

I have a listbox populated with table names. I want to be able to click a row in the listbox and return all records from that table and bind to a datagridview. Using good old fashioned SQL this is a piece of cake. Attempting to do the same with Entity Framework 4.3.1 is another matter.
For instance, is there a way to represent "get_picklist_names_v" as a variable in this code below?
static class EfHelper
{
public static EfEntities CreateContext()
{
EfEntities context = new EfEntities();
return context;
}
}
using (var context = EfHelper.CreateContext())
{
IList list = context.get_picklist_names_v.ToList();
lboPicklist.DataSource = list;
lboPicklist.DisplayMember = "name";
}
Entity Framework abstact the SQL stuff with types and does its best so you don't have to write sql for each server flavor (Oracle, SQL Server, MySQL, etc.). That's what an ORM does...
If your used to generic types its quite trivial to do the same with Entity Framework.
It's not exactly what yor looking for but maybe you can find some hints from the followig article :
Repository Pattern with Entity Framework
Entity Framework is not the best tool to achieve this. You could try Entity Sql, but the problem is that in order to use that in a dynamic way you have to capture the results in an IEnumerable<DbDataRecord>. A DataGridView won't display that as such.
There is nothing more convenient for this than filling a DataSet by a TableAdapter and a sql statement. I would stick to that if I were you.

MVC Linq to SQL Update Object in database

I have a table called Code in my LINQ to SQL datacontext. I also have a class called Codes in my Models folder. What I want to do is save the updated object Codes to my database table Code. Is this possible?
In my controller, I would pass the edited Object to my Model. My CodesRepository file contains this:
public Codes EditCode(Codes CodeToEdit)
{
private EventsDataContext _db = new EventsDataContext();
Codes C = new Codes();
C = CodeToEdit;
_db.Codes.InsertOnSubmit(C); //error here, something about invalid arguments
//InsertOnSubmit is for adding a new object, but I don't know the syntax
// for editing an existing object.
_db.SubmitChanges();
}
This is probably not the correct way of doing this so can someone point me in the right direction? Do I even need a class called Codes or do I need to somehow just use my database table? Thanks.
Solution: I decided to change from Linq to SQL to an Entity Framework and it works much better. This way, I don't have to define my Codes class since it comes straight from the database and I was able to delete the Codes class file.
You should use DataContext.Attach when you get an object back that corresponds to en existing row in the database. For Linq-to-sql's optimistic concurrency handling to work this requires that you either have the original, unsaved object available, or that you have a TimeStamp column in the database. The latter is preferred, as it only requires one extra field to be handled (probably through a hidden field in the web form).

"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.

Resources