DCSIMG
Change Entity Framework storage DB schema in runtime - Ido Flatow's Blog Veni Vidi Scripsi

Ido Flatow's Blog

Veni Vidi Scripsi

News

Have you heard me speak?
Powered
<style type='text/css' media='screen' id='sm_css'> #smix {overflow: visible;height: auto;border-radius: 10px;max-width: 250px;background-color: #323232;text-align: left;font-size: 12px;line-height: 16px;font-family:'Lucida Sans Unicode','Lucida Grande',Verdana,Arial,Helvetica,sans-serif;-webkit-border-radius: 10px;-moz-border-radius: 10px;border-radius: 10px;} #smix a {color: #0056CC;text-decoration: none;} #smix .sm_head {color: #fff; line-height: 1em;font-size: 1.4em;padding: 10px;color: #fff;} #smix .sm_lanyard_wrapper {background-color: #fff;;clear: both;width: 97%;margin: 0 auto;margin-bottom: 0px;} #smix .sm_lanyard_content {padding: 7px;}#smix button.sm_rec, #smix a.sm_rec, #smix input[type=submit].sm_rec { padding: 6px 10px; -webkit-border-radius: 2px 2px;-moz-border-radius: 2px; border-radius: 2px; border: solid 1px rgb(153, 153, 153); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(255, 255, 255)), to(rgb(221, 221, 221))); color: #333; text-decoration: none; cursor: pointer; display: inline-block; text-align: center; text-shadow: 0px 1px 1px rgba(255,255,255,1); line-height: 1; }#smix .sm_rec:hover { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(248, 248, 248)), to(rgb(221, 221, 221))); }#smix .sm_rec:active { background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgb(204, 204, 204)), to(rgb(221, 221, 221))); }#smix .sm_rec.medium { padding: 3px 7px; font-size: 13px; }#smix .sm_rec span.icon.thumbs_up {background-position: 0px 36px;vertical-align: text-top;display: inline-block;margin-right: 4px;height: 18px;width: 16px;background-image: url(http://speakermix.com/images/new/thumbsold.png);}#smix .sm_rec:hover span.icon.thumbs_up {background-position: 0px 18px;} #smix .sm_events {padding:2px 0px 4px 0px;} #smix .sm_section {font-size: 10px; border-bottom: 1px solid silver; margin-bottom: 6px;} #smix .sm_subline {font-size:120%;margin-top:4px;font-weight:bold} #smix .powered {text-align: right} #smix .powered img {margin: 7px} </style>
Sela Technology Center

Advertisement

Change Entity Framework storage DB schema in runtime

When you build a EF schema, it automatically plant the name of the imported tables schema into the EDMX file. But alas, what happens when you work in different environments (such as Dev, Test, Prod...) and in each database, the schema of the tables is different?

Basically you either reset all your databases to use the same schema or worse - change the EDMX and recompile the assembly before updating the wanted environment.

But there is another solution, not a clear one, probably not a supported one, but still, it works.

Every EF model is accompanied by metadata, accessible from the MetadataWorkspace property of the model. This metadata class holds the collections of the conceptual, store and mapping items, and is loaded in sections: first of all the conceptual model is loaded, and before the first query is executed, the store and mapping model loads.

The idea is to interrupt to load process and change the schema name before any query is executed.

Look at this code:

   1: Employees e = new Employees();
   2:  
   3: XmlReader[] sReaders = new XmlReader[]{ XmlReader.Create(
   4:     Assembly.GetExecutingAssembly().GetManifestResourceStream("Employees.ssdl"))};
   5:  
   6: XmlReader[] mReaders = new XmlReader[]{ XmlReader.Create(
   7:     Assembly.GetExecutingAssembly().GetManifestResourceStream("Employees.msl"))};
   8:  
   9: StoreItemCollection sCollection = new StoreItemCollection(sReaders);
  10: EdmItemCollection cCollection = e.MetadataWorkspace.GetItemCollection(
  11:     DataSpace.CSpace) as EdmItemCollection;
  12:  
  13: StorageMappingItemCollection csCollection = 
  14:     new StorageMappingItemCollection(cCollection, sCollection, mReaders);
  15:  
  16: e.MetadataWorkspace.RegisterItemCollection(sCollection);
  17: e.MetadataWorkspace.RegisterItemCollection(csCollection);
  18:  
  19: EntityContainer container = 
  20:     e.MetadataWorkspace.GetItem<EntityContainer>(
  21:         "TestModelStoreContainer", 
  22:         DataSpace.SSpace);            
  23:                     
  24: EntitySetBase set = container.BaseEntitySets["Person"];
  25:  
  26: typeof(EntitySetBase).GetField(
  27:     "_schema",
  28:     BindingFlags.NonPublic | BindingFlags.Instance).SetValue(set, "dbo");
  29:           
  30: var q = from p in e.Person
  31:         select p;
  32:  
  33: Console.WriteLine(e.Person.ToTraceString());
  34: Console.WriteLine(q.First().Age);

Let's inspect the code:

Lines 3-7: We load the conceptual and storage xml from the resources of the containing assembly (if the code is run in another assembly, change the code to return the assembly that holds the model).

Lines 16-17: We must load both new parts to the MetadataWorkspace, otherwise we'll get an exception when trying to execute a query.

Lines 20-24: Extract an entity set definition from the store collection (if all tables are needed to be changed, you may want to use a foreach statement on the BaseEntitySets collection).

Lines 26-29: Here's the catch - in order to change the schema, we need to reset the value of a private field (the access property is an inline one). So we use wonderful reflection that allows us to do it (full trust required).

From there on, every time a query is built, it will use the wanted schema.

Note:

  1. Using this approach you can also change other stuff, like table names, defining queries and so on.
  2. There is a simpler way to load the store model (instead of writing rows 3-22), explained by Julie Lerman here, but it will work for examining the collection, not for changing it - the use of ToTraceString on an entity will cause the query to be cached, so future queries on the same entity will use the old schema name instead of the new. Loading the store collection first and then creating a query will use the new schema name.

 

 

Comments

Ido Flatow said:

If you're going to change the schema after some queries have been loaded, you should also set the field "_cachedProviderSql" to null after setting the new schema value (after line 28).

Setting this will cause already prepared queries and new queries on items which where queried before to use the new schema without the need to build a new model instance

# August 24, 2008 3:01 AM

Sam said:

You say it would be possible to change the table name, using this approach - do you maybe know which fields need to be changed to achieve this?

Namely I want to add the database name to the table name, and I'm not sure  what value I'd need to change to achieve this.

Thanks!

Sam

# March 4, 2009 1:28 PM

Nir P said:

Hi,

I can't thank you enough for this post, it helped me a lot.

This is working, however, only for queries manipulation but not for modification (update\insert\delete).

The schema stays the same, in the modification commans.

Any idea why?

Thanks!

Nir.

# April 18, 2010 8:19 PM

LM said:

@Nir

I am facing the same issue. Any solutions so far?

LM

# October 17, 2011 9:10 AM

Francisco said:

Same issue, Please can you let us know if this has been looked at. I would love to use this approach for updates/deletes and insterts.

# January 26, 2012 11:09 PM

Change Table Mapping EF with POCO objects | MSDN @ EEYOGO said:

Pingback from  Change Table Mapping EF with POCO objects | MSDN @ EEYOGO

# October 13, 2012 6:26 PM

Changing schema name on runtime – Entity Framework | MSDN @ EEYOGO said:

Pingback from  Changing schema name on runtime &#8211; Entity Framework | MSDN @ EEYOGO

# October 13, 2012 6:41 PM

Changing Database mapping on run time EF 4.0 | MSDN @ EEYOGO said:

Pingback from  Changing Database mapping on run time EF 4.0 | MSDN @ EEYOGO

# October 15, 2012 1:41 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: