微软轻量级“代码生成器”—Repository Factory使用(下)

概述

Repository Factory是微软模式与实践小组发布的一个开发指南包,它把之前的Web Service Software Factory(WSSF)集成的Data Access Guidance Package分离出来,形成了一个单独的开发指南包。引用Johnny Halife的话说:“它不是一个对象-关系映射(Object-Relational Mapping,ORM)工具,它的目的是作为一个轻量级的代码生成器,以自动化完成绝大部分生成领域模型对象,并将之持久化到数据库的任务代码。”本文为微软轻量级“代码生成器”—Repository Factory使用下篇。

生成Data Repository 类

接上篇,生成存储过程脚本之后,我们执行脚本,在数据库中生成相应的存储过程。接下来就可以生成Data Repository类了。

Step1:选择Create Data Repository Classes

TerryLee_RF_016

Step2:仍然是指定数据库连接

TerryLee_RF_017

Step3:指定作为数据访问层的项目

TerryLee_RF_018

Step4:指定要生成代码的实体

TerryLee_RF_019

Step5:指定实体和存储过程之间的映射

TerryLee_RF_020

在这一步还可以指定存储过程参数和业务实体属性之间的映射关系

TerryLee_RF_021

Step6:生成代码:

生成Data Repository代码,生成的代码包括Repository接口和实现,分别针对每种操作生成一个工厂类。

生成的IDimCustomerRepository类:

public interface IDimCustomerRepository
{

    List<DimCustomer> GetAllFromDimCustomer();

    void Add(DimCustomer dimCustomer);

    void Remove(System.Int32 customerKey);

    void Save(DimCustomer dimCustomer);

}

生成的DimCustomerRepository类:

public class DimCustomerRepository : Repository<DimCustomer>, IDimCustomerRepository
{
    public DimCustomerRepository(string databaseName)
        : base(databaseName)
    {
    }

    public DimCustomerRepository()
        : base()
    {
    }


    public List<DimCustomer> GetAllFromDimCustomer()
    {
        ISelectionFactory<NullableIdentity> selectionFactory = new GetAllFromDimCustomerSelectionFactory();

        try
        {
            NullableIdentity nullableIdentity = new NullableIdentity();
            return base.Find(selectionFactory, new GetAllFromDimCustomerFactory(), nullableIdentity);
        }
        catch (SqlException ex)
        {
            HandleSqlException(ex, selectionFactory);
        }

        return new List<DimCustomer>();
    }

    public void Add(DimCustomer dimCustomer)
    {
        DimCustomerInsertFactory insertFactory = new DimCustomerInsertFactory();
        try
        {
            base.Add(insertFactory, dimCustomer);
        }
        catch (SqlException ex)
        {
            HandleSqlException(ex, insertFactory);
        }
    }

    public void Remove(System.Int32 customerKey)
    {
        IDeleteFactory<System.Int32> deleteFactory = new DimCustomerDeleteFactory();

        try
        {
            base.Remove(deleteFactory, customerKey);
        }
        catch (SqlException ex)
        {
            HandleSqlException(ex, deleteFactory);
        }
    }


    public void Save(DimCustomer dimCustomer)
    {
        DimCustomerUpdateFactory updateFactory = new DimCustomerUpdateFactory();
        try
        {
            base.Save(updateFactory, dimCustomer);
        }
        catch (SqlException ex)
        {
            HandleSqlException(ex, updateFactory);
        }
    }

    private void HandleSqlException(SqlException ex, IDbToBusinessEntityNameMapper mapper)
    {
        if (ex.Number == ErrorCodes.SqlUserRaisedError)
        {
            switch (ex.State)
            {
                case ErrorCodes.ValidationError:
                    string[] messageParts = ex.Errors[0].Message.Split(':');
                    throw new RepositoryValidationException(
                        mapper.MapDbParameterToBusinessEntityProperty(messageParts[0]),
                        messageParts[1], ex);

                case ErrorCodes.ConcurrencyViolationError:
                    throw new ConcurrencyViolationException(ex.Message, ex);

            }
        }

        throw new RepositoryFailureException(ex);
    }
}

DimCustomerInsertFactory类:

internal class DimCustomerInsertFactory : IDbToBusinessEntityNameMapper, IInsertFactory<DimCustomer>
{
    /// <summary>
    /// Creates the DimCustomerInsertFactory to build an insert statement for
    /// the given DimCustomer object.
    /// </summary>
    /// <param name="DimCustomer">New DimCustomer to insert into the database.</param>
    public DimCustomerInsertFactory()
    {
    }

    #region IInsertFactory<DimCustomer> Members

    public DbCommand ConstructInsertCommand(Database db, DimCustomer dimCustomer)
    {
        DbCommand command = db.GetStoredProcCommand("dbo.InsertDimCustomer");

        if (dimCustomer.AddressLine1 != null)
        {
            db.AddInParameter(command, "addressLine1", DbType.String, dimCustomer.AddressLine1);
        }
        if (dimCustomer.AddressLine2 != null)
        {
            db.AddInParameter(command, "addressLine2", DbType.String, dimCustomer.AddressLine2);
        }
        if (dimCustomer.BirthDate != null)
        {
            db.AddInParameter(command, "birthDate", DbType.DateTime, dimCustomer.BirthDate);
        }
        if (dimCustomer.CommuteDistance != null)
        {
            db.AddInParameter(command, "commuteDistance", DbType.String, dimCustomer.CommuteDistance);
        }
        if (dimCustomer.CustomerAlternateKey != null)
        {
            db.AddInParameter(command, "customerAlternateKey", DbType.String, dimCustomer.CustomerAlternateKey);
        }
        db.AddOutParameter(command, "customerKey", DbType.Int32, 4);
        if (dimCustomer.DateFirstPurchase != null)
        {
            db.AddInParameter(command, "dateFirstPurchase", DbType.DateTime, dimCustomer.DateFirstPurchase);
        }
        if (dimCustomer.EmailAddress != null)
        {
            db.AddInParameter(command, "emailAddress", DbType.String, dimCustomer.EmailAddress);
        }
        if (dimCustomer.EnglishEducation != null)
        {
            db.AddInParameter(command, "englishEducation", DbType.String, dimCustomer.EnglishEducation);
        }
        if (dimCustomer.EnglishOccupation != null)
        {
            db.AddInParameter(command, "englishOccupation", DbType.String, dimCustomer.EnglishOccupation);
        }
        if (dimCustomer.FirstName != null)
        {
            db.AddInParameter(command, "firstName", DbType.String, dimCustomer.FirstName);
        }
        if (dimCustomer.FrenchEducation != null)
        {
            db.AddInParameter(command, "frenchEducation", DbType.String, dimCustomer.FrenchEducation);
        }
        if (dimCustomer.FrenchOccupation != null)
        {
            db.AddInParameter(command, "frenchOccupation", DbType.String, dimCustomer.FrenchOccupation);
        }
        if (dimCustomer.Gender != null)
        {
            db.AddInParameter(command, "gender", DbType.String, dimCustomer.Gender);
        }
        if (dimCustomer.GeographyKey != null)
        {
            db.AddInParameter(command, "geographyKey", DbType.Int32, dimCustomer.GeographyKey);
        }
        if (dimCustomer.HouseOwnerFlag != null)
        {
            db.AddInParameter(command, "houseOwnerFlag", DbType.String, dimCustomer.HouseOwnerFlag);
        }
        if (dimCustomer.LastName != null)
        {
            db.AddInParameter(command, "lastName", DbType.String, dimCustomer.LastName);
        }
        if (dimCustomer.MaritalStatus != null)
        {
            db.AddInParameter(command, "maritalStatus", DbType.String, dimCustomer.MaritalStatus);
        }
        if (dimCustomer.MiddleName != null)
        {
            db.AddInParameter(command, "middleName", DbType.String, dimCustomer.MiddleName);
        }
        if (dimCustomer.NameStyle != null)
        {
            db.AddInParameter(command, "nameStyle", DbType.Boolean, dimCustomer.NameStyle);
        }
        if (dimCustomer.NumberCarsOwned != null)
        {
            db.AddInParameter(command, "numberCarsOwned", DbType.Byte, dimCustomer.NumberCarsOwned);
        }
        if (dimCustomer.NumberChildrenAtHome != null)
        {
            db.AddInParameter(command, "numberChildrenAtHome", DbType.Byte, dimCustomer.NumberChildrenAtHome);
        }
        
        //......
        return command;
    }

    public void SetNewID(Database db, DbCommand command, DimCustomer dimCustomer)
    {
        System.Int32 id1 = (System.Int32)(db.GetParameterValue(command, "customerKey"));
        dimCustomer.CustomerKey = id1;

    }

    #endregion

    #region IDbToBusinessEntityNameMapper Members
    public string MapDbParameterToBusinessEntityProperty(string dbParameter)
    {
        switch (dbParameter)
        {
            case "addressLine1":
                return "AddressLine1";
            case "addressLine2":
                return "AddressLine2";
            case "birthDate":
                return "BirthDate";
            case "commuteDistance":
                return "CommuteDistance";
            case "customerAlternateKey":
                return "CustomerAlternateKey";
            case "dateFirstPurchase":
                return "DateFirstPurchase";
            case "emailAddress":
                return "EmailAddress";
            //......
            default:
                throw new RepositoryInvalidParameterException(dbParameter);
        }
    }
    #endregion
}

并且会在配置文件中,自动配置Repository接口和实现之间的关系:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="repositoryFactory" type="Microsoft.Practices.Repository.Configuration.RepositoryFactorySection, Microsoft.Practices.Repository, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  </configSections>
  <connectionStrings>
    <add name="RFConnectionString" connectionString="Data Source=Esint-lhj\Sql2005;Initial Catalog=AdventureWorksDW;Persist Security Info=True;User ID=sa;Password=sql2005" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <repositoryFactory>
    <repositories>
      <add interfaceType="RepositoryFactoryDemo2.IDimEmployeeRepository, RepositoryFactoryDemo2" 
           repositoryType="RepositoryFactoryDemo2.DimEmployeeRepositoryArtifacts.DimEmployeeRepository, RepositoryFactoryDemo2" />
      <add interfaceType="RepositoryFactoryDemo2.IDimCustomerRepository, RepositoryFactoryDemo2" 
           repositoryType="RepositoryFactoryDemo2.DimCustomerRepositoryArtifacts.DimCustomerRepository, RepositoryFactoryDemo2" />
    </repositories>
  </repositoryFactory>
</configuration>

使用生成的代码

如下示例代码所示:

class Program
{
    static void Main(string[] args)
    {
        IDimEmployeeRepository repository = RepositoryFactory.Create<IDimEmployeeRepository>();

        List<DimEmployee> employees = repository.GetAllFromDimEmployee();
    }
}

自定义生成代码的风格

如果上面生成的代码风格,并不适合您现在所在团队的编码规范,譬如说您习惯于以“_”开头来命名业务实体中的私有字段。在Repository Factory中可以自定义生成代码的风格,因为Repository Factory中的代码生成也是基于模板引擎的,您可以通过修改模板来完成自定义生成代码的风格。打开<安装目录>\Microsoft Patterns & Practices\Data Access Guidance Package Setup\Templates,就可以看到生成代码所使用的模板了。模板编写说明:

1.以<#@ Template Language="C#" #>开头来指定一个模板

2.通过Assembly来添加对程序集的引用

<#@ Assembly Name="System.dll" #>

3.通过Import来导入命名空间

<#@ Import Namespace="System.Data" #>

4.通过Property来指定输入的参数

<#@ Property Processor="PropertyProcessor" Name="Entities"#>

5.通过include来引入外部的文件

<#@ include file="Templates\T4\Common\NamingHelper.t4" #>

6.完全使用C#语言来编写代码,是不是也可以通过<#@ Template Language="C#" #>来指定使用VB.NET编写,我没做过尝试:)

<# foreach(Property property in entity.Properties)
{
#>
        private <#= (property.IsNullable && property.Type.IsValueType) ? "Nullable<" + property.Type.ToString() + ">" : property.Type.ToString() #> <#= GetFieldName(property.Name) #>;       
        public <#= (property.IsNullable && property.Type.IsValueType) ? "Nullable<" + property.Type.ToString() + ">" : property.Type.ToString() #> <#= property.Name #>
        {
            get { return this.<#= GetFieldName(property.Name) #>; }
<#
if(!property.ReadOnly)
{
#>
            set { this.<#= GetFieldName(property.Name) #> = value; }
<#
}
#>
  }

譬如,想在生成的业务实体私有字段前都加上下划线“_”,可以打开NamingHelper.t4文件,修改其中的GetFieldName方法如下:

private string GetFieldName(string type)
{
    return "_" + NamingHelper.GetFieldName(type);
}

这时再使用Repository Factory时可以看到生成的代码如下,私有字段命名前都加上“_”:

private System.String _classField;

public System.String Class
{
    get { return this._classField; }
    set { this._classField = value; }
}

private System.String _colorField;

public System.String Color
{
    get { return this._colorField; }
    set { this._colorField = value; }
}

结束语

通过Repository Factory我们可以很方便的生成自己的数据访问层,减少重复的体力劳动,并且支持灵活的自定义功能。

祝大家编程愉快:)

参考:微软轻量级“代码生成器”—Repository Factory使用(上)

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2007-11-29 21:26 TerryLee 阅读(4876) 评论(29)  编辑 收藏 所属分类: Software Factory

  回复  引用  查看    
#1楼 2007-11-29 22:23 | 阿滨       
还让让我们这些代码工人活啊
  回复  引用  查看    
#2楼 2007-11-29 22:24 | 江南白衣      
可惜无法在2008下使用。不过有了linq to sql,这个工具包应该没多少人用,不过里面的一些模式还是可以借鉴的:)
  回复  引用  查看    
#3楼 [楼主]2007-11-29 22:25 | TerryLee      
@阿滨
:)
  回复  引用  查看    
#4楼 [楼主]2007-11-29 22:28 | TerryLee      
@江南白衣
对于还没有用LINQ to SQL的朋友来说,还可以用

//下个版本应该会支持吧?不过有了LINQ to SQL设计器,似乎这个东东无用武之地了:)
  回复  引用  查看    
#5楼 2007-11-30 08:32 | henry      
呵自己也在搞这样的插件,还有朋友找我合作没想到MS也出这玩意了.
  回复  引用  查看    
#6楼 [楼主]2007-11-30 08:44 | TerryLee      
@henry
你做的那个插件我看了,很不错,你是用NVelocity作为模板引擎的吗?

BTW:我最近也在这样的一个东西,有机会交流一下:)
  回复  引用  查看    
#7楼 2007-11-30 08:53 | henry      
@TerryLee
是的用NVelocity作代码模板,不过现功能针对class和function级别的,还没上升到project级别.
看来现在很多人看到了插件的威力了:)
  回复  引用  查看    
#8楼 [楼主]2007-11-30 08:59 | TerryLee      
@henry
:)
  回复  引用  查看    
#9楼 2007-11-30 09:09 | 怀念家驹      
数据库开发,最关心的是事务机制是怎么样的。
  回复  引用  查看    
#10楼 2007-11-30 09:34 | Clark Zheng      
呵呵,你们再上升就可以编出“专家系统”啦
  回复  引用  查看    
#11楼 2007-11-30 16:13 | 随风飘散      
使用了一个月后的结论就是,不如codesimth 方便灵活。
  回复  引用  查看    
#12楼 2007-11-30 20:19 | winzheng      
good ,微软开展了延长"程序员"生命的举动...^_^
  回复  引用  查看    
#13楼 [楼主]2007-11-30 21:15 | TerryLee      
@Clark Zheng
:)
  回复  引用  查看    
#14楼 [楼主]2007-11-30 21:16 | TerryLee      
@随风飘散
对于一个免费、开源并且具有自动化指导包的工具,就不要要求太高了,呵呵

毕竟CodeSmith是收费的
  回复  引用  查看    
#15楼 [楼主]2007-11-30 21:16 | TerryLee      
@winzheng
:)
  回复  引用    
#16楼 2007-12-01 10:40 | Ray Zhang [未注册用户]
对于LINQ TO SQL的支持会在近期加入。
对于LINQ TO ENTITY的支持还需要一段时间。
  回复  引用  查看    
#17楼 [楼主]2007-12-01 14:35 | TerryLee      
@Ray Zhang
不错哦,我也在做一个代码生成器,过段时间发布:)
  回复  引用    
#18楼 2007-12-03 18:20 | 玻璃纸包装机 [未注册用户]
代码生成器
  回复  引用    
#19楼 2007-12-04 18:58 | shanghai escort [未注册用户]
LINQ TO SQL的支持会在近期加入
  回复  引用    
#20楼 2007-12-19 16:35 | Eddy wang [未注册用户]
Terry,你好!
请问一下模板文件(\Microsoft Patterns & Practices\Data Access Guidance Package Setup\Templates)可以设置到具体项目的目录下吗?而不是安装目录嘛!因为我希望把它作为SVN的source!

  回复  引用  查看    
#21楼 [楼主]2007-12-19 19:46 | TerryLee      
@Eddy wang
不太好设置吧,我没尝试过
  回复  引用  查看    
#22楼 2007-12-25 14:36 | Helnet      
正在试用这个工具,请问,如果是自己写的涉及多表查询的存储过程,用这个工具怎么生成代码呢?以其中某个实体为主,还是能生成一个公共的实体?
还有个问题,好像生成的c# code和sp都没有涉及到事务,只能手工添加在sp或者调用的DataRepository的代码里?
  回复  引用  查看    
#23楼 2007-12-26 14:28 | Little Snail      
--引用--------------------------------------------------
阿滨 : 还让让我们这些代码工人活啊
--------------------------------------------------------
不让
  回复  引用  查看    
#24楼 2007-12-26 14:29 | Little Snail      
@Helnet
多表查询。。。 本来就违背了它的设计原则
  回复  引用  查看    
#25楼 2008-01-15 13:35 | 蓝天旭日      
hehe
  回复  引用    
#26楼 2008-03-06 14:54 | FLYabroad [未注册用户]
应该很快会支持vs2008的
  回复  引用    
#27楼 2008-04-06 19:03 | netwenchao [未注册用户]
真的是So郁闷呀!Vs2008快,用起来比vs2005强。但是还没支持DataAccessGuidancePage,看来毕业设计只好用vs2005了!可惜!

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接: