前面已经介绍了,IProvider提供了一种插件式的服务接口,能够将一些扩展的功能附加在提供者中,因为IProvider由IDatabase所引用,因此,只要有IDatabase,就能够获得所有的扩展功能,那么本篇将介绍一下这些扩展服务如何与IProvider一起工作。
首先看一下IProvider接口的定义:
/// 为不同的数据库类型提供创建工厂及插件服务。
/// </summary>
public interface IProvider
{
/// <summary>
/// 获取提供者类型。
/// </summary>
ProviderType ProviderType { get; }
/// <summary>
/// 获取数据库提供者工厂。
/// </summary>
DbProviderFactory DbProviderFactory { get; }
/// <summary>
/// 获取相应的插件服务。
/// </summary>
/// <typeparam name="T">插件类型。</typeparam>
/// <returns></returns>
T GetService<T>() where T : IProviderService;
/// <summary>
/// 附加一个插件服务。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="service"></param>
/// <param name="overlay"></param>
void RegisterService<T>(T service, bool overlay = false) where T : IProviderService;
/// <summary>
/// 初始化所有插件服务。
/// </summary>
/// <param name="action"></param>
void Initialize(Action<IProviderService> action);
/// <summary>
/// 获取所有插件服务。
/// </summary>
/// <returns></returns>
IEnumerable<IProviderService> GetServices();
/// <summary>
/// 获取当前连接的参数。
/// </summary>
/// <returns></returns>
ConnectionParameter GetConnectionParameter(ConnectionString connectionString);
}
本文着重关心的是IProviderServices的相关实现细节,因此其他内容不再赘述。
接下来定义一个实现类,当然也是一个抽象类,具体还要由相应的数据库提供者来实现。
/// 基本的数据库提供者。
/// </summary>
public abstract class BaseProvider : IProvider
{
private readonly Dictionary<string, IProviderService> m_plugs = new Dictionary<string, IProviderService>();
/// <summary>
/// 表示 <see cref="DbProviderFactory"/> 对象。
/// </summary>
protected DbProviderFactory Factory;
private Action<IProviderService> m_action;
/// <summary>
/// 初始化 <see cref="BaseProvider"/> 类的新实例。
/// </summary>
protected BaseProvider()
{
}
/// <summary>
/// 使用提供者名称初始化 <see cref="BaseProvider"/> 类的新实例。
/// </summary>
/// <param name="providerName"></param>
protected BaseProvider(string providerName)
{
Factory = DbProviderFactories.GetFactory(providerName);
}
/// <summary>
/// 获取数据提供者类型。
/// </summary>
public abstract ProviderType ProviderType { get; }
/// <summary>
/// 获取数据库提供者工厂。
/// </summary>
public virtual DbProviderFactory DbProviderFactory
{
get
{
if (Factory == null)
{
throw new Exception("DbProviderFactory 为空,请检查 providerName 或相关的程序集是否存在。");
}
return Factory;
}
}
/// <summary>
/// 获取相应的插件。
/// </summary>
/// <typeparam name="T">插件类型。</typeparam>
/// <returns></returns>
public virtual T GetService<T>() where T : IProviderService
{
var key = typeof(T).GUID.ToString();
if (m_plugs.ContainsKey(key))
{
return (T)m_plugs[key];
}
return default(T);
}
/// <summary>
/// 附加一个插件。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="provider"></param>
/// <param name="overwite"></param>
public void RegisterService<T>(T provider, bool overwite = false) where T : IProviderService
{
var implType = provider.GetType().GetDirectImplementInterface(typeof(IProviderService));
var key = implType.GUID.ToString();
if (!m_plugs.ContainsKey(key))
{
m_plugs.Add(key, provider);
if (m_action != null)
{
m_action(provider);
}
}
else if (overwite)
{
m_plugs[key] = provider;
if (m_action != null)
{
m_action(provider);
}
}
}
void IProvider.Initialize(Action<IProviderService> action)
{
if (action == null)
{
return;
}
m_action = action;
foreach (var kvp in m_plugs)
{
action(kvp.Value);
}
}
/// <summary>
/// 获取所有插件服务。
/// </summary>
/// <returns></returns>
IEnumerable<IProviderService> IProvider.GetServices()
{
return m_plugs.Select(kvp => kvp.Value);
}
/// <summary>
/// 获取当前连接的参数。
/// </summary>
/// <returns></returns>
public abstract ConnectionParameter GetConnectionParameter(ConnectionString connectionString);
}
着重在于RegisterService方法,为什么要用GetDirectImplementInterface方法呢?该方法用于从类型type中获得直接继承自IProviderServices接口的接口,呵呵,这个有点不清晰吧,就用前篇提到的IBatcherProvider接口为例来说吧,IBatcherProvider继承自IProviderServices,将我们用RegisterService方法注册一个IBatcherProvider的实现类时,实际上是获得了IBatcherProvider的类型,这主要是为了方便GetService方法的使用,我们想通过IProvider获得某种扩展服务的实例时,并不需要知道具体的实现类,而是使用GetService<IBatcherProvider>()这样的方式来获取。
在不同数据库提供者的实现中,分别注册进不同的扩展服务:
/// MsSql数据库提供者。
/// </summary>
public sealed class MsSqlProvider : BaseProvider
{
/// <summary>
/// 提供 <see cref="MsSqlProvider"/> 的静态实例。
/// </summary>
public readonly static MsSqlProvider Instance = new MsSqlProvider();
/// <summary>
/// 初始化 <see cref="MsSqlProvider"/> 类的新实例。
/// </summary>
public MsSqlProvider()
: base ("System.Data.SqlClient")
{
RegisterService(new MsSqlBatcher());
RegisterService(new MsSqlSyntax());
RegisterService(new MsSqlSchema());
RegisterService(new MsSqlBackup());
}
}
然而,IProviderServices接口有一个上下文对象,该上下文是通过Initialize方法附加到扩展服务中的。在Database的构造函数中,我们可以看到:
/// 初始化 <see cref="Database"/> 类的新实例。
/// </summary>
/// <param name="connectionString">数据库连接字符串。</param>
/// <param name="provider">数据库提供者。</param>
public Database (ConnectionString connectionString, IProvider provider)
: this ()
{
Checker.ArgumentNull(provider, "provider");
Provider = provider;
ConnectionString = connectionString;
var providerContext = new ServiceContext { Database = this };
Provider.Initialize(p => p.ServiceContext = providerContext);
}
通过委托的方式进行了初始化,当然前提是,这部份扩展服务是在提供者的构造里注册的,如果后期注册的,则在RegisterService里由m_action进行调用处理了。
这样,我们可以使用GetService来获取不同的扩展服务了:
var syntax = database.Provider.GetService<ISyntaxProvider>();
var schema = database.Provider.GetService<ISchemaProvider>();
var backup = database.Provider.GetService<IBackupProvider>();