手把手教你:让EF动态支持新增表、动态支持多数据库
2016-07-25 10:16
441 查看
名词解释:此动态非运行时动态,让EF动态支持新增表、动态切换数据库意在不改变项目核心框架,
通过新增或者替换组件的方式达到标题目地。
一、先来点简单的,动态支持多数据库
AppDbContext实现:
在AppDbContext构造函数中添加configKey参数,通过configKey参数指定配置文件中的连接字符串的配置项;
创建IDbContextProvider接口,如下:
意图很明显了,通过IDbContextProvider来提供AppDbContext,这样我们首先将AppDbContext与业务层解耦;
继续创建2个项目:MsSqlProvider、MySqlProvider,分别实现IDbContextProvider接口:
MsSql:
MySQL:
下面继续解释动态支持/切换DbContextProvider,没错…聪明的你一开始就应该想到了..依赖注入,这个时候我们就需要使用依赖注入来完成使命了;
我已MEF为例来演示下如何动态获取2种DbContextProvider:
首先为我们的IDbContextProvider添加 [InheritedExport] 标记,然后分别为两种Provider添加 [Export]标记;
"MEF的使用还请大家自己去熟悉,我也仅仅是会使用而已,并不精通"
接着在Demo中添加App.Config和测试代码;
App.Config:
这里要提醒下哦:要使MySql能够支持EF使用的话,需要到MySql官方下载最新的驱动;
测试代码如下:
OK,我们来编译测试下,当应用程序目录下没有任何Provider的时候是没有获取到任何是不会获取到任何Provider的,如果只放置MySqlProvider再执行的话结果如下:
放置两项Provider组件的时候自然就会是两个都被获取到,我就不演示了;
到这里可能很多人就要嘘声一片了,也许你会提出一些问题:
比如:
1)为什么不做一个Provider的实现,在Get()方法或者构造函数中依赖注入参数呢?
其实这样做的目地是我们在使用UnitOfWork和Repository模式时能够简单方便的获取DbContext;
可参见示例:
2)我只需要一个DbContext,但有时候需要切换数据库,那怎么办呢?
这个问题是ico与依赖注入方面的基础内容,需要您自己去学习哦;
至此,简单的“动态”支持多数据库示例就完成了~~~ 我们的关键还是动态支持新建表,下面我们就来一步一步实践吧;
二、“动态”支持新建表,计划先行
首先我们创建ModelBase类库,存放一些与实体相关的接口和基类,结构如图所示:
根据项目结构,我需要给大家解释每个文件的存在意义;
IEntity接口与AbstractEntityBase类,顾名思义,大家应该猜得到它们是实体基类,为什么要如此定义呢,主要是方便我们写实体的时候直接继承Id属性,(因为我们的所有表主键都是Guid且名为Id)
还有一个好处就是我们直接在基类中描述 主键关系,在写实体的时候直接继承后,可以省去很多重复操作哦^_^
再来说IMapping和Mapping,为什么要有这2个基类接口呢,出于以下方面考虑:
1)将实体与数据库的映射关系产生Mapping类与DbContext类解耦(这个会在下面具体出现时再说)
2)通过MappingBase基类实现一些公共操作,避免每个实体类的重复操作,具体看代码你就会明白;
呵呵,有了“动态”支持多数据库,这里很多人应该就能猜到我们如何“动态”支持新增表咯;注意这里的IMapping接口的精妙所在哦,您发现了吗???;
三、万事俱备,只欠东风
我们先在ModelA类库中创建一个User实体和Role实体,同时创建UserMapping和RoleMapping,(为什么要创建Mapping类,后面我会讲)
USer 、UserMapping:
来指明表明呢,
通过新增或者替换组件的方式达到标题目地。
一、先来点简单的,动态支持多数据库
AppDbContext实现:
<span class="kwrd">public</span> <span class="kwrd">class</span> AppDbContext:DbContext
{
<span class="kwrd">public</span> AppDbContext(<span class="kwrd">string</span> configKey)
: base(configKey)
{
}
<span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> OnModelCreating(DbModelBuilder modelBuilder)
{
<span class="kwrd">base</span>.OnModelCreating(modelBuilder);
}
}
在AppDbContext构造函数中添加configKey参数,通过configKey参数指定配置文件中的连接字符串的配置项;
创建IDbContextProvider接口,如下:
<span class="kwrd">public</span> <span class="kwrd">interface</span> IDbContextProvider
{
AppDbContext Get();
}
意图很明显了,通过IDbContextProvider来提供AppDbContext,这样我们首先将AppDbContext与业务层解耦;
继续创建2个项目:MsSqlProvider、MySqlProvider,分别实现IDbContextProvider接口:
MsSql:
<span class="kwrd">public</span> <span class="kwrd">class</span> MsSqlProvider:IDbContextProvider
{
AppDbContext m_AppDbContext = <span class="kwrd">null</span>;
<span class="kwrd">public</span> AppDbContext Get()
{
<span class="kwrd">return</span> m_AppDbContext ?? <span class="kwrd">new</span> AppDbContext(<span class="str">"MsSql"</span>);
}
}
MySQL:
<span class="kwrd">public</span> <span class="kwrd">class</span> MySqlProvider:IDbContextProvider
{
AppDbContext m_AppDbContext = <span class="kwrd">null</span>;
<span class="kwrd">public</span> AppDbContext Get()
{
<span class="kwrd">return</span> m_AppDbContext ?? <span class="kwrd">new</span> AppDbContext(<span class="str">"MySql"</span>);
}
}
下面继续解释动态支持/切换DbContextProvider,没错…聪明的你一开始就应该想到了..依赖注入,这个时候我们就需要使用依赖注入来完成使命了;
我已MEF为例来演示下如何动态获取2种DbContextProvider:
首先为我们的IDbContextProvider添加 [InheritedExport] 标记,然后分别为两种Provider添加 [Export]标记;
"MEF的使用还请大家自己去熟悉,我也仅仅是会使用而已,并不精通"
接着在Demo中添加App.Config和测试代码;
App.Config:
<?xml version=<span class="str">"1.0"</span> encoding=<span class="str">"utf-8"</span> ?>
<configuration>
<connectionStrings>
<add name="MsSql" connectionString="Data Source=LIANG-HU-PC;Initial Catalog=appbase;Integrated Security=True;Pooling=False" providerName="System.Data.SqlClient" />
<add name=<span class="str">"MySql"</span> connectionString=<span class="str">"server=localhost;User Id=root;password=mysql;Persist Security Info=True;database=appbase"</span> providerName=<span class="str">"MySql.Data.MySqlClient"</span> />
</connectionStrings>
</configuration>
这里要提醒下哦:要使MySql能够支持EF使用的话,需要到MySql官方下载最新的驱动;
测试代码如下:
<span class="kwrd">class</span> Program
{
[ImportMany]
static IEnumerable<IDbContextProvider> m_Providers = null;
static void Main(string[] args)
{
//使用目录方式查找MEF部件
var catalog = <span class="kwrd">new</span> DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
<span class="rem">//创建Container</span>
var container = new CompositionContainer(catalog);
//获取Export项,这里直接加载不采用Lazy
m_Providers = container.GetExportedValues<IDbContextProvider>();
if (m_Providers != null)
{
foreach (var provider in m_Providers)
{
Console.WriteLine(provider.Get().Database.Connection.ConnectionString);
}
}
Console.ReadKey(<span class="kwrd">false</span>);
}
OK,我们来编译测试下,当应用程序目录下没有任何Provider的时候是没有获取到任何是不会获取到任何Provider的,如果只放置MySqlProvider再执行的话结果如下:
放置两项Provider组件的时候自然就会是两个都被获取到,我就不演示了;
到这里可能很多人就要嘘声一片了,也许你会提出一些问题:
比如:
1)为什么不做一个Provider的实现,在Get()方法或者构造函数中依赖注入参数呢?
其实这样做的目地是我们在使用UnitOfWork和Repository模式时能够简单方便的获取DbContext;
可参见示例:
<span class="rem">/// <summary></span>
/// Entity Framework Repository
<span class="rem">/// </summary></span>
/// <typeparam name="T"></typeparam>
<span class="kwrd">public</span> <span class="kwrd">class</span> EFRepository<T>:IRepository<T>
where T:class
{
readonly IDataBaseFactory m_DataBaseFactory = null;
public EFRepository(IDataBaseFactory dataBaseFactory)
{
if(dataBaseFactory==null)
{
throw new ArgumentNullException("DataBaseFactory");
}
m_DataBaseFactory=dataBaseFactory;
}
DbContext m_DbContext = <span class="kwrd">null</span>;
<span class="kwrd">protected</span> DbContext DbContext
{
get
{
<span class="kwrd">return</span> m_DbContext ?? m_DataBaseFactory.Get();
}
}
}
对UnitOfWork模式的使用与此类似;
2)我只需要一个DbContext,但有时候需要切换数据库,那怎么办呢?
这个问题是ico与依赖注入方面的基础内容,需要您自己去学习哦;
至此,简单的“动态”支持多数据库示例就完成了~~~ 我们的关键还是动态支持新建表,下面我们就来一步一步实践吧;
二、“动态”支持新建表,计划先行
首先我们创建ModelBase类库,存放一些与实体相关的接口和基类,结构如图所示:
根据项目结构,我需要给大家解释每个文件的存在意义;
IEntity接口与AbstractEntityBase类,顾名思义,大家应该猜得到它们是实体基类,为什么要如此定义呢,主要是方便我们写实体的时候直接继承Id属性,(因为我们的所有表主键都是Guid且名为Id)
<span class="kwrd">public</span> <span class="kwrd">interface</span> IEntity
{
Guid Id { get; }
}
<span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> AbstractEntityBase : IEntity
{
<span class="kwrd">public</span> AbstractEntityBase()
{
<span class="kwrd">this</span>.Id = Guid.NewGuid();
}
[Key]
[Required]
public Guid Id
{
get;
<span class="kwrd">protected</span> set;
}
}
还有一个好处就是我们直接在基类中描述 主键关系,在写实体的时候直接继承后,可以省去很多重复操作哦^_^
再来说IMapping和Mapping,为什么要有这2个基类接口呢,出于以下方面考虑:
1)将实体与数据库的映射关系产生Mapping类与DbContext类解耦(这个会在下面具体出现时再说)
2)通过MappingBase基类实现一些公共操作,避免每个实体类的重复操作,具体看代码你就会明白;
[InheritedExport]
public interface IMapping
{
void RegistTo(ConfigurationRegistrar configurationRegistrar);
}
public class MappingBase<TEntity> : EntityTypeConfiguration<TEntity>, IMapping
<span class="kwrd">where</span> TEntity : <span class="kwrd">class</span>,IEntity
{
<span class="kwrd">public</span> MappingBase()
{
<span class="kwrd">this</span>.Map(m => m.ToTable(<span class="kwrd">typeof</span>(TEntity).Name));
}
public void RegistTo(ConfigurationRegistrar configurationRegistrar)
{
configurationRegistrar.Add(this);
}
}
呵呵,有了“动态”支持多数据库,这里很多人应该就能猜到我们如何“动态”支持新增表咯;注意这里的IMapping接口的精妙所在哦,您发现了吗???;
三、万事俱备,只欠东风
我们先在ModelA类库中创建一个User实体和Role实体,同时创建UserMapping和RoleMapping,(为什么要创建Mapping类,后面我会讲)
USer 、UserMapping:
<span class="rem">/*</span>
* 为什么没有通过