EPR类企业管理系统

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


定制
QQ:460-3528

开发
QQ群:3360-90194

源码
微信:136-3650-3721

转换应用程序模型差异

eXpressApp框架是基于模块的概念。通常,每个模块都实现特定功能。应用程序的用户可以通过模型编辑器来自定义模块中实现的实体,例如持久性类或额外的应用程序模型节点。此类自定义将作为应用程序模型差异保存在XAFML文件中。当模块更新到新版本并更改其内部结构时,旧版应用程序模型的差异可能会带来问题。因此,实现模块的开发人员应提供将应用程序模型差异转换为新版本的方法。该eXpressApp框架提供简单的方法来实现这样的转换器。本主题描述它们。

基本上,应用程序模型的差异分为两个步骤。

  1. 可以通过实现IModelXmlConverter接口的模块来处理以XML格式存储的XAFML文件。此步骤允许应用程序正确启动,否则,旧的应用程序模型差异将在应用程序启动时导致异常。在简单的转换方案中,这可能是唯一需要的步骤。
  2. 对于复杂的转换方案,应使用实现IModelNodeUpdater <T>接口的特殊更新程序。这样的更新程序可以执行更多用途的转换。

根据特定的情况,您可能需要执行这两个步骤中的一个或仅执行一个。因此,例如,在一种情况下,您可能需要实现XML转换器,以便应用程序可以启动,并需要节点更新程序来执行复杂的转换。在其他情况下,您可能仅需要实现XML转换器或节点更新程序。

实现一个XML转换器

XML转换器由实现IModelXmlConverter接口的模块表示。该接口声明IModelXmlConverter.ConvertXml方法,该方法将为“应用程序模型”差异中自定义的每个节点调用。该方法采用ConvertXmlParameters对象,提供差异作为参数。您可以通过转换差异并使它们与实际模型相对应来处理方法主体中节点的更改。考虑以下示例。

假设您的应用程序使用一个模块,该模块将OperatingMode字符串属性添加到应用程序模型的“选项”节点。此属性旨在将“简单”或“复杂”字作为值。最初,该模块通过以下方式扩展了应用程序模型

using DevExpress.ExpressApp.Model;

public interface IModelMyOptions{
    string OperatingMode { get; set; }
}
public sealed partial class MyModule : ModuleBase {
    //...
    public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
        base.ExtendModelInterfaces(extenders);
        extenders.Add<IModelOptions, IModelMyOptions>();
    }
}

应用程序的用户自定义此属性值,并将其存储到他们的应用程序模型差异中。然后,该模块将更新为新版本,其中引入了一些更改。首先,将OperatingMode属性类型更改为新引入的枚举。其次,新枚举值的字符串表示形式与以前使用的字符串值不同:

public interface IModelMyOptions {
    OperatingMode OperatingMode { get; set; }
}
public enum OperatingMode { Basic, Advanced }

如果现在使用更新的模块重新编译应用程序并重新分发,则现有用户将无法使用该应用程序。在应用程序启动时,将显示一条消息,指出在加载应用程序模型差异时发生了错误。为了避免这种情况,应该在模块中实现XML转换器。以下代码段说明了一种可能的解决方案:

using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Updating;

public sealed partial class MyModule : ModuleBase, IModelXmlConverter {
    //...
    public void ConvertXml(ConvertXmlParameters parameters) {
        if(typeof(IModelOptions).IsAssignableFrom(parameters.NodeType)) {
            string property = "OperatingMode";
            if(parameters.ContainsKey(property)) {
                string value = parameters.Values[property];
                if(!Enum.IsDefined(typeof(OperatingMode), value)) {
                    switch(value.ToLower()) {
                        case "complex":
                            parameters.Values[property] = OperatingMode.Advanced.ToString();
                            break;
                        default:
                            parameters.Values[property] = OperatingMode.Basic.ToString();
                            break;                            
                    }                                  
                }                    
            }
        }
    }
}

转换器检查当前处理的节点是否为“选项”节点。如果是,转换器将检查OperatingMode属性是否具有旧值。然后,“ Complex”值变为OperatingMode.Advanced,所有其他值变为OperatingMode.Basic

现在,考虑将节点的属性重命名时的另一种非常常见的情况。假设在“选项”节点中声明的Mode属性已重命名为OperatingMode。以下代码段说明了可能的XML转换器:

using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Updating;

public sealed partial class MyModule : ModuleBase, IModelXmlConverter {
    //...
    public void ConvertXml(ConvertXmlParameters parameters) {
        if(typeof(IModelOptions).IsAssignableFrom(parameters.NodeType)) {
            string oldProperty = "Mode";
            string newProperty = "OperatingMode";
            if(parameters.ContainsKey(oldProperty)) {
                parameters.Values[newProperty] = parameters.Values[oldProperty];
                parameters.Values.Remove(oldProperty);
            }
        }
    }
}

如示例所示,XML转换器一次只能使用一个节点。因此,不可能同时影响多个节点的复杂转换。在这些情况下,应使用实现IModelNodeUpdater <T>接口的特殊更新程序。

实施节点更新程序

节点更新程序由实现IModelNodeUpdater <T>接口的类表示。通用类型参数指定更新程序所针对的节点的类型。该接口声明一个IModelNodeUpdater`1.UpdateNode方法,该方法带有两个参数。第一个参数是进行中的“应用程序模型”节点,由实现IModelNode接口的对象表示。第二个参数是应用程序的应用程序模型,由一个对象表示,实现IModelApplication接口。由于您可以访问整个应用程序模型,因此可以执行影响多个节点的复杂转换。考虑以下示例。

假设您的应用程序使用一个模块,该模块将OperatingMode字符串属性添加到应用程序模型的“选项”节点。最初,扩展应用程序模型的接口如下所示:

public interface IModelMyOptions {
    OperatingMode OperatingMode { get; set; }
}
public enum OperatingMode { Basic, Advanced }

然后,模块被更新到新版本,该版本将属性移动到新引入的子节点:

using DevExpress.ExpressApp.Model;

public interface IModelMyOptions {
    IModelChildOptions ChildOptions { get; }
}
public interface IModelChildOptions : IModelNode {
    OperatingMode OperatingMode { get; set; }
}
public enum OperatingMode { Basic, Advanced }

如果现在使用更新的模块重新编译应用程序并重新分发,则应用程序用户指定的原始OperatingMode属性值将丢失。为避免这种情况,应实施节点更新程序。实现的可能地方是更新的模块:

using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Model.Core;

public sealed partial class MyModule : ModuleBase, IModelNodeUpdater<IModelOptions> {
    //...
    public void UpdateNode(IModelOptions node, IModelApplication application) {
        string myProperty = "OperatingMode";
        if(node.HasValue(myProperty)) {
            string value = node.GetValue<string>(myProperty);
            node.ClearValue(myProperty);
            ((IModelMyOptions)node).ChildOptions.OperatingMode = 
                (OperatingMode)Enum.Parse(typeof(OperatingMode), value);
        }
    }        
}

示例中的更新程序检查当前处理的节点是否具有旧的OperatingMode属性值。如果存在这样的值,则将其分配给新的OperatingMode属性,并清除旧的属性值。

请注意,由于可以在应用程序中的任何位置实现节点更新程序,因此应通过ModuleBase.AddModelNodeUpdaters方法注册该更新程序:

using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Model.Core;

public sealed partial class MyModule : ModuleBase, IModelNodeUpdater<IModelOptions> {
    //...
    public override void AddModelNodeUpdaters(IModelNodeUpdaterRegistrator updaterRegistrator) {
        base.AddModelNodeUpdaters(updaterRegistrator);
        updaterRegistrator.AddUpdater<IModelOptions>(this);
    }    
}
相关文章

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