《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().CreateReader();
mslReader = reader.GetSqlXml().CreateReader();
ssdlReader = reader.GetSqlXml().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/
《Entity Framework 6 Recipes》中文翻译系列 (38) ------ 第七章 使用对象服务之动态创建连接字符串和从数据库读取模型的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (39) ------ 第七章 使用对象服务之配置模型和使用单复数服务
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-3 配置模型 问题 你想了解配置模型中的各种选项. 解决方案 当你添加一个AD ...
- 《Entity Framework 6 Recipes》中文翻译系列 (40) ------ 第七章 使用对象服务之从跟踪器中获取实体与从命令行生成模型(想解决EF第一次查询慢的,请阅读)
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-5 从跟踪器中获取实体 问题 你想创建一个扩展方法,从跟踪器中获取实体,用于数 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (41) ------ 第七章 使用对象服务之标识关系中使用依赖实体与异步查询保存
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 7-7 标识关系中使用依赖实体 问题 你想在标识关系中插入,更新和删除一个依赖实体 ...
- 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述
微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...
- 《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型
不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创 ...
- 《Entity Framework 6 Recipes》翻译系列(2) -----第一章 开始使用实体框架之使用介绍
Visual Studio 我们在Windows平台上开发应用程序使用的工具主要是Visual Studio.这个集成开发环境已经演化了很多年,从一个简单的C++编辑器和编译器到一个高度集成.支持软件 ...
- 《Entity Framework 6 Recipes》翻译系列 (3) -----第二章 实体数据建模基础之创建一个简单的模型
第二章 实体数据建模基础 很有可能,你才开始探索实体框架,你可能会问“我们怎么开始?”,如果你真是这样的话,那么本章就是一个很好的开始.如果不是,你已经建模,并在实体分裂和继承方面感觉良好,那么你可以 ...
- 《Entity Framework 6 Recipes》翻译系列 (5) -----第二章 实体数据建模基础之有载荷和无载荷的多对多关系建模
2-3 无载荷(with NO Payload)的多对多关系建模 问题 在数据库中,存在通过一张链接表来关联两张表的情况.链接表仅包含连接两张表形成多对多关系的外键,你需要把这两张多对多关系的表导入到 ...
- 《Entity Framework 6 Recipes》中文翻译系列 目录篇 -持续更新
为了方便大家的阅读和学习,也是响应网友的建议,在这里为这个系列做一个目录.在目录开始这前,我先来回答之前遇到的几个问题. 1.为什么要学习EF? 这个问题很简单,项目需要.这不像学校,没人强迫你学习! ...
随机推荐
- XACML学习
学习的网站: http://www.cinlk.com/2015/07/27/xacml/ http://www.cinlk.com/2015/08/22/swiftabac/ http://blog ...
- [原创]Centos7 从零编译Nginx+PHP+MySql
序言 这次玩次狠得.除了编译器使用yum安装,其他全部手动编译.哼~ 看似就Nginx.PHP.MySql三个东东,但是它们太尼玛依赖别人了. 没办法,想用它们就得老老实实给它们提供想要的东西. 首先 ...
- 高效快捷实用移动开单手持扫描打印一体智能 POS PDA
PDA数据采集器,是一款移动手持开单设备,它通过WIFI和GPRS连接并访问电脑,从进销存软件中读取数据,实现移动开单,打破电脑开单模式. 它自带扫描器,可直接扫描条码来查找产品,且功能强大.操作简单 ...
- [Leetcode] Number of Digit Ones
Given an integer n, count the total number of digit 1 appearing in all non-negative integers less th ...
- bzoj2342还是马拉车
就好比第一次写主席树的时候写了30行,第二次写了5行 这次马拉车只剩下 ,id=,mx=;i<=n;i++) { -i],mx-i):;a[i-p[i]]==a[i+p[i]+];p[i]++) ...
- 【BZOJ】3997: [TJOI2015]组合数学
题意 \(N \times M\)的网格,一开始在\((1, 1)\)每次可以向下和向右走,每经过一个有数字的点最多能将数字减1,最终走到\((N, M)\).问至少要走多少次才能将数字全部变为\(0 ...
- 基础3.Jquery操作Dom
1 内部插入节点 <body> <ul id="city"> <li id="bj" name=&qu ...
- System.Web.HttpContext.Current.Session为NULL解决方法
http://www.cnblogs.com/tianguook/archive/2010/09/27/1836988.html 自定义 HTTP 处理程序,从IHttpHandler继承,在写Sys ...
- EditPlus快捷键
Editplus 选中一行: ctrl+rEditplus 复制一行: ctrl+r选择行,然后ctrl+c复制.复制一行到下一行中:Editplus有:Ctrl+j 复制上一行的一个字符到当前行Ed ...
- mysql自动加入添加时间列
`addtime` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',