zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型

    翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

    第七章 使用对象服务

      本章篇幅适中,对真实应用中的常见问题提供了切实可行的解决方案。我们构建的应用,应当具备在部署环境中接受改变的能力,我们将应用构建得足够灵活,使其几乎没有配置需要硬编码。

      前三节向你提供了应对这些挑战的办法。剩下的小节覆盖了诸如:实体框架的单复数服务、使用edmgen.exe实用工具、使用标识关系以及从ObjectContext中获取对象。

    7-1  动态构建连接字符串

    问题

      你想为你的应用动态构建连接字符串。

    解决方案

      许多真实应用一开始是在开发人员的电脑里,然后通过一个或多个测试,集成测试以及在过渡环境(staging environments)上的测试,最终才作为一个产品发布。你想依据当前环境来动态配置应用的连接字符串。

      按代码清单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文件中发现。但我们想从数据库读取它们的定义。为了能从数据库中读取这些定义,请按下面的步骤进行:

        1、右键设计器,查看属性。更改代码生成策略为None。我们将为我们的Customer类使用POCO;

        2、创建如图7-2所示的表,这张表将保存我们项目模型的定义;

    图7-2. 表Definitions,保存SSDL,CSDL和MSL的定义,注意字段的类型为XML

         3、右键设计器,查看属性。更改元数据项目处理(Metadate Artifact Processing)为“复制到输出目录”(Copy to Output Directory)。重新编译你的项目。编译过程将在输出目录生成三个文件:Recipe2.ssdl,Recipe2.csdl和Recipe2.msl;

        4、将上一步生成文件的内容插入到Definitions表中合适的列,Id列使用值1;

        5、使用代码清单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/

  • 相关阅读:
    LinkedBlockingQueue 单向链表实现的阻塞队列
    ArrayBlockingQueue 实现定时推送数据
    前台接收后端的JsonArray,循环输入页面表格
    一个常用的文件操作类
    DelayQueue 延时获取元素的无界阻塞队列
    单例设计模式
    使用SQL Server Data Tools (SSDT)比较和同步两个数据库的架构或者数据
    Entity Framework技能知识点测试2
    JS设置localStorage有效期
    Entity Framework技能知识点
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4541329.html
Copyright © 2011-2022 走看看