解析大型.NET ERP系統數據訪問 對象關係映射框架LLBL Gen Pro

LLBL Gen Pro是一個爲.NET開發人員設計的的對象關係映射(ORM)框架,與NHibernate,Entity Framework等框架同樣,經過實體與數據表的映射,實現關係數據庫持久化。html

1  LLBL Gen Pro 入門  LLBL Gen Pro Basic

打開LLBL Gen Pro程序,在右邊的數據庫瀏覽器(Catelog Explorer)中根結點右鍵選擇從關係數據庫建立關係模型(數據庫

Add Relational Model Data from a Database),而後根據SQL Server,而且填入登陸賬號和密碼。express

最終的界面以下圖的所示,點擊工具欄按鈕生成.NET項目文件和實體映射文件。編程

image

1.1 持久化類 Persistent classes

以數據庫表銷售合同爲例,它的數據庫表結構定義以下。瀏覽器

CREATE TABLE [dbo].[SLORDR]
(
[RECNUM] [decimal] (18, 0) NOT NULL IDENTITY(1, 1),
[CONTRACT_NO] [nvarchar] (20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[CUSTOMER_NO] [nvarchar] (8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CUSTOMER_NAME] [nvarchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CONTRACT_DATE] [datetime] NULL,
[REMARK] [nvarchar] (4000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CREATED_DATE] [datetime] NULL,
[CREATED_BY] [nvarchar] (8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[REVISED_DATE] [datetime] NULL,
[REVISED_BY] [nvarchar] (8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CLOSED] [nvarchar] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[SLORDR] ADD CONSTRAINT [PK_SLORDR] PRIMARY KEY CLUSTERED  ([CONTRACT_NO]) ON [PRIMARY]
GO

經過LLBL Gen Pro,生成的實體的代碼量比較多,上面的銷售合同數據表生成的實體類型定義源代碼有600多行。做爲入門的例子,先了解LLBL Gen Pro生成的構造方法和字段映射。先看銷售合同實體的構造方法:app

/// <summary> Static CTor for setting up custom property hashtables. Is executed before the first instance of this entity class or derived classes is constructed. </summary>
static SalesContractEntity()
{
    SetupCustomPropertyHashtables();
}
        
/// <summary> CTor</summary>
public SalesContractEntity():base("SalesContractEntity")
{
    InitClassEmpty(null, null);
}

/// <summary> CTor</summary>
/// <remarks>For framework usage.</remarks>
/// <param name="fields">Fields object to set as the fields for this entity.</param>
public SalesContractEntity(IEntityFields2 fields):base("SalesContractEntity")
{
    InitClassEmpty(null, fields);
}

/// <summary> CTor</summary>
/// <param name="validator">The custom validator object for this SalesContractEntity</param>
public SalesContractEntity(IValidator validator):base("SalesContractEntity")
{
    InitClassEmpty(validator, null);
}
                
/// <summary> CTor</summary>
/// <param name="contractNo">PK value for SalesContract which data should be fetched into this SalesContract object</param>
/// <remarks>The entity is not fetched by this constructor. Use a DataAccessAdapter for that.</remarks>
public SalesContractEntity(System.String contractNo):base("SalesContractEntity")
{
    InitClassEmpty(null, null);
    this.Fields[(int)SalesContractFieldIndex.ContractNo].CurrentValue = contractNo;
}

/// <summary> CTor</summary>
/// <param name="contractNo">PK value for SalesContract which data should be fetched into this SalesContract object</param>
/// <param name="validator">The custom validator object for this SalesContractEntity</param>
/// <remarks>The entity is not fetched by this constructor. Use a DataAccessAdapter for that.</remarks>
public SalesContractEntity(System.String contractNo, IValidator validator):base("SalesContractEntity")
{
    InitClassEmpty(validator, null);
    this.Fields[(int)SalesContractFieldIndex.ContractNo].CurrentValue = contractNo;
}

/// <summary> Protected CTor for deserialization</summary>
/// <param name="info"></param>
/// <param name="context"></param>
[EditorBrowsable(EditorBrowsableState.Never)]
protected SalesContractEntity(SerializationInfo info, StreamingContext context) : base(info, context)
{
    if(SerializationHelper.Optimization != SerializationOptimization.Fast) 
    {
              _salesContractDetail = (EntityCollection<SalesContractDetailEntity>)info.GetValue("_salesContractDetail", typeof(EntityCollection<SalesContractDetailEntity>));
      this.FixupDeserialization(FieldInfoProviderSingleton.GetInstance());
    }
// __LLBLGENPRO_USER_CODE_REGION_START DeserializationConstructor
// __LLBLGENPRO_USER_CODE_REGION_END
}

一共是7個構造方法,它們的做用說明以下:框架

1 靜態構造方法 調用SetupCustomPropertyHashtables方法以初始化屬性的自定義元數據。ide

2 默認無參數構造方法,以主鍵爲參數的構造方法應用於實際開發過程。函數

3 以IEntityFields2爲參數的構造方法,被框架使用。工具

4 以IValidator爲參數的構造方法,用於自定義驗證類型。

5 以SerializationInfo爲參數的方法用於序列化傳輸。

1.2 映射 Mapping

LLBL Gen Pro框架工具會生成2個項目文件,實體定義放在項目DatabaseGeneric中,實體與數據表的映射放在DatabaseSpecific,也就是前者是數據庫無關的,後者與數據庫的具體特性相關聯。好比生成存儲過程調用,則代碼生成會放到DatabaseSpecific項目中。來看一下上面的銷售合同表是如何與實體映射的。

/// <summary>Inits SalesContractEntity's mappings</summary>
private void InitSalesContractEntityMappings()
{
    this.AddElementMapping( "SalesContractEntity", "dbEnterprise", @"dbo", "SLORDR", 11 );
    this.AddElementFieldMapping( "SalesContractEntity", "Recnum", "RECNUM", false, "Decimal", 0, 0, 18, true, "SCOPE_IDENTITY()", null, typeof(System.Decimal), 0 );
     this.AddElementFieldMapping( "SalesContractEntity", "ContractNo", "CONTRACT_NO", false, "NVarChar", 20, 0, 0, false, "", null, typeof(System.String), 1 );
      this.AddElementFieldMapping( "SalesContractEntity", "CustomerNo", "CUSTOMER_NO", true, "NVarChar", 8, 0, 0, false, "", null, typeof(System.String), 2 );
      this.AddElementFieldMapping( "SalesContractEntity", "CustomerName", "CUSTOMER_NAME", true, "NVarChar", 50, 0, 0, false, "", null, typeof(System.String), 3 );
      this.AddElementFieldMapping( "SalesContractEntity", "ContractDate", "CONTRACT_DATE", true, "DateTime", 0, 0, 0, false, "", null, typeof(System.DateTime), 4 );
      this.AddElementFieldMapping( "SalesContractEntity", "Remark", "REMARK", true, "NVarChar", 4000, 0, 0, false, "", null, typeof(System.String), 5 );
      this.AddElementFieldMapping( "SalesContractEntity", "CreatedDate", "CREATED_DATE", true, "DateTime", 0, 0, 0, false, "", null, typeof(System.DateTime), 6 );
      this.AddElementFieldMapping( "SalesContractEntity", "CreatedBy", "CREATED_BY", true, "NVarChar", 8, 0, 0, false, "", null, typeof(System.String), 7 );
      this.AddElementFieldMapping( "SalesContractEntity", "RevisedDate", "REVISED_DATE", true, "DateTime", 0, 0, 0, false, "", null, typeof(System.DateTime), 8 );
      this.AddElementFieldMapping( "SalesContractEntity", "RevisedBy", "REVISED_BY", true, "NVarChar", 8, 0, 0, false, "", null, typeof(System.String), 9 );
      this.AddElementFieldMapping( "SalesContractEntity", "Closed", "CLOSED", true, "NVarChar", 1, 0, 0, false, "",  new ISL.TypeConverters.BooleanStringConverter(), typeof(System.String), 10 );
}
 

與NHiberate不一樣,LLBL Gen Pro將對象關係映射直接存儲在源代碼中,數據表字段與實體屬性的映射由代碼生成工具維護。

1.3 數據讀寫 Data access

能過如下幾行簡單的代碼例子,瞭解LLBL Gen Pro提供的數據訪問接口,下面的代碼適用於Adapter模式。

//讀取實體
DataAccessAdapter adapter...
SalesContractEntity salesContract = new SalesContractEntity("SC201507260001");
adapter.FetchEntity(salesContract, prefetchPath, null, fieldList);

//保存實體
DataAccessAdapter adapter...
SalesContractEntity  salesContract =new SalesContractEntity("SC201507260001");
salesContract .Customer="FLEXTRONICS";
adapter.SaveEntity(salesContract , true, false);

//刪除實體
DataAccessAdapter adapter...
SalesContractEntity salesContract...
adapter.DeleteEntity(salesContract);

DataAccessAdapter 是LLBL Gen Pro生成的項目DatabaseSpecific中的一個類型定義,是Adapter模式下標準的數據訪問接口。

 

2  數據訪問接口 Data access adapter

 

DataAccessAdapter  對Adapter模式,這個類型是數據訪問接口的所有。若是是自治(SelfServicing)模式,則數據的增刪改方法會直接附加到實體類型定義中。

EntityCollection 對象集合 實體的容器,通常用於數據設計時綁定和實體集的表示。有泛型和非泛型兩個版本。

泛型版本的建立例子以下代碼所示:

EntityCollection collection = new EntityCollection(new SalesContractEntityFactory());

 

EntityState 對象的狀態 參考下面的枚舉類型定義,對象可處於4種狀態,New表示對象在內存中剛剛建立,Fetched表示對象從數據庫中剛剛取到內存,OutOfSync表示對象的值與數據庫中的值不一致,Deleted表示對象已經被刪除。

public enum EntityState
{
    New,
    Fetched,
    OutOfSync,
    Deleted,
}

事務 爲保證數據操做中發生異常的回滾操做,事務的例子代碼以下所示。

try
{
        adapter.StartTransaction(IsolationLevel.ReadCommitted, "Post SalesContract");
        adapter.DeleteEntity(SalesContract);
        adapter.Commit();
}
catch
{
        adapter.Rollback();
        throw;
}

存儲過程 LLBL Gen Pro支持兩種存儲過程,一種須要返回值的定義爲RetrievalProcedures,另外一種是執行數據操做不返回結果的ActionProcedures。

數據庫鏈接  類型DataAccessAdapter封裝了數據訪問接口,好比打開與關閉數據庫鏈接,開啓事務。

adapter.OpenConnection();
adapter.CloseConnection();

 

3  對象關係映射 Object relational mapping

3.1 屬性映射 property mapping

以銷售合同表(SLORDR)的客戶編號(Customer_No),客戶名稱(Customer_Name),合同日期(Contract_Date)三個字段爲例子,參考下面的屬性與數據表字段定義的映射。

/// <summary> The CustomerNo property of the Entity SalesContract<br/>
/// Mapped on  table field: "SLORDR"."CUSTOMER_NO"<br/>
/// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 8<br/>
/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false<br/><br/></summary>
/// <remarks>Mapped on  table field: "SLORDR"."CUSTOMER_NO"<br/>
/// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 8<br/>
/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>
public virtual System.String CustomerNo
{
    get { return (System.String)GetValue((int)SalesContractFieldIndex.CustomerNo, true); }
    set { SetValue((int)SalesContractFieldIndex.CustomerNo, value); }
}

/// <summary> The CustomerName property of the Entity SalesContract<br/>
/// Mapped on  table field: "SLORDR"."CUSTOMER_NAME"<br/>
/// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 50<br/>
/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false<br/><br/></summary>
/// <remarks>Mapped on  table field: "SLORDR"."CUSTOMER_NAME"<br/>
/// Table field type characteristics (type, precision, scale, length): NVarChar, 0, 0, 50<br/>
/// Table field behavior characteristics (is nullable, is PK, is identity): true, false, false</remarks>
public virtual System.String CustomerName
{
    get { return (System.String)GetValue((int)SalesContractFieldIndex.CustomerName, true); }
    set { SetValue((int)SalesContractFieldIndex.CustomerName, value); }
}

/// <summary> The ContractDate property of the Entity SalesContract<br/>
/// Mapped on  table field: "SLORDR"."CONTRACT_DATE"<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<br/><br/></summary>
/// <remarks>Mapped on  table field: "SLORDR"."CONTRACT_DATE"<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 ContractDate
{
    get { return (System.DateTime)GetValue((int)SalesContractFieldIndex.ContractDate, true); }
    set { SetValue((int)SalesContractFieldIndex.ContractDate, value); }
}

 

LLBL Gen Pro框架依據表的主鍵生成實體的構造方法,設置主鍵字段的元數據,而不像NHibernate那樣,爲每一個實體生成一個Id主鍵,參考下面的代碼,重寫對象的ToString方法,返回主鍵值。

public override string ToString()
{
       List<string> builder = new List<string>();
       foreach (IEntityField2 field2 in Fields)
       {
            if (field2.IsPrimaryKey)
                 builder.Add(Convert.ToString(field2.CurrentValue));
       }
       return string.Join(",", builder.ToArray());
}

實體對象繼承於CommonEntityBase,CommonEntityBase繼承於EntityBase2,EntityBase2又繼承於IEntity2。

public abstract class EntityBase2 : IEntity2, IEntityCore, IEditableObject, IActiveContextParticipant, ITransactionalElement, ISerializable, IXmlSerializable, INotifyPropertyChanged, IDataErrorInfo, IEntityCoreInternal
{
}

這裏須要注意一下爲何有此類型的定義後面會加數字2,這是爲了支持兩種模式的實體類型定義。

SelfServicing 模式的實體定義接口是IEntity,Adapter模式的實體定義接口是IEntity2。在整個LLBL Gen Pro框架中,凡是與Adapter模式相關的基類會在類型後面加一個數字2,好比上面定義的EntityBase2 。

LLBL Gen Pro不支持實體對象的繼承,數據庫表生成的實體之間都是平行的關係。固然也能夠用面向對象的方式編程,增長一個類型繼承於實體類型定義,但不推薦這樣的寫法。

3.2 對象生命週期  Object lifecycle

事件 Events

  • Entity: Initializing 實體初始化前
  • Entity: Initialized 實體初始化完成
  • Entity collection: EntityRemoving 實體集合中的實體刪除前
  • Entity collection: EntityRemoved 實體集合中的實體刪除完成(Remove或RemoveAt)
  • Entity collection: EntityAdding 實體集合增長實體前
  • Entity collection: EntityAdded   實體集合增長實體完成

重寫方法 Overridable methods

  • Entity: OnFieldsCreated(僅適用於SelfServicing) 字段建立完成
  • Entity: OnFieldValueChanged 實體值更改完成。
  • Entity: OnGetValue 獲取值。
  • Entity: OnGetValueComplete 獲取值完成 。
  • Entity: OnInitializing 實體初始化
  • Entity: OnInitialized 初始化完成.
  • Entity: OnInitClassMembersComplete 初始化類成員完成。
  • Entity: OnRelatedEntitySet 相關實體設置.
  • Entity: OnRelatedEntityUnset 相關實體完成
  • Entity: OnSetValue 屬性設置值
  • Entity: OnSetValueComplete 設置值完成.
  • Entity (adapter): OnBeforeEntitySave 實體保存前
  • Entity / entity collection: OnGetObjectData 序列化時
  • Entity / entity collection: OnDeserialized 反序列化完成。
  • Entity collection: OnEntityRemoving 被刪除時。
  • Entity collection: OnEntityRemoved 實體集合中的實體刪除後。
  • Entity collection: OnEntityAdding 實體集合中增長實體前。
  • Entity collection: OnEntityAdded 實體集合中增長實體後.
  • DataAccessAdapter: OnInsertPersistenceInfoObjects
  • TypedList: OnResultsetBuilt
  • TypedList: OnRelationSetBuilt
  • TypedList: OnInitialized
  • TypedView: OnInitialized

 

3.3 實體驗證 Entity validate

在實體初始化類型定義中,指定驗證類型,參考下面的代碼。

this.Validator =new  SalesContractValidator();

SalesContractValidator驗證類型的完整代碼,主要重寫了保存前驗證和客戶編號值賦值驗證。

[Serializable]
public partial class SalesContractValidator : ValidatorBase
{
        // Add your own validation code between the two region markers below. You can also use a partial class and add your overrides in that partial class.
        // __LLBLGENPRO_USER_CODE_REGION_START ValidationCode
public override void ValidateEntityBeforeSave(IEntityCore involvedEntity)
{
      base.ValidateEntityBeforeSave(involvedEntity);
      SalesContractEntity salesContract = (SalesContractEntity)involvedEntity;

      if (string.IsNullOrEmpty(salesContract.ContractNo))
           throw new EntityValidationException("Contract No. is required");
      if (string.IsNullOrEmpty(salesContract.CustomerNo))
           throw new EntityValidationException("Customer No. is required");
          
      if (salesContract.IsNew)
      {
          ISalesContractManager salesContractManager = ClientProxyFactory.CreateProxyInstance<ISalesContractManager>();
         if (salesContractManager.IsSalesContractExist(Shared.CurrentUserSessionId, salesContract.ContractNo))
            throw new RecordDuplicatedException(salesContract.ContractNo, "Cotract No. is already used");
      }
}

public override bool ValidateFieldValue(IEntityCore involvedEntity, int fieldIndex, object value)
{
      bool result = base.ValidateFieldValue(involvedEntity, fieldIndex, value);
      if (!result) return false;

      switch ((SalesContractFieldIndex) fieldIndex)
      {
           case SalesContractFieldIndex.CustomerNo:
                return this.ValidateCustomerNo((string) value);
      }

      return true;
}

private bool ValidateCustomerNo(string value)
{
    if (!string.IsNullOrEmpty(value))
    {
         ICustomerManager customerManager = ClientProxyFactory.CreateProxyInstance<ICustomerManager>();
         customerManager.ValidateCustomerNo(Shared.CurrentUserSessionId, value);
     }

     return true;
}

// __LLBLGENPRO_USER_CODE_REGION_END

 

4  對象關係 Object relation

1  主從關係 Mater/Detail

LLBL Gen Pro根據表之間的主從關係,在生成源代碼時已經構建好了對象之間的關係。從表會增長一個集合屬性(EntityCollection)到主表所映射的類型中,同時從表也有一個主表的屬性方便引用主表的字段(屬性)。

以銷售合同爲例子,一個銷售合同包含多個銷售訂單,銷售合同與銷售訂單是一對多關係。銷售合同的源代碼定義文件:

[Serializable]
public partial class SalesContractEntity : CommonEntityBase
// __LLBLGENPRO_USER_CODE_REGION_START AdditionalInterfaces
// __LLBLGENPRO_USER_CODE_REGION_END    
{
    #region Class Member Declarations
    private EntityCollection<SalesContractDetailEntity> _salesContractDetail;

    // __LLBLGENPRO_USER_CODE_REGION_START PrivateMembers
    // __LLBLGENPRO_USER_CODE_REGION_END
    #endregion

在查詢時,要查詢出銷售合同和它的銷售訂單明細表,可參考下面的代碼。

IPrefetchPath2 prefetchPath = new PrefetchPath2((int) EntityType.SalesContractEntity);
prefetchPath.Add(SalesContractEntity.PrefetchPathSalesContractDetail);
SalesContractEntity  salesContract = _salesContractEntityManager.GetSalesContract("SC201507270001", prefetchPath);
 

IPrefetchPath2用於LLBL Gen Pro關係查詢,再來看一個三層結構的查詢例子:

IPrefetchPathElement2 prefetchElement = prefetchPath.Add(SalesOrderEntity.PrefetchPathSalesOrderDetails);
prefetchElement.SubPath.Add(SalesOrderDetailEntity.PrefetchPathSalesOrderOrderLots);

第一層表是銷售訂單SalesOrder,第二層是銷售訂單明細(物料編號,數量,單價),第三層是銷售訂單物料明細下的批號(Lot)。對於數據關係中已經創建好的關係,LLBL Gen Pro都會爲咱們生成關係的類型定義,也支持經過代碼構建關係,參考下面的查詢語句。

IEntityRelation entityRelation = new EntityRelation(OrderLinkFields.OrderNo, JobOrderFields.JobNo, RelationType.OneToMany);
IRelationPredicateBucket filterBucket.Relations.Add(entityRelation); 

2  關係類型 Entity relation

先來看一下LLBL Gen Pro定義的關係類型,參考下面的枚舉定義。

  public enum RelationType
  {
    OneToMany,
    OneToOne,
    ManyToOne,
    ManyToMany,
  }

實際應用中多對多關係的例子比較少,多對多的關係應該要考慮增長數據表,分解成一對多的關係。

 

 

5  數據查詢  Query

5.1 SELECT子句

認識兩個類型ExcludeFieldsList和IncludeFieldsList,及以ExcludeIncludeFieldsList。從名稱中能夠推測出類型的含義,IncludeFieldsList是包含要選擇的字段,ExcludeFieldsList是排除不讀取的字段,ExcludeIncludeFieldsList經過傳入構造方法參數,簡化以上兩個類型的的使用。好比下面的代碼,是讀取指定的字段:

ExcludeIncludeFieldsList custSepcFieldList = new ExcludeIncludeFieldsList(false);
custSepcFieldList.Add(CustomerSpecificationFields.Description);
custSepcFieldList.Add(CustomerSpecificationFields.SalesUom);
custSepcFieldList.Add(CustomerSpecificationFields.LotSize);
custSepcFieldList.Add(CustomerSpecificationFields.CustItemNo);
custSepcFieldList.Add(CustomerSpecificationFields.Specifications);

翻譯成SQL語句,要選擇的字段(IncludeFieldsList),好比讀取客戶編號和客戶名稱:

SELECT CustomerNo,CustomerName

排除不讀取的字段,好比物料主檔中圖片比較大,SELECT語句中不包含圖片字段,就須要用這個類型的寫法。

除非真的須要,儘可能不要寫SELECT * 讀取全部字段。

5.2  FROM子句

LLBL Gen Pro根據讀取的數據類型(Entity,TypeList) 自動生成FROM部分。參考讀取一個實體的代碼:

CustomerSpecificationEntity customerSpec = new CustomerSpecificationEntity(customerNo, itemNo);
adapter.FetchEntity(customerSpecificationEntity, prefetchPath, null, fieldList);

再看下面讀取一個列表(TypeList)的寫法,返回一個結果集DataTable。

ResultsetFields fields = new ResultsetFields(1);
fields.DefineField(JobOrderFields.JobNo, 0);

IRelationPredicateBucket bucket = new RelationPredicateBucket();
bucket.PredicateExpression.Add(JobOrderFields.Posted == true);
bucket.PredicateExpression.Add(JobOrderFields.Finished == true);
bucket.PredicateExpression.Add(JobOrderFields.Closed == false);

System.Data.DataTable table = queryManager.GetQueryResult(fields, bucket, null, null, true, false);
 

5.3  WHERE 子句與條件表達式

IRelationPredicateBucket類型是SQL中WHERE語句部分的面向對象封裝,IRelationPredicateBucket包含條件和關係,IPredicateExpression 只包含條件,IEntityRelation只包含關係。

IPredicateExpression 舉例以下,

IPredicateExpression B = ((Table1Fields.Foo == "One") & (Table1Fields.Bar == "Two")) // A
    | (Table2Fields.Bar2 == "Three");
 

LLBL Gen Pro官方幫助文件中列舉了一個詳細的SQL語句與查詢類型中的映射關係,摘要以下。

SQL 語句

Predicate派生類型

Field BETWEEN 3 AND 5
Field BETWEEN field2 AND 4

FieldBetweenPredicate

Field = Field2
Field < (Field2 * 4)

FieldCompareExpressionPredicate

Field Is NULL

FieldCompareNullPredicate

Field IN (1, 2, 3, 5)

FieldCompareRangePredicate

Field IN (
SELECT Field FROM Foo WHERE ...)

FieldCompareSetPredicate

Field = 3
Field != "Foo"

FieldCompareValuePredicate

Field LIKE "Foo%"

FieldLikePredicate

再來看一下關係,若是是在數據庫中有創建表之間的主從關係,則可直接用LLBL Gen Pro生成的關係類型,好比一個客戶可對應多個聯繫地址,客戶與聯繫地址是一對多的關係:

CustomerEntity.Relations.CustomerContactEntityUsingCustomerNo

若是沒有在數據庫中創建關係,則須要經過IEntityRelation類型建立關係,參看下面的例子代碼。

IEntityRelation entityRelation = new EntityRelation(JobOrderMaterialLedgerFields.JobNo, JobOrderMaterialFields.JobNo, RelationType.OneToMany);

5.4 數據排序 ORDER BY

ISortExpression接口用於封裝排序相關的操做,參考下面的例子。

ISortExpression sortExpression = new SortExpression();
sortExpression.Add(SalesOrderFields.OrderNo | SortOperator.Ascending);

支持多個字段排序,排序優先級與排序字段的增長順序有關。

5.5 數據分組 GROUP BY

IGroupByCollection接口封裝分組相關的操做。來看一個例子代碼,建立分組接口的實例。

IGroupByCollection groupBy = new GroupByCollection(SalesOrderFields.OrderNo);

支持多個字段分組,能夠借用下面的代碼來建立多個字段分組的查詢。

IGroupByCollection groupByClause = new GroupByCollection();
groupByClause.Add(JobOrderFields.JobNo);
groupByClause.Add(JobOrderFields.BomNo);

 

5.6 聚合函數  Aggregate functions

LLBL Gen Pro幫助文檔中有一篇《Generated code - Field expressions and aggregates》是專門講解聚合函數的,參考下面簡單的例子。

ResultsetFields fields = new ResultsetFields(2);
fields.DefineField(CustomerFieldIndex.Country, 0, "Country");
fields.DefineField(CustomerFieldIndex.CustomerID, 1, "AmountCustomers");
fields[1].AggregateFunctionToApply = AggregateFunction.CountDistinct;
 

也能夠直接讀取數據,求一個聚合函數的返回值,例子代碼以下。

DataAccessAdapter adapter = new DataAccessAdapter();
decimal orderPrice = (decimal)adapter.GetScalar(OrderDetailsFields.OrderId, 
        (OrderDetailsFields.Quantity * OrderDetailsFields.UnitPrice), AggregateFunction.Sum, 
        (OrderDetailsFIelds.OrderId == 10254));
 
 
 

5.7 子查詢 Subquery

先看SQL語句,讀取客戶編號和它的訂單數量。

SELECT CustomerID, 
( SELECT COUNT(*) FROM Orders WHERE CustomerID = Customers.CustomerID ) AS NumberOfOrders FROM Customers

用LLBL Gen Pro Adapter模式實現,代碼以下:

ResultsetFields fields = new ResultsetFields(2); 
fields.DefineField(CustomerFields.CustomerID, 0); 
fields.DefineField(new EntityField2("NumberOfOrders", new ScalarQueryExpression(OrderFields.OrderId.SetAggregateFunction(AggregateFunction.Count), (CustomerFields.CustomerId == OrderFields.CustomerId))), 1); 
DataTable results = new DataTable(); 
adapter.FetchTypedList(fields, results, null); 
相關文章
相關標籤/搜索