• XAF-Domain Components 技术 使用接口来定义ORM业务对象


    一、简介 

    Domain Component组件技术,以下简称DC,扩展XPO的, 官方不建议新手使用DC
    如果你用过EF,XPO及类似的ORM,这是很容易理解的,DC是基于XPO的,只是原来定义ORM对象时用的是类,现在改用接口
    然后通过DC上声明的一些Attribute来生成最终的类代码,在运行时编译,最终还是生成了xpo的类。
     
    当然接口只是可定义一些结构,方法,属性及一些个性化的逻辑并没有实现,再使用一个Logic类,来做真正的实现。对于一些默认的读写功能的属性,不需要在logic类中实现。DC机制会默认生成一个实现。
     
    • 可以创建可重用领域对象:多数情况下,每个XAF中用到的领域对象都不是唯一的,比较常见的对如:人、电话、地址,等领域对象,这些使用频率较高的,想要抽象出来还是有点难度的,这不是一个简单的任务,使用DC这事就简单了

    • 可以使用多重继承:因为DC是用接口描述的,所以,多继承在C#的语法级别被支持,你可以使用继承以前写过的DC,重用它,并且可以增加新的属性和替换逻辑。事实上,这是我最喜欢的一个特性!

    • 需要持久性化基类继承实现领域对象 :最终的执行代码是生成的,这当然很容易实现了。当然,也可以指定基类。
     
      注意:
    • DC组件技术支持Model First和Database first的方式我们推荐使用附加(就是两个或多个)数据库因此我们提供任何手段现有数据库生成组件代码逻辑我们没有立即计划支持方案试试 XPO 实体框架数据模型
    • 自定义字段不能设计添加组件
    • 如果一个DC组件注册为SharePart不能添加自定义字段

     二、DC定义

     下面的代码片段演示如何定义一个DC
    复制代码
    [DomainComponent]
    public interface IPerson {
        string LastName { get; set; }
        string FirstName { get; set; }
        string FullName { get; }
        void Copy(IPerson target);
    }
    复制代码

    你可以看到,接口上必须使用DomainComponentAttribute 来声明接口是个DC.接口的属性就是将来出来表的字段.在普通BO定义中使用的一些Attribute现在仍可用.例如你可以给LastName 上面加上 RuleRequiredFieldAttribute, 给接口上加上 NavigationItemAttributeFullName 被定义为只读的.它需要在logic类中定义实现.另外Copy方法也必须在logic中实现.


     

    三、注册DC

    只有注册了DC后,才会被XAF生成真正的XPO类,下面是注册方法,需要打开Module.cs文件,实override下面的方法:
     
    复制代码
    using DevExpress.Persistent.BaseImpl;
    // ... 
    public override void Setup(XafApplication application) {
        base.Setup(application);
        XafTypesInfo.Instance.RegisterEntity("Person", typeof(IPerson));
    }
    复制代码

     上面的注册中,并没有指定基类,所以将会默认使用DCBaseObject 做为基类,如果要指定基类,可以看下RegisterEntity的其它重载方法。


    四、Domain Logic

    每个DC可以有一(零)个或多个Domain Logic. Domain Logic 是一个普通的类,加上了 DomainLogicAttribute 标记, 并指定DC类型. 其实再多的话都没有一个代码实例有用:

    复制代码
    [DomainLogic(typeof(IPerson))]   //必须写个,IPerson是指为哪个DC的逻辑
    public class PersonLogic {       //类别是任意的
        public const string FullNameSeparator = " ";
        public string Get_FullName(IPerson person) {  
        //Get_XXX Get_是固定的,实现property的get的方法,FullName是属性的名称 return string.Format("{0}{1}{2}", person.FirstName, FullNameSeparator, person.LastName); } public static void Copy(IPerson person, IPerson target) {
        //实现了上面定义的Copy方法,但是,注意,第一个参数,在接口中并没有定义,但在这里却可以出现,也可以不出现,调用时会被自动替换为当前对象 if(target != null) { target.FirstName = person.FirstName; target.LastName = person.LastName; } } }
    复制代码

    上面的示例中,还可以看到,Get_FullName是非静态的,Copy是静态的,事实上,是不是静态的都没有关系,都会被调用,当然,你可以想像一下,静态方法是不需要实例化对象的。将来被调用时,是不会实例化PersonLogic这个类的。

    否则就会实例化。当然,类中一个非静态方法都没有时,才会不实例化logic类。

    那么,DC中的语法到底有多少呢?

    说明示例

    Get_属性名称

    当property的get被调用时,就执行这个方法。
    如果属性是只有get或,非持久化时,才可以实现这个,否则都自动实现。

    public static string Get_FullName(IMyInterface instance)
    public static string Get_FullName(IMyInterface instance, IObjectSpace objectSpace)

    Set_属性名

    当property的set被调用时,就执行这个方法。
    属性必须不能是只读的,即,DC中声明了set,使用这个方法实现非持久化属性。

    public static void Set_FullName(IMyInterface instance, string value)
    public static void Set_FullName(IMyInterface instance, IObjectSpace objectSpace, string value)

    BeforeChange_属性名

    当属性被设置新值之前被调用 
    属性必须是非只读的。

    public static void BeforeChange_FirstName(IMyInterface instance, string value)
    public static void BeforeChange_FirstName(IMyInterface instance, IObjectSpace objectSpace, string value)

    AfterChange_属性名

    当属性被设置新值之后被调用. 
    非只读属性可用.

    public static void AfterChange_FirstName(IMyInterface instance)
    public static void AfterChange_FirstName(IMyInterface instance, IObjectSpace objectSpace)

    方法名称

    接口上定义了一个方法定义,那个方法被调用时,执行此处的逻辑。
    接口上要是没定义这个方法,那它就是普通方法了。 

    public static void CalculateSalary(IMyInterface instance, int amount, int price)
    public static void CalculateSalary(IMyInterface instance, IObjectSpace objectSpace, int amount, int price)

    AfterConstruction

    Bo中有也有这个,就是新建对象完成后,可以在这里写一些初始化属性值的操作。

    public static void AfterConstruction(IMyInterface instance)
    public static void AfterConstruction(IMyInterface instance, IObjectSpace objectSpace)

    OnDeleting

    Bo中也有这个,当删除时执行。

    public static void OnDeleting(IMyInterface instance)
    public static void OnDeleting(IMyInterface instance, IObjectSpace objectSpace)

    OnDeleted

    Bo中有,删除后执行。

    public static void OnDeleted(IMyInterface instance)
    public static void OnDeleted(IMyInterface instance, IObjectSpace objectSpace)

    OnSaving

    Bo中有也有这个,保存中执行。

    public static void OnSaving(IMyInterface instance)
    public static void OnSaving(IMyInterface instance, IObjectSpace objectSpace)

    OnSaved

    Bo中有也有这个,保存完成执行。

    public static void OnSaved(IMyInterface instance)
    public static void OnSaved(IMyInterface instance, IObjectSpace objectSpace)

    OnLoaded

    Bo中有也有这个,已经的对象,被加载后执行。

    public static void OnLoaded(IMyInterface instance)
    public static void OnLoaded(IMyInterface instance, IObjectSpace objectSpace)
     

    上面的方法,必须是静态或是非静态的,必须为public,参数的定义可以是以下几种情况:

    • LogicMethodName(source_parameters)
      与DC中定义的方法是一致的。
    • LogicMethodName(target_interfacesource_parameters)
      当前DC类型,指当前对象和接口中定义的那些参数.
    • LogicMethodName(target_interfaceobject_spacesource_parameters)
      与上面的相对,多了一个object_space,用过xpo+xaf的同学一看就懂了,就是指当前对象用的objectspace,因为有时我们需要使用objectspace进行一些crud操作.

    五、示例:

    之前的Logic你看起来可能感觉有点麻烦,下面来看看一种简写方法:

    复制代码
    [DomainComponent]
    public interface IPerson {
        string FirstName { get; set; }
        [NonPersistentDc]
        string FullName { get; set; }
    }
    [DomainLogic(typeof(IPerson))]
    public class PersonLogic {
        IPerson person;
        public PersonLogic(IPerson person) {
            this.person = person; //构造逻辑时就传入了当前对象
        }
    //像BO中一样直接写property public string FullName { get { return person.FirstName; } set { person.FirstName = value; } } }
    复制代码

    下面是静态的实现方法:

    复制代码
    [DomainComponent]
    public interface IContact {
        static string Name { get; }
    }
    [DomainLogic(typeof(IContact))]
    public class ContactLogic {
        public static string Name {
            get { return "a constant string"; }
        }
    }
    复制代码

    下面是如何使用ObjectSpace的示例:

    复制代码
    [DomainLogic(typeof(IPerson))]
    public class AdditionalPersonLogic {
        public static void AfterConstruction(IPerson person, IObjectSpace objectSpace) {
            person.Address = objectSpace.CreateObject<IAddress>();
        }
    }
    复制代码

    下面来看看重写别的DC中定义的逻辑

    复制代码
    [DomainComponent]
    public interface IPerson {
        [ImmediatePostData]
        string FirstName { get; set; }
        [ImmediatePostData]
        string LastName { get; set; }
        string DisplayName { get; }
    }
    [DomainLogic(typeof(IPerson))]
    public class IPerson_Logic {
        public string Get_DisplayName(IPerson person) {
            return person.FirstName + " " + person.LastName;
        }
    }
    [DomainComponent]
    public interface IClient : IPerson {
        [ImmediatePostData]
        string ClientID { get; set; }
    }
    [DomainLogic(typeof(IClient))]
    public class IClient_Logic {
        public string Get_DisplayName(IClient client) { 
        //这里重写了IPerson_Logic中的定义,相当于bo中的override return client.ClientID; } }
    复制代码

    下面演示了如何为collection属性返回值:

    复制代码
    [DomainComponent]
    public interface IOrder {
        [NonPersistentDc]
        IList<IOrderLine> OrderLines { get; }
    }
    [DomainLogic(typeof(IOrder))]
    public class OrderLogic {
        public IList<IOrderLine> Get_OrderLines(IOrder order) {
            //... 
        }
    }
    复制代码

    下面的IUser并不是一个DC定义(没用[DomainComponent]来定义,这时,必须在逻辑中为IsActive和UserName两个属性的实现。否则是不能运行通过的。

    复制代码
    public interface IUser {
        bool IsActive { get; set; }
        string UserName { get; }
    }
    [DomainComponent]
    public interface IPerson : IUser {
        string LastName { get; set; }
        string FirstName { get; set; }
    }
    复制代码

    程序集包含DC组件时,可以通过过 ITypesInfo.RegisterEntity 方法来注册,也可以通过 ITypesInfo.RegisterDomainLogic  ITypesInfo.UnregisterDomainLogic 的方法手动注册逻辑,当访问DC Logic来源需要操作DC逻辑分配时这很有用

     


     

    六、一对多和多对多关系的定义

    在DC中,你不需要使用 Association来定义一对多和多对多关系.下面的代码片段演示了如何定义订单与订单明细关系.

    复制代码
    [DomainComponent]
    public interface IOrder {
        IList<IOrderItem> Items { get; }
    }
    [DomainComponent]
    public interface IOrderItem {
        IOrder Order { get; set; }
    }
    复制代码

    下面是多对多关系:

    复制代码
    [DomainComponent]
    public interface IEmployee {
        IList<ITask> Tasks { get; }
    }
    [DomainComponent]
    public interface ITask {
        IList<IEmployee> Employees { get; }
    }
    复制代码

    你可以只定义一端的属性,比如,IEmplyee.Tasks,另一端的,将会自动生成。当然在XAF的界面中,ITask.Employees将不会被显示出来。

    下面的情况时,生成器不知道该如何生成代码,所以需要BackReferenceProperty来指定对方的属性:

    复制代码
    [DomainComponent]
    public interface IAccount {
        [BackReferenceProperty("AccountOne")]
        IList<IContact> ContactA { get; }
        [BackReferenceProperty("AccountTwo")]
        IList<IContact> ContactB { get; }
        IList<IContact> ContactC { get; }
    }
    [DomainComponent]
    public interface IContact {
        string Name { get; set; }
        IAccount AccountOne { get; set; }
        IAccount AccountTwo { get; set; }
        IAccount AccountThree { get; set; }
    }
    复制代码

    七、Shared Parts

    当一个DC被几个DC同时继承时,这个DC必须要注册为SharePart,使用ITypesInfo.RegisterSharedPart 方法完成.

    复制代码
    [DomainComponent]
    public interface IWorker { }
    
    [DomainComponent]
    public interface IManager : IWorker { }
    
    [DomainComponent]
    public interface IEvangelist : IWorker { }
    
    public class MyModule : ModuleBase {
        // ... 
        public override void Setup(XafApplication application) {
            base.Setup(application);
            XafTypesInfo.Instance.RegisterEntity("Manager", typeof(IManager));
            XafTypesInfo.Instance.RegisterEntity("Evangelist", typeof(IEvangelist));
            XafTypesInfo.Instance.RegisterSharedPart(typeof(IWorker));   //<-----这里
        }
    }
    复制代码

     


     

    八、DC特有的Attribute

    除了XAF中在BO中使用的Attribute,DC又增加了几个Attribute:

    Attribute说明
    BackReferencePropertyAttribute 前面已经看到了,是用来明确的指定对方属性的。
    CreateInstanceAttribute Applied to methods. Specifies that a Domain Component's target method will create Domain Component instances.
    DomainComponentAttribute 用了这个,接口才叫DC。
    DomainLogicAttribute DC逻辑类标识。
    NonPersistentDcAttribute 非持久化的DC,使用时也需要标识上DomainComponentAttribute
    PersistentDcAttribute 与BO中的PersistentAttribute 是一样的。可以指定一个表名。
  • 相关阅读:
    字符串练习题
    js
    百度商桥--提供网站与用户之间交流平台
    git从本地上传到码云
    命名单词
    swiper 点击切换,拖动切换后继续自动轮播
    ionic4创建新项目
    两个年月日相减,获取年数和年数及半年数
    微信小程序点击跳转出现背景
    列表数据进行左浮动造成页面空白一块,排版错位问题
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6543816.html
Copyright © 2020-2023  润新知