《Entity Framework 6 Recipes》中文翻譯系列 (38) ------ 第七章 使用對象服務之動態建立鏈接字符串和從數據庫讀取模型

翻譯的初衷以及爲何選擇《Entity Framework 6 Recipes》來學習,請看本系列開篇

第七章 使用對象服務

  本章篇幅適中,對真實應用中的常見問題提供了切實可行的解決方案。咱們構建的應用,應當具有在部署環境中接受改變的能力,咱們將應用構建得足夠靈活,使其幾乎沒有配置須要硬編碼。html

  前三節向你提供了應對這些挑戰的辦法。剩下的小節覆蓋了諸如:實體框架的單複數服務、使用edmgen.exe實用工具、使用標識關係以及從ObjectContext中獲取對象。sql

7-1  動態構建鏈接字符串

問題數據庫

  你想爲你的應用動態構建鏈接字符串。app

解決方案框架

  許多真實應用一開始是在開發人員的電腦裏,而後經過一個或多個測試,集成測試以及在過渡環境(staging environments)上的測試,最終才做爲一個產品發佈。你想依據當前環境來動態配置應用的鏈接字符串。ide

  按代碼清單7-1中的方式,爲你的應用動態構建鏈接字符串。函數

代碼清單7-1. 動態構建鏈接字符串工具

 public static class ConnectionStringManager
    {
        public static string EFConnection = GetConnection();

        private static string GetConnection()
        {
            var sqlBuilder = new SqlConnectionStringBuilder();

            sqlBuilder.DataSource = ConfigurationManager.AppSettings["SqlDataSource"];

            // 填充剩下的
            sqlBuilder.InitialCatalog = ConfigurationManager.AppSettings["SqlInitialCatalog"];
            sqlBuilder.IntegratedSecurity = true;
            sqlBuilder.MultipleActiveResultSets = true;

            var eBuilder = new EntityConnectionStringBuilder();
            eBuilder.Provider = "System.Data.SqlClient";
            eBuilder.Metadata =
                  "res://*/Recipe1.csdl|res://*/Recipe1.ssdl|res://*/Recipe1.msl";
            eBuilder.ProviderConnectionString = sqlBuilder.ToString();
            return eBuilder.ToString();
        }

    }

   public partial class EF6RecipesContainer
    {
        public EF6RecipesContainer(string nameOrConnectionString)
            : base(nameOrConnectionString)
        {
            
        }
    }

原理學習

  當你添加一個ADO.NET實體數據模型到你的項目中時,實體框架會在項目的.config文件的<ConnectionStirngs>小節中添加一條目。在運行時,給上下文對象的構造函數傳入配置條目的鍵(本書中絕大章節使用的上下文是EF6RecipesContext)。經過給定的鍵,數據庫上下文會去.config文件中是查找鏈接字符串並使用。測試

  爲了根據應用所在的環境動態建立鏈接字符串,咱們建立了ConnectionStringManager類(如代碼清單7-1)。在GetConnection()方法中,咱們從配置文件中獲取了特定環境的data source和initial catalog。爲了能使用ConnectionStringManager,咱們在EF6RecipesContainer部分類中,增長了一個額外的構造函數,它接受一個表示鏈接字符串或鏈接字符串名稱的參數。

  當實例化EF6RecipesContainer時,咱們傳遞ConnectionStringManager.EFContection給它做爲參數。最終,它將使用動態建立的鏈接字符串來鏈接數據庫服務。

 

7-1  從數據庫中讀取模型

問題

  你想從數據表中讀取爲模型定義的CSDL,MSL和SSDL。

解決方案

  假設你有一個如圖7-1所示的模型。

圖7-1. 一個包含Cutomer實體的模型

   

   咱們的模型只有一個實體:Customer。概念層的CSDL),映射層的MSL和存儲層的SSDL,它們的定義一般能在你的項目中的.edmx文件中發現。但咱們想從數據庫讀取它們的定義。爲了能從數據庫中讀取這些定義,請按下面的步驟進行:

    一、右鍵設計器,查看屬性。更改代碼生成策略爲None。咱們將爲咱們的Customer類使用POCO;

    二、建立如圖7-2所示的表,這張表將保存咱們項目模型的定義;

圖7-2. 表Definitions,保存SSDL,CSDL和MSL的定義,注意字段的類型爲XML

     三、右鍵設計器,查看屬性。更改元數據項目處理(Metadate Artifact Processing)爲「複製到輸出目錄」(Copy to Output Directory)。從新編譯你的項目。編譯過程將在輸出目錄生成三個文件:Recipe2.ssdl,Recipe2.csdl和Recipe2.msl;

    四、將上一步生成文件的內容插入到Definitions表中合適的列,Id列使用值1;

    五、使用代碼清單7-2,從數據庫表Definitions中讀取元數據,並建立一個應用要使用的MetadateWorkSpace類;

代碼清單7-2.從表Definitions中讀取數據

  public static class Recipe2Program
    {
        public static void Run()
        {
            using (var context = ContextFactory.CreateContext())
            {
                context.Customers.AddObject(
                      new Customer { Name = "Jill Nickels" });
                context.Customers.AddObject(
                      new Customer { Name = "Robert Cole" });
                context.SaveChanges();
            }

            using (var context = ContextFactory.CreateContext())
            {
                Console.WriteLine("Customers");
                Console.WriteLine("---------");
                foreach (var customer in context.Customers)
                {
                    Console.WriteLine("{0}", customer.Name);
                }
            }            
        }


    }


 public class Customer
    {
        public virtual int CustomerId { get; set; }
        public virtual string Name { get; set; }
    }

 public class EFRecipesEntities : ObjectContext
    {
        private ObjectSet<Customer> customers;
        public EFRecipesEntities(EntityConnection cn)
            : base(cn)
        {
        }

        public ObjectSet<Customer> Customers
        {
            get
            {
                return customers ?? (customers = CreateObjectSet<Customer>());
            }
        }
    }

 public static class ContextFactory
    {
        static string connString = @"Data Source=localhost;
           initial catalog=EFRecipes;Integrated Security=True;";
        private static MetadataWorkspace workspace = CreateWorkSpace();

        public static EFRecipesEntities CreateContext()
        {
            var conn = new EntityConnection(workspace, 
                             new SqlConnection(connString));
            return new EFRecipesEntities(conn);
        }

        private static MetadataWorkspace CreateWorkSpace()
        {
            string sql = @"select csdl,msl,ssdl from Chapter7.Definitions";
            XmlReader csdlReader = null;
            XmlReader mslReader = null;
            XmlReader ssdlReader = null;

            using (var cn = new SqlConnection(connString))
            {
                using (var cmd = new SqlCommand(sql, cn))
                {
                    cn.Open();
                    var reader = cmd.ExecuteReader();
                    if (reader.Read())
                    {
                        csdlReader = reader.GetSqlXml(0).CreateReader();
                        mslReader = reader.GetSqlXml(1).CreateReader();
                        ssdlReader = reader.GetSqlXml(2).CreateReader();
                    }
                }
            }

            var edmCollection = new EdmItemCollection(new XmlReader[] 
                                                   { csdlReader });
            var ssdlCollection = new StoreItemCollection(new XmlReader[] 
                                                   { ssdlReader });
            var mappingCollection = new StorageMappingItemCollection(
                edmCollection, ssdlCollection, new XmlReader[] { mslReader });

            var localWorkspace = new MetadataWorkspace();
            localWorkspace.RegisterItemCollection(edmCollection);
            localWorkspace.RegisterItemCollection(ssdlCollection);
            localWorkspace.RegisterItemCollection(mappingCollection);
            return localWorkspace;
        }
    }

代碼清單7-2的輸出以下:

Customers
---------Jill Nickels
Robert Cole

原理

  代碼清單7-2的第一部分,對於你來講,應該是很是熟悉了。咱們使用實體框架建立了一個新的上下文對象,建立了一些實體對象,並調用SaveChages()方法將這些實體對象持久化到數據庫中。爲了獲取這些實體,咱們枚舉了整個集合,並將它們從控制檯輸出。惟一不一樣的是,咱們在建立上下文對象時調用ContextFactory.CreateConext()。 通常狀況下,咱們只須要使用new 操做符來獲取一個新的EFRecipesEntities上下文對象實例。

  咱們建立一個ContextFacotry,使用存儲的元數據來建立咱們的上下文對象,元數據不是存儲在.edmx文件,而是數據庫中。咱們使用CreateContext()方法來實現這個功能。CreateContext()方法建立了一個新的基於兩個參數的EntityConnection:咱們在CreateWorkSpace()方法中建立的 workspace,和一個SQL鏈接字符串。真正的工做發生在CreateWorkSpace()方法如何建立workspace的過程當中。

  CreateWorkSpace()方法,打開存儲元數據數據庫的鏈接,咱們構建一條SQL語句從Definitions表中讀取一行數據,Definitions表(如圖7-2)保存着,概念層、存儲層和映射層的定義。咱們使用Xmlreaders來讀取這些定義 。 有了這些定義數據後,咱們就能夠建立MetadataWorkspace的實例對象了。 MetadataWorkspace在內存中表明一個模型。通常地,這個workspace是實體框架的默認管道從.edmx文件建立的,而如今咱們是從數據庫表Definitions建立。還有別的方法能夠建立這個對象,這些方法包含使用嵌入資源和Code First來實現。

  代碼清單7-2使用POCO來表示Customer實體。雖然咱們在第八章才覆蓋POCO的內容,可是這裏使用POCO來簡化代碼。 有了POCO,咱們就不用使用實體框架生成的類。相反,咱們使用本身建立的,沒有依賴實體框架的類。在代碼清單7-2中,咱們使用Customer類來定義Customer實體。同時咱們還建立了本身的上下文對象EFRecipesEntities。 固然,咱們的上下文對象,依賴於實體框架,由於它繼承至ObjectContext。

 

 

 

實體框架交流QQ羣:  458326058,歡迎有興趣的朋友加入一塊兒交流

謝謝你們的持續關注,個人博客地址:http://www.cnblogs.com/VolcanoCloud/

相關文章
相關標籤/搜索