LLBL Gen Pro 設計器使用指南


LLBL Gen Pro是個專業的ORM開發工具,官方網站是 http://www.llblgen.com/

LLBL Gen Pro是個支持多種持久層框架的ORM工具,如LLBL Gen Pro Runtime、Entity Framework、NHibernate和LINQ to SQL。其他一些新特性還有:

支持.NET 4.0、模型先行或數據庫先行的開發模式、模型視圖、項目驗證。LLBL Gen Pro有兩個主要的組件:一是設計器,

這是一個可視化工具,供開發者創建項目所用;二是運行時,這是與數據庫交互的持久層框架,用於執行映射操作。

1 設計實體類型

打開LLBL Gen程序,選擇創建新項目

clip_image002

打開Catelog Explorer,選擇從數據庫添加關系對象模型

clip_image004

輸入SQL Server實例名稱,登陸用戶和秘密,點”Test Connection”測試連接

clip_image006

測試連接成功后,點擊Next按鈕,進行到下一步,選擇數據庫表

clip_image008

如果需要支持存儲過程調用,可選擇勾選存儲過程

clip_image010

LLBL Gen開始讀取系統表信息,供生成實體定義。

在Catelog Explorer點選右鍵,選擇從表定義中生成實體

clip_image012

如果表名帶有簡寫,可以輸入一個有意義容易理解的實體名稱

clip_image014

LLBL Gen不建議的命名方法

1. 在表名前加前綴。比如給客戶表名定義成tblCustomer, 用戶表定義成tblUser,只要是表都加一個tbl標簽。如果是手寫SQL語句,這可以區分出表和自定義的實體,但是ORM會對每個生成的實體名稱后面加Entity,比如Employee表,默認會對生EmployeeEntity。

為此,LLBL Gen設計了名稱剔除(strip)模式,請看下圖中的配置屬性

clip_image016

2. 同樣的原理,存儲過程也不應該加sp_前綴,視圖也不應該加vw_。

2 修改實體屬性

在Project Exploer窗口中,選擇要修改的實體名,點擊編輯

clip_image018

默認情況下,不需要做修改,LLBL Gen會生成合適的數據庫與實體映射。但是有以下幾種情況需要修改。

2.1 空值類型

數據庫字段值允許為空,對應生成的代碼類型應該是NULLABLE,通常看到的可空類型。

生成的代碼例子

/// <summary> The CreatedDate property of the Entity User<br/><br/>

///Mapped on table field: User.CreatedDate</summary>

/// <remarks>Mapped on table field: "User"."CreatedDate"<br/>

/// Table field type characteristics (type, precision, scale, length): DateTime, 0, 0, 0<br/>

/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>

public virtual Nullable<System.DateTime> CreatedDate

{

get { return (Nullable<System.DateTime>)GetValue((int)UserFieldIndex.CreatedDate, false); }

set { SetValue((int)UserFieldIndex.CreatedDate, value); }

}

如果不想生成這種類型,可在Field mapping窗口中,選擇對應的屬性名稱,去掉Is nullable標簽。

clip_image020

再次生成代碼,它生成的屬性應該是這樣的

/// <summary> The CreatedDate property of the Entity User<br/><br/>

///Mapped on table field: User.CreatedDate</summary>

/// <remarks>Mapped on table field: "User"."CreatedDate"<br/>

/// Table field type characteristics (type, precision, scale, length): DateTime, 0, 0, 0<br/>

/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>

public virtual System.DateTime CreatedDate

{

get { return (Nullable<System.DateTime>)GetValue((int)UserFieldIndex.CreatedDate, false); }

set { SetValue((int)UserFieldIndex.CreatedDate, value); }

}

2.2 自定義屬性

LLBL Gen支持為實體增加自定義屬性,獨立於數據庫字段映射之外的屬性,相當於代碼生成器。

clip_image022

在生成代碼中,會產生額外的屬性,它的類型是Dictionary

private static void SetupCustomPropertyHashtables()

{

_customProperties = new Dictionary<string, string>();

_fieldsCustomProperties = new Dictionary<string, Dictionary<string, string>>();

Dictionary<string, string> fieldHashtable;

_fieldsCustomProperties.Add("UserName", fieldHashtable);

fieldHashtable = new Dictionary<string, string>();

fieldHashtable.Add("AllowEditForNewOnly", @"");

}

在運行時,可通過下面的方法,找到自定義屬性

// [C#]

Dictionary<string, string> userProperties = UserEntity.CustomProperties;

string description = userProperties ["AllowEditForNewOnly "];

2.3 類型轉化器

如果需要對值進行類型轉化。比如數據庫中存儲的是字符串,但要在程序中以bool類型來使用,可設置TypeConverter to use

clip_image024

強類型編程的好處是可以發現編譯時錯誤,而不是到運行才出現錯誤。

舉例說明,我們常用1或0表示銷售單已經過帳或沒有過帳,但是在程序中以1或0來設置,缺少編譯期間的檢查,即使你賦值時給它值3,也不會有編譯錯誤。如果換成bool類型,true表是已經過帳,false表示沒有過帳,則可以極大的增加可維護性。

請參考如下的代碼,了解類型轉換器,進一步的原理請參考.NET組件設計。

Namespace TypeConverters
{
    [Description("Converter with as core type System.Boolean, for mapping a field with a .NET type System.Boolean onto a string database field")]
    public class BooleanStringConverter : TypeConverter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="BooleanStringConverter"/> class.
        /// </summary>
        public BooleanStringConverter()
        {
        }

        /// <summary>
        /// Returns whether this converter can convert an object of the given type to the type of this converter (Boolean).
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="sourceType">A <see cref="T:System.Type"/> that represents the type you want to convert from.</param>
        /// <returns>
        ///  <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
        /// </returns>
        /// <remarks>Accepted types are: String</remarks>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            // any integer type is accepted. No fractional types like float/double.
            switch (sourceType.FullName)
            {
                case "System.String":
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Returns whether this converter can convert the object to the specified type.
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="destinationType">A <see cref="T:System.Type"/> that represents the type you want to convert to.</param>
        /// <returns>
        ///  <see langword="true "/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
        /// </returns>
        /// <remarks>Accepted types are: String. True will be converted to 1, false will be
        /// converted to 0.</remarks>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            // any integer type is accepted. No fractional types like float/double.
            switch (destinationType.FullName)
            {
                case "System.String":
                    return true;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Converts the given object to the type of this converter (Boolean).
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="culture">Ignored</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value, which is of type boolean.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The conversion could not be performed.</exception>
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            bool toReturn = true;
            switch (value.GetType().FullName)
            {
                case "System.String":
                    toReturn = ((string)value == "Y");
                    break;
                case "System.DBNull":
                    toReturn = false;
                    break;
                default:
                    throw new NotSupportedException("Conversion from a value of type '" + value.GetType().ToString() + "' to System.Boolean isn't supported");
            }

            return toReturn;
        }

        /// <summary>
        /// Converts the given value object to the specified type
        /// </summary>
        /// <param name="context">Ignored</param>
        /// <param name="culture">Ignored</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <param name="destinationType">The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value. The value will be 1 if <paramref name="value"/> is true, otherwise 0
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="destinationType"/> parameter is <see langword="null"/>.</exception>
        /// <exception cref="T:System.NotSupportedException">The conversion could not be performed.</exception>
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value", "Value can't be null");
            }

            if (!(value is bool))
            {
                throw new ArgumentException("Value isn't of type boolean", "value");
            }

            if ((bool)value)
            {
                switch (destinationType.FullName)
                {
                    case "System.String":
                        return (string)"Y";
                    case "System.DBNull":
                        return (string)"N";

                    default:
                        throw new NotSupportedException("Conversion to a value of type '" + destinationType.ToString() + "' isn't supported");
                }
            }
            else
            {
                switch (destinationType.FullName)
                {
                    case "System.String":
                    case "System.DBNull":
                        return (string)"N";
                    default:
                        throw new NotSupportedException("Conversion to a value of type '" + destinationType.ToString() + "' isn't supported");
                }
            }
        }

        /// <summary>
        /// Creates an instance of the Type that this <see cref="T:System.ComponentModel.TypeConverter"/> is associated with (bool)
        /// </summary>
        /// <param name="context">ignored.</param>
        /// <param name="propertyValues">ignored.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> of type bool. It always returns 'true' for this converter.
        /// </returns>
        public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
        {
            return true;
        }
    }
}

3 生成源代碼

設置好屬性后,可用生成源代碼,F7調出生成窗口

clip_image026

Template group中Adapter 模式把數據和數據的讀寫獨立成二個部分,SelfServicing則混合兩者

Adapter模式下,讀取用戶的代碼,如下所示

UserLogEntity _UserLog = new UserLogEntity(Logno);

using (DataAccessAdapterBase adapter = GetSystemDataAccessAdapter())

{

bool found = adapter.FetchEntity(_UserLog, prefetchPath, null, fieldList);

if (!found) throw new RecordNotFoundException("Invalid UserLog");

}

SelfServicing模式,則是這樣的

CustomerEntity customer = new CustomerEntity();

customer.FetchUsingPK("CHOPS");

實際過程中,生成器生成的代碼和自己加入的業務邏輯分開存放,當數據庫更新字段時,再次生成代碼,為了不覆蓋已經加入的代碼,常常要把自己的邏輯放到代碼生成器生成的代碼之外。

clip_image028

在我的項目中,我是將Database Generic和Database Specific兩個項目的代碼放在一起,在同一個項目中進行編譯。

clip_image030

框架中大量依賴於這個設置,對生成的實體代碼進行反射,讀取元數據。

 

4 MySQL 快速開發

4.1 MySQL 安裝配置

安裝MySQL, 使用查詢分析器連接數據庫服務器

clip_image032

創建數據庫CTU, 並且創建新表Agent

clip_image034

給數據表Agent添加三筆數據,Jack,Tony,Charles

clip_image036

數據庫設計的任務到此為止,記得給每個表添加主鍵。這里設置的主鍵是Name

4.2 LLBL Gen 設計實體模型

啟動LLBL Gen 3.1, 創建新的項目,注意選擇Target為LLBL Gen框架

clip_image038

在Category Explorer窗口中,右鍵,添加新連接

clip_image040

請在context菜單中選擇Reverse-engineer Tables to Entity Definitions

clip_image042

這些表就會自動mapping到實體集合中

clip_image044

從圖中可以看到,已經發現了Agent表

如果需要進一步的編輯屬性與字段的映射關系,請用Edit 菜單,打開窗體

clip_image046

在這里,可以設置關系,設置映射的名稱。

F7,可以開始生成代碼

clip_image048

默認情況下,會生成2個項目文件

clip_image050

從名稱中可以看出,一個是數據庫無關的,另一個是做數據訪問的。以此的原理是我們在生成數據庫時,選擇Adapter模式,而不是SelfServicing模式.

4.3 啟動程序

在Visual Studio中打開生成的代碼,並且添加測試項目(Console)

解決方案瀏覽器中現在有三個項目Main是我們用來測試數據訪問的

clip_image052

再來看一下Main方法,只有簡單的幾行程序,也就是一個最簡單的測試程序

UserEntity user=new UserEntity("Jack")
DataAccessAdapter adapter=new DataAccessAdapter();
adapter.FetchEntity(user);
string title=user.Description;

它會生成SQL語句 SELECT Name , Postion , Description FROM agent

 

LLBL Gen支持NHibernate,ADO.NET Entity Framework,可以考慮用它來作為ORM框架,實現三層架構中的數據訪問層。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM