通过新增或者替换组件的⽅式达到标题⽬地。
⼀、先来点简单的,动态⽀持多数据库
AppDbContext实现:
public class AppDbContext:DbContext
{
public AppDbContext(string configKey)
: base(configKey)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
在AppDbContext构造函数中添加configKey参数,通过configKey参数指定配置⽂件中的连接字符串的配置项;
创建IDbContextProvider接⼝,如下:
public interface IDbContextProvider
{
AppDbContext Get();
}
意图很明显了,通过IDbContextProvider来提供AppDbContext,这样我们⾸先将AppDbContext与业务层解耦;
继续创建2个项⽬:MsSqlProvider、MySqlProvider,分别实现IDbContextProvider接⼝:
MsSql:
public class MsSqlProvider:IDbContextProvider
{
AppDbContext m_AppDbContext = null;
public AppDbContext Get()
{
return m_AppDbContext ?? new AppDbContext("MsSql");
}
}
MySql:
public class MySqlProvider:IDbContextProvider
{
AppDbContext m_AppDbContext = null;销售经理月工作总结
public AppDbContext Get()
{
return m_AppDbContext ?? new AppDbContext("MySql");
}
}
下⾯继续解释动态⽀持/切换DbContextProvider,没错…聪明的你⼀开始就应该想到了..依赖注⼊,这个时候我们就需要使⽤依赖注⼊来完成使命了;我已MEF为例来演⽰下如何动态获取2种DbContextProvider:
⾸先为我们的IDbContextProvider添加 [InheritedExport] 标记,然后分别为两种Provider添加 [Export]标记;
"MEF的使⽤还请⼤家⾃⼰去熟悉,我也仅仅是会使⽤⽽已,并不精通"
接着在Demo中添加App.Config和测试代码;
App.Config:
<?xml version="1.0" encoding="utf-8" ?>
<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="MySql" connectionString="server=localhost;User Id=root;password=mysql;Persist Security Info=True;database=appbase" providerName="MySql.Data.MySqlClient" /> </connectionStrings>
周扬青怒斥罗志祥劈腿</configuration>
这⾥要提醒下哦:要使MySql能够⽀持EF使⽤的话,需要到MySql官⽅下载最新的驱动;
测试代码如下:
class Program
{
[ImportMany]
static IEnumerable<IDbContextProvider> m_Providers = null;
static void Main(string[] args)
{
//使⽤⽬录⽅式查MEF部件
var catalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
/
/创建Container
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(false);
}
泫雅 纹身OK,我们来编译测试下,当应⽤程序⽬录下没有任何Provider的时候是没有获取到任何是不会获取到任何Provider的,如果只放置MySqlProvider再执⾏的话结果如下:
放置两项Provider组件的时候⾃然就会是两个都被获取到,我就不演⽰了;
到这⾥可能很多⼈就要嘘声⼀⽚了,也许你会提出⼀些问题:
⽐如:
1)为什么不做⼀个Provider的实现,在Get()⽅法或者构造函数中依赖注⼊参数呢?
其实这样做的⽬地是我们在使⽤UnitOfWork和Repository模式时能够简单⽅便的获取DbContext;
黄国伦老婆可参见⽰例:
/// <summary>
/// Entity Framework Repository
/// </summary>
/// <typeparam name="T"></typeparam>
public class 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 = null;
protected DbContext DbContext
{
get
{
return m_DbContext ?? m_DataBaseFactory.Get();
}
}
}
对UnitOfWork模式的使⽤与此类似;
2)我只需要⼀个DbContext,但有时候需要切换数据库,那怎么办呢?
这个问题是ico与依赖注⼊⽅⾯的基础内容,需要您⾃⼰去学习哦;
⾄此,简单的“动态”⽀持多数据库⽰例就完成了~~~ 我们的关键还是动态⽀持新建表,下⾯我们就来⼀步⼀步实践吧;
⼆、“动态”⽀持新建表,计划先⾏
⾸先我们创建ModelBase类库,存放⼀些与实体相关的接⼝和基类,结构如图所⽰:
根据项⽬结构,我需要给⼤家解释每个⽂件的存在意义;
IEntity接⼝与AbstractEntityBase类,顾名思义,⼤家应该猜得到它们是实体基类,为什么要如此定义呢,主要是⽅便我们写实体的时候直接继承Id属性,(因为我们的所有表主键都是Guid且名为Id)
public interface IEntity
{
Guid Id { get; }
}
public abstract class AbstractEntityBase : IEntity
{
public AbstractEntityBase()
{
this.Id = Guid.NewGuid();舒奕橙简介
}
[Key]
[Required]
public Guid Id
{
get;
protected set;
}
}
还有⼀个好处就是我们直接在基类中描述主键关系,在写实体的时候直接继承后,可以省去很多重复操作哦^_^
再来说IMapping和Mapping,为什么要有这2个基类接⼝呢,出于以下⽅⾯考虑:
1)将实体与数据库的映射关系产⽣Mapping类与DbContext类解耦(这个会在下⾯具体出现时再说)
2)通过MappingBase基类实现⼀些公共操作,避免每个实体类的重复操作,具体看代码你就会明⽩;
[InheritedExport]
public interface IMapping
{
void RegistTo(ConfigurationRegistrar configurationRegistrar);
}
public class MappingBase<TEntity> : EntityTypeConfiguration<TEntity>, IMapping
where TEntity : class,IEntity
{
public MappingBase()
{
this.Map(m => m.ToTable(typeof(TEntity).Name));
}
public void RegistTo(ConfigurationRegistrar configurationRegistrar)
{
configurationRegistrar.Add(this);
}
}
呵呵,有了“动态”⽀持多数据库,这⾥很多⼈应该就能猜到我们如何“动态”⽀持新增表咯;注意这⾥的IMapping接⼝的精妙所在哦,您发现了吗;
三、万事俱备,只⽋东风
我们先在ModelA类库中创建⼀个User实体和Role实体,同时创建UserMapping和RoleMapping,(为什么要创建Mapping类,后⾯我会讲)
USer 、UserMapping:梦幻西游召唤兽进阶
/*
* 为什么没有通过[Table]来指明表明呢,
* 并不是因为我们需要EF⾃⼰⽀持的表明⽅式
* ⽽是我们继承⾃AbstractEntityBase,在其基类已经实现了将类名映射为表名
*/
public class User : AbstractEntityBase
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
/
*
* 注意这⾥,我为什么不通过DataAnnotations⽅式添加外键关联呢
* 个⼈认为User实体与Role实体关联,已经拥有Role属性了,
* 如果在添加⼀个RoleId来表⽰外键关系,会让我觉得User类不够清爽
* 所以我的做法是添加UserMapping类来指定它与Role实体的关系
*
* 但是有⼀点要注意,如果不指定外键的话,默认数据库外键是为表名_主键(Role_Id)类型
*/
public virtual Role Role{get;set;}
}
[Export("UserMapping")]
public class UserMapping:MappingBase<User>
{
public UserMapping()
{
this.HasRequired(m => m.Role)
.WithMany(m => m.Users);
/*注意这⾥没有指定HasForeignKey哦*/
}
}
Role类和RoleMapping的实现也是同理,结合上⾯代码中的注释内容,我想⼤家也能够理解我的良苦⽤⼼了吧;如果还不能理解,我们再看下DbContext是如何实现的:
/*
* 很清爽的DbContext,完全不包含任何DbSet
* 通过Mapping来加载表结构
*/
public class AppDbContext:DbContext
{
public AppDbContext(string configKey)
: base(configKey)
{
//可以设置通过反向⽅式创建表哦,但是我们演⽰的⽬地不在于此
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AppDbContext>());
//加载⽬录下所有IMapping实现
var catalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
var container = new CompositionContainer(catalog);
m_Mappings = container.GetExportedValues<IMapping>();
}
发布评论