65.9K
CodeProject 正在变化。 阅读更多。
Home

Antler:.NET 中你喜欢的 ORM 抽象 (第一部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (15投票s)

2014年5月28日

CPOL

4分钟阅读

viewsIcon

30310

downloadIcon

199

使用相同的语法来处理不同的 ORM。

引言

在某个时候,我们意识到在开发各种 .NET 项目时,我们会不时地重复做一些类似的事情。通常,我们的项目具有以下共同点:

  • 使用某个 ORM 以 Code First 的方式与数据库交互。
  • 使用某个 IoC 容器。
  • 以某种方式使用 UnitOfWork/Repository 模式。
  • 拥有解耦的架构和良好的可测试性。

因此,我们决定实现一个完全可插拔的开源框架,使用相同的语法来处理流行的 ORM。

现在,包括瑞银投资银行在内的几家主要组织都在使用这个框架。也许其他人也会觉得它有用,甚至会有兴趣为其贡献力量。

请将本文视为对 Antler 框架的初步介绍。

框架目标

  • 使用通用的语法来处理不同的 ORM,这样我们就可以轻松地替换一个 ORM。
  • 拥有非常简单的“流畅”配置。
  • 完全可插拔。例如,选择使用哪个 ORM、IoC 容器或数据库应该非常容易。
  • 使用该框架的应用程序应该是可轻松测试的(单元测试和集成测试)。

概念解释

ORM 抽象

核心思想是拥有统一的语法来处理任何 ORM。这是通过完全解耦的风格来实现 UnitOfWork、Generic Repository 等模式的。

统一的语法提供了无痛切换不同 ORM 的能力:你只需要更新应用程序引导程序中的一行配置,并更新实体的映射。即使你未来不打算切换 ORM,这种 ORM-无关的架构也会使你的代码更清晰。

配置非常简单,下面的示例展示了如何配置应用程序以使用 EntityFramework ORM 和 Castle Windsor 容器

var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(EntityFrameworkStorage.Use.WithConnectionString(connectionString).
                        WithMappings(assemblyMithMappings)); 

然后,您就可以使用统一的语法来处理数据库。例如,在数据库中插入数据:

UnitOfWork.Do(uow =>
                { 
                  uow.Repo<Team>().Insert(new Team() {Name = "Penguins", Description = "Hockey"});
                  uow.Repo<Team>().Insert(new Team() {Name = "Capitals", Description = "Hockey"});
                  uow.Repo<Team>().Insert(new Team() {Name = "Nets", Description = "Basketball"});
                });   

从数据库查询

var found  = UnitOfWork.Do(uow => uow.Repo<Team>().AsQueryable().
                                                   Where(t => t.Description == "Hockey").
                                                   OrderBy(t => t.Name).ToArray());  

UnitOfWork 在这里代表了底层数据库事务的包装器。Generic Repository 提供了 IQueryable 接口来查询数据库,并提供了一组标准操作,如 Insert、Delete 等。

但是,如果出于某些原因需要使用 ORM 特有的语法,当然,你可以访问“内部” ORM 的会话。当需要执行某些不适合统一 Antler 语法的 ORM 特有操作时,你可能需要这样做。

例如,你可以访问“内部” NHibernate 会话 ISession 并使用 NHibernate 的 QueryOver 方法,如下所示

UnitOfWork.Do(uow =>
                  {
                    var internalSession = uow.SessionScope.GetInternal<ISession>();
                    var result = internalSession.QueryOver<Team>().Where(t => t.Name == "Awesome")
                                                                  .List();
                     //do something with result
                   });   

目前,Antler 框架支持 NHibernate、EntityFramework 和 Linq2Db 这三种 ORM。

IoC 抽象

IoC 容器在 Antler 框架中也有其抽象。在应用程序的引导程序中安装你喜欢的容器后,你的代码中就没有对其的依赖了。所有与容器的工作都通过 IContainer 接口完成,该接口看起来像这样:

public interface IContainer
 {        
    T Get<T>();        
    T Get<T>(string name);        
    objectGet(Type type);        
    object Get(Type type, string name);        
    IList<T> GetAll<T>();        
    IList GetAll(Type type);        
    void Release(object instance);        
    void Put(IBindingSyntax binding);        
    bool Has<T>();        
    bool Has(Type type);        
    bool Has<T>(string name);        
    bool Has(Type type, string name);
  } 

下面的示例展示了如何配置应用程序以使用 StructureMap 容器以及 NHibernate + SqlServer

var configurator = new AntlerConfigurator();
configurator.UseStructureMapContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(MsSqlConfiguration.
                        MsSql2008.ConnectionString(connectionString)).
                        WithMappings(assemblyWithMappings));  

目前,Antler 框架支持 Castle Windsor 和 StructureMap 这两种 IoC 容器。

顺便说一下,非常感谢 Kostassoid 完成了这一部分。

可测试性

如前所述,Antler 框架的目标是提供轻松切换 ORM、IoC 和数据库的能力。这在编写功能集成测试时非常有用,因为你可以轻松地配置测试环境。

例如,如果你的应用程序使用 NHibernate + Oracle,你可能会在引导程序中有以下配置:

var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(
                        OracleDataClientConfiguration.Oracle10.ConnectionString(connectionString).
                        DefaultSchema(dbSchemaName)).WithMappings(assemblyWithMappings)); 

然后,你可能不想为集成测试创建一个另一个臃肿的 Oracle 数据库。相反,你可以配置你的测试项目使用 NHibernate + Sqlite 内存数据库。在这种情况下,你的测试项目将使用与你的应用程序相同的 ORM 映射,但数据库不同。

var configurator = new AntlerConfigurator();
configurator.UseWindsorContainer()
            .UseStorage(NHibernateStorage.Use.WithDatabaseConfiguration(SQLiteConfiguration.
                        Standard.InMemory()).WithMappings(assemblyWithMappings)); 

可插拔结构

框架以“可插拔”风格实现。“Antler.Core”项目包含所有抽象和共享功能。还有其他项目(适配器)包含具体的实现。例如,NHibernate ORM 或 Castle Windsor IoC 容器的适配器。

因此,如果我们想在项目中使用 NHibernate 作为 ORM,Castle Windsor 作为 IoC 容器,那么我们需要从 NuGet 安装 Antler.Core 主库和 Antler.NHibernate/Antler.Windsor 适配器。然后,如果我们决定更改 ORM,例如,从 NHibernate 更改为 EntityFramework,那么我们只需要从 NuGet 替换 Antler.NHibernate 适配器为 Antler.EntityFramework。

类似地,如果需要,我们可以更改 IoC 容器适配器(例如,从 Antler.Castle 更改为 Antler.StructureMap)。

目前,有 NHibernate、EntityFramework、Linq2Db 的 ORM 适配器,以及 Castle Windsor 和 StructureMap 的 IoC 适配器。

结论

尽管 Antler 框架已被投入生产使用,但未来还有大量工作要做。我们计划添加对其他流行 IoC 容器的适配器,并且有一个雄心勃勃的想法是将 Antler 框架适配到 NoSql 解决方案(MongoDb 等)。

非常欢迎贡献。 如果您有兴趣,可以在 GitHub 上找到项目页面

如果您只想尝试一下这个框架,您可以轻松地从 NuGet 安装它:

核心库,以及 NHibernateEntityFrameworkLinq2Db Castle WindsorStructureMap 的适配器。

如果您在安装/使用过程中遇到问题或有任何疑问,请告诉我。感谢您的关注。

继续阅读(第二部分)

© . All rights reserved.