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/

  • 相关阅读:
    防火墙透明模式
    HP管理工具System Management Homepage安装配置
    kbmmw 中JSON 中使用SQL 查询
    kbmmw 中JSON 操作入门
    第一个kbmmw for Linux 服务器
    kbmmw 5.02发布
    kbmmw 5.01 发布
    使用delphi 10.2 开发linux 上的Daemon
    使用unidac 在linux 上无驱动直接访问MS SQL SERVER
    使用delphi 10.2 开发linux 上的webservice
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4541329.html
Copyright © 2011-2022 走看看