EF6 Database First (DbContext) - Change Schema at runtime

2 篇文章 0 订阅

Problem:

There are two SQL databases (dev and live) with on Azure which has identical table structures but different table schema name. We need a way to change the schema name at runtime thus we can maintain one code base for the two database


Solution:

public class DBHelper
    {
        public static MyDBEntities Connect() 
        {
            if (ConfigurationManager.ConnectionStrings["DBConnection"] == null)
            {
                throw new ApplicationException("connectionStrings in .config file is missing a connection named 'connectionStrings'");
            }
            string connectionString = ConfigurationManager.ConnectionStrings["DBConnection"].ConnectionString;

            //parse schema name from database name
            SqlConnection conn = new SqlConnection(connectionString);
            string schema = conn.Database.Replace("_db", "");
            return Connect<MyDBEntities>(connectionString, schema);
        }
        private static T Connect<T>(string connectionString, string schema) where T : DbContext
        {
            var assembly = typeof(T).Assembly;

            var type = typeof(T);
            var resourceNames = assembly.GetManifestResourceNames();
            string contextName = null;

            // code used to avoid of getting the wrong csdl, used when you have more than one 
            // DbContext on your project
            foreach (var csdl in resourceNames.Where(r => r.EndsWith(".csdl")))
            {
                var csdlDocument = XDocument.Load(assembly.GetManifestResourceStream(csdl));
                XNamespace csdlNamespace = "http://schemas.microsoft.com/ado/2009/11/edm";
                var name = csdlDocument.Root.Elements(csdlNamespace + "EntityContainer").First().Attribute("Name").Value;

                if (type.Name.Equals(name))
                {
                    contextName = csdl.Replace(".csdl", "");
                    break;
                }
            }

            string csdlName = resourceNames.Single(r => r.Equals(contextName + ".csdl"));
            string ssdlName = resourceNames.Single(r => r.Equals(contextName + ".ssdl"));
            string mslName = resourceNames.Single(r => r.Equals(contextName + ".msl"));

            var ssdlDocument = XDocument.Load(assembly.GetManifestResourceStream(ssdlName));
            XNamespace ssdlNamespace = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
            var functions = ssdlDocument.Root.Elements(ssdlNamespace + "Function").ToList();

            foreach (var f in functions)
                f.SetAttributeValue("Schema", schema);

            var entitySets = ssdlDocument.Root.Elements(ssdlNamespace + "EntityContainer").ToList().Elements(ssdlNamespace + "EntitySet").ToList();

            foreach (var es in entitySets)
                if (es.Attribute("Schema") != null) es.SetAttributeValue("Schema", schema);

            Func<string, XmlReader[]> getFromResource = (name) =>
            {
                using (var s = assembly.GetManifestResourceStream(name))
                {
                    return new XmlReader[] { XDocument.Load(s).CreateReader() };
                }
            };

            var storeItems = new StoreItemCollection(new XmlReader[] { ssdlDocument.CreateReader() });
            var edmItems = new EdmItemCollection(getFromResource(csdlName));
            var mappingItems = new StorageMappingItemCollection(edmItems, storeItems, getFromResource(mslName));

            var workspace = new MetadataWorkspace();
            workspace.RegisterItemCollection(storeItems);
            workspace.RegisterItemCollection(edmItems);
            workspace.RegisterItemCollection(mappingItems);
            workspace.RegisterItemCollection(new ObjectItemCollection());
            workspace.LoadFromAssembly(assembly);
            
            var storeConn = new SqlConnection(connectionString);

            ConstructorInfo contextConstructor = typeof(T).GetConstructor(new Type[] { typeof(DbConnection)}); //require a partial class of the EF model to accept one parameter of type DbConnection
            var entityConn = new EntityConnection(workspace, storeConn);
            return (T)contextConstructor.Invoke(new Object[] { entityConn });
        }
    }

    public partial class MyDBEntities
    {
        public MyDBEntities(DbConnection existingConnection):base(existingConnection,true)
        {
        }
    }

then can use the entity like below with the DBHelper

using (var db = DBHelper.Connect())
{
  //you code here to use db
}



Note: if you use different EF version, you may need to change the csdl or ssdl namespace in above code

Reference:

http://blogs.msdn.com/b/dbrowne/archive/2013/08/30/entity-framework-schema-redirection.aspx

http://pastebin.com/UpqUzezq


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值