EPR类企业管理系统

在我们现有系统基础上或全新开发,提供定制服务
为您的企业高效快速实施ERP,WMS,MES,CRM管理系统
全面管控物料仓库、销售业务、采购业务、仓库业务
生产过程、质量检验、组织架构、业务报表


定制
QQ:460-3528

开发
QQ群:3360-90194

源码
微信:136-3650-3721

使用元数据动态自定义业务类

本主题描述如何在不修改其代码的情况下更改现有的XPO或实体框架(EF)业务模型。在模型编辑器中,最终用户(应用程序管理员)可以将自定义字段添加到业务对象,而无需重新编译其应用程序。XAF应用程序开发人员还可以将自定义字段添加到在外部库中实现的对象。可以计算自定义字段(应提供用于计算字段值的表达式),也可以是自定义字段(将新列自动添加到数据库表中)。

在应用程序模型中声明的自定义字段将自动收集并在Types Info子系统中注册。请注意以下运行时自定义字段用法的特殊性。

  • 通常,仅应在设计时添加持久字段。允许最终用户更改数据库架构不是一个好习惯-仅作为最后的措施。
  • 强烈建议在运行时删除自定义字段后重新启动应用程序。
  • 当使用ThreadSafeDataLayer数据访问层时,不支持添加到用户差异(Model.User.xafml文件)的自定义字段。在XAF中,当您使用采用threadSafe参数并将此参数设置为true的构造函数实例化XPObjectSpaceProvider时,将启用此数据层。未指定threadSafe参数时,默认情况下,在安全系统的集成模式下使用的SecuredObjectSpaceProvider使用ThreadSafeDataLayer。解决方法是,将自定义字段复制到管理员的差异(Model.xafml )使用模型合并功能。
  • 计算字段总是在客户端评估。因此,排序,分组,过滤和汇总计算不适用于Server和InstantFeedback模式下的计算列。如果使用XPO并通过本主题中描述的方法之一创建新的计算字段,则PersistentAliasAttribute将自动添加到该新创建的字段中,因此,这些操作可用于相应的列。
  • 您不能将自定义持久字段添加到EF数据模型。
  • 您不能将非计算字段添加到非持久类

请注意,如果您更改移动应用程序模型,则需要重新构建应用程序。

本主题包括以下部分:

注意

要查看实际使用的自定义字段,请参阅XAF随附的Feature Center演示中的“自定义字段”部分。默认情况下,Feature Center演示安装在%PUBLIC%\ Documents \ DevExpress演示19.2 \ Components \ eXpressApp Framework \ FeatureCenter中。该演示的ASP.NET版本可从http://demos.devexpress.com/XAF/FeatureCenter/在线获得。

在模型编辑器中添加自定义计算字段

若要添加自定义计算字段,请在设计时或在运行时在WinForms应用程序中调用模型编辑器,或使用独立的模型编辑器工具(例如,将字段添加到ASP.NET应用程序)。然后按照以下步骤操作。

  1. 右键单击BOModel | <类别> | OwnMembers节点,然后选择添加... | 成员

    AddCustomField_ME

  2. 重点关注新的成员节点。在右侧的属性网格中,指定IModelMember.NameIModelMember.Type值。
  3. 将用于计算字段值的表达式传递给IModelMember.Expression属性。您可以单击属性值右侧的省略号按钮以调用“表达式编辑器”对话框。在此编辑器中,可以使用编辑器控件选择函数,运算符和操作数。
  4. 如果要在运行时创建自定义字段,请指定IModelCommonMemberViewItem.PropertyEditorType值。从组合框中选择一个合适的属性编辑器。在设计时,PropertyEditorType值是可选的-会自动确定适当的Property Editor。请注意,特定于平台的属性编辑器在通用模块中不可用。
  5. 保存所做的更改,然后重新启动“模型编辑器”。
  6. 创建映射到新添加的自定义字段的“详细信息视图”项或“列表视图”列(请参阅将编辑器添加到“详细信息视图”“在列表视图中更改字段布局和可见性”)。

下图说明了上面描述的成员节点设置。

AddCustomField_ME2

在Visual Studio的模型编辑器中添加自定义持久字段

创建自定义持久字段与创建上一部分中描述的计算字段非常相似。唯一的区别是您应该将IModelMember.IsCalculated属性设置为false,而不是指定Expression值。

注意

一起使用时,不支持以下功能组合。

在这种配置中,您的应用程序从数据库加载有关自定义持久性字段的信息,然后更新数据库架构。但是,建立数据库连接后,线程安全数据层不支持更改数据模型。

在运行时在模型编辑器中添加自定义持久字段

默认情况下,仅在设计时才允许创建自定义持久字段(IsCalculated值不可编辑)。要允许最终用户在运行时在模型编辑器中添加自定义持久性字段,请将静态ModelMemberReadOnlyCalculator.AllowPersistentCustomProperties属性设置为true。然后,应将创建的字段合并到应用程序级别的模型差异中(请参阅Model Merge Tool)。要在运行时添加字段后允许更新数据库架构,请将XafApplication.DatabaseUpdateMode属性设置为DatabaseUpdateMode.UpdateDatabaseAlways。另外,还要考虑有关的说明ThreadSafeDataLayer上一节。映射到当前字段的列将自动添加到数据库表中。

重点
  • 如果将应用程序模型差异存储在数据库中,则还应该重写XafApplication.OnSetupComplete 受保护的虚拟方法,并从重写的方法中调用XafApplication.CheckCompatibilityCore方法。
  • 中间层安全性-WCF服务,Workflow Server服务或创建和配置ServerApplication / XafApplication的任何其他应用程序无法访问在客户端XAF应用程序项目中添加的自定义字段(在代码中或在XAFML中),或者无法通过在运行时自定义应用程序模型。您应该在一个通用模块中添加一个自定义字段,并在所有要使用它的应用程序中对其进行引用。

在代码中添加自定义字段

要从外部类库扩展现有业务类,可以在代码中添加一个自定义字段。编辑Module.csModule.vb)文件,并重写ModuleBase.CustomizeTypesInfo方法。下面的代码演示了如何添加持久字段(PersistentField1),与一个持久字段ImmediatePostDataAttribute施加(PersistentField2)和计算出的字段(CalculatedField)。

using DevExpress.ExpressApp.DC;
using DevExpress.Persistent.Base;
using DevExpress.Persistent.BaseImpl;
// ...
public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
    base.CustomizeTypesInfo(typesInfo);
    TypeInfo personTypeInfo = typesInfo.FindTypeInfo(typeof(Person)) as TypeInfo;
    if (personTypeInfo != null) {
        personTypeInfo.CreateMember("PersistentField1", typeof(int));
        personTypeInfo.CreateMember("PersistentField2", typeof(int)).AddAttribute(new ImmediatePostDataAttribute());
        personTypeInfo.CreateMember("CalculatedField", typeof(int), "PersistentField1   PersistentField2");
    }
}

访问代码中的自定义字段

以下代码段说明了如何使用IMemberInfo.GetValue方法访问Controller上下文中的自定义字段。

Person person = this.View.CurrentObject as Person;
ITypeInfo personInfo = this.Application.TypesInfo.FindTypeInfo(typeof(Person));
int? value = personInfo.FindMember("CalculatedField").GetValue(person) as int?;

以下代码段说明了如何从PropertyEditor.ControlValue属性访问Controller上下文中的自定义字段。

ViewItem viewItem = ((DetailView)View).FindItem("CalculatedField");
decimal? value = ((PropertyEditor)viewItem).ControlValue as decimal?;

在代码中创建关联

以下代码段说明了在无法访问这些类代码且无法直接应用AssociationAttribute的情况下如何声明两个类的属性之间的关联。

public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
    base.CustomizeTypesInfo(typesInfo);
    ITypeInfo typeInfo1 = typesInfo.FindTypeInfo(typeof(DomainObject1));
    ITypeInfo typeInfo2 = typesInfo.FindTypeInfo(typeof(DomainObject2));
    IMemberInfo memberInfo1 = typeInfo1.FindMember("Object2");
    IMemberInfo memberInfo2 = typeInfo2.FindMember("Object1s");
    if(memberInfo1 == null) {
        memberInfo1 = typeInfo1.CreateMember("Object2", typeof(DomainObject2));
        memberInfo1.AddAttribute(new DevExpress.Xpo.AssociationAttribute("A", typeof(DomainObject2)), true);
    }
    if(memberInfo2 == null) {
        memberInfo2 = typeInfo2.CreateMember("Object1s", typeof(XPCollection<DomainObject1>));
        memberInfo2.AddAttribute(new DevExpress.Xpo.AssociationAttribute("A", typeof(DomainObject1)), true);
        memberInfo2.AddAttribute(new DevExpress.Xpo.AggregatedAttribute(), true);
    }
    ((XafMemberInfo)memberInfo1).Refresh();
    ((XafMemberInfo)memberInfo2).Refresh();
}

如您在上面的代码中看到的那样,IMemberInfo.AddAttribute方法的第二个参数为true。在这种情况下,类型信息不会立即从XPDictionary重新加载信息。添加所有必需的属性后,为每个更新的成员调用XafMemberInfo.Refresh方法。另外,您可以为每个包含更新成员的类型调用XafTypesInfo.RefreshInfo

public override void CustomizeTypesInfo(ITypesInfo typesInfo) {
    // ...
    typesInfo.RefreshInfo(typeof(DomainObject1));
    typesInfo.RefreshInfo(typeof(DomainObject2));
}
提示

若要直接访问XPDictionary,请使用XpoTypesInfoHelper.GetXpoTypeInfoSource方法。

相关文章

转载保留此链接,注明出处