Convert Application Model Differences | |
eXpressApp Framework > Concepts > Application Model > Convert Application Model Differences
The eXpressApp Framework is based on the modules concept. As a rule, every module implements a certain feature. Entities implemented in a module, such as persistent classes or extra Application Model nodes - can be customized by users of the application via the Model Editor. Such customizations are saved as Application Model differences in XafML files. Legacy Application Model differences might pose a problem when a module is updated to a new version, and its internal structure is changed. So, developers implementing modules should provide means to convert Application Model differences with new versions. The eXpressApp Framework provides easy ways to implement such converters. This topic describes them.
eXpressApp框架基于模块构成。作为一个规则,每个模块实现一个特定功能。在一个模块中实现实体,如持久类或者用户应用程序可以通过模型编辑器定制其他应用程序模型节点。这种自定义保存不同应用程序模型在XafML文件中。当你升级一个模块是,可能不同应用程序模型会有问题,它的内部结构发生改变。因此,要实现转换新版本应用程序模型的差异部分。eXpressApp框架通过了非常容易的方法实现转换。本主题详细描述。
Basically, Application Model differences are converted in two steps.
基本上,转换应用程序模型差异部分分两步。
- The XafML files stored in XML format can be processed by a module implementing the IModelXmlConverter interface. This step allows the application to start correctly, where otherwise, legacy Application Model differences would cause an exception at the application start. In simple conversion scenarios, this may be the only required step.
XafML文件以XML文件格式存储,能被由实现IModelXmlConverter接口的模块处理。这个步骤可使应用程序正常启动,另外应用程序模型的差异可能导致应用程序在启动时异常。这是实现简单转换必须要做的步骤。 - For complex conversion scenarios, special updaters implementing the IModelNodeUpdater<T> interface should be used. Such updaters can perform much more versatile conversions.
复杂的转换,指定更新者实现IModelNodeUpdater<T>接口。这样的更新者可以实现更灵活的转换。
Depending on a particular scenario, you may need to perform either both of these steps or just one. So, for example, in one scenario you may need to implement an XML converter, so that the application could start, and a node updater, to perform a complex conversion. In other scenarios, you may need to implement only an XML converter or a node updater.
根据不同情况,你可能需要实现这两个步骤中一个或多个。例如,在一种情况下你可能需要实现一个XML转换,启动应用程序,一个节点更新,实现一个复杂转换。在其他情况下,你可能只需实现一个XML转换或一个节点更新。
Implement an XML converter(实现一个XML转换)
An XML converter is represented by a module implementing the IModelXmlConverter interface. The interface declares the IModelXmlConverter.ConvertXml method, which is invoked for each node customized in Application Model differences. The method takes the ConvertXmlParameters object, supplying differences as a parameter. You can handle changes to a node in the method's body, by converting differences and making them correspond to the actual model. Consider the following example.
用一个模块实现IModelXmlConverter接口代表一个XML转换器。这个接口定义了IModelXmlConverter.ConvertXml方法,被用在应用程序模型差异部分的每个自定义节点。
该方法使用ConvertXmlParameters参数对象作为差异参数.你可以在方法内改变一个节点处理,通过转换差异使他们适应实际模型。参考下面例子。
Suppose your application uses a module which adds an OperatingMode string property to the Application Model's Options node. This property is designed to take either a "Simple" or "Complex" word as a value. Originally, the module extends the Application Model in the following way:
假设你用了一个应用程序给应用程序模型的选项节点添加一个字符串属性OperatingMode的模块,这个属性用“Simple”或“Complex”作为值。最初,这个模块同过以下方式扩展应用程序模型:
| ||||||
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>(); } } Imports DevExpress.ExpressApp.Model
Public Interface IModelMyOptions Property OperatingMode() As String End Interface Public NotInheritable Partial Class MyModule Inherits ModuleBase '... Public Overrides Sub ExtendModelInterfaces(ByVal extenders As ModelInterfaceExtenders) MyBase.ExtendModelInterfaces(extenders) extenders.Add(Of IModelOptions, IModelMyOptions)() End Sub End Class |
C# |
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>(); } } |
VB |
Imports DevExpress.ExpressApp.Model
Public Interface IModelMyOptions Property OperatingMode() As String End Interface Public NotInheritable Partial Class MyModule Inherits ModuleBase '... Public Overrides Sub ExtendModelInterfaces(ByVal extenders As ModelInterfaceExtenders) MyBase.ExtendModelInterfaces(extenders) extenders.Add(Of IModelOptions, IModelMyOptions)() End Sub End Class |
Users of your application customize this property value and it is stored to their Application Model differences. Then, the module is updated to a new version which introduces a couple of changes. First, the OperatingMode property type is changed to a newly introduced enumeration. Second, string representations of the new enumeration values are different from the previously used string values:
应用程序的用户自定义这个属性值并存储到他们的应用程序模型差异。接着,模块更新到新版本改动。首先,OperatingMode属性类型更改为新进的枚举。第二,新的枚举字符串值不同于之前用的字符串值:
| ||||||
public interface IModelMyOptions { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } Public Interface IModelMyOptions Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
C# |
public interface IModelMyOptions { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } |
VB |
Public Interface IModelMyOptions Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
If you now recompile the application with the updated module and redistribute it, existing users will not be able to use the application. At the application start, a message will be displayed stating that an error has occurred while loading the Application Model differences. To avoid this, an XML converter should be implemented in the module. The following code snippet illustrates a possible solution:
如果你用更新模块重新编辑应用程序并重新发布,已有的用户将不能用这个应用程序。在应用程序启动时,将显示一个信息,指出有错误发生,在加载应用程序模型差异。为了避免这个错误提示,应当在模块中实现一个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; } } } } } } Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Updating
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelXmlConverter '... Public Sub ConvertXml(ByVal parameters As ConvertXmlParameters) If GetType(IModelOptions).IsAssignableFrom(parameters.NodeType) Then Dim [property] As String = "OperatingMode" If parameters.ContainsKey([property]) Then Dim value As String = parameters.Values([property]) If (Not System.Enum.IsDefined(GetType(OperatingMode), value)) Then Select Case value.ToLower() Case "complex" parameters.Values([property]) = OperatingMode.Advanced.ToString() Case Else parameters.Values([property]) = OperatingMode.Basic.ToString() End Select End If End If End If End Sub End Class |
C# |
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; } } } } } } |
VB |
Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Updating
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelXmlConverter '... Public Sub ConvertXml(ByVal parameters As ConvertXmlParameters) If GetType(IModelOptions).IsAssignableFrom(parameters.NodeType) Then Dim [property] As String = "OperatingMode" If parameters.ContainsKey([property]) Then Dim value As String = parameters.Values([property]) If (Not System.Enum.IsDefined(GetType(OperatingMode), value)) Then Select Case value.ToLower() Case "complex" parameters.Values([property]) = OperatingMode.Advanced.ToString() Case Else parameters.Values([property]) = OperatingMode.Basic.ToString() End Select End If End If End If End Sub End Class |
The converter checks whether the currently processed node is the Options node. If it is, the converter checks whether the OperatingMode property has a legacy value. Then, the "Complex" value becomes OperatingMode.Advanced and all other values become OperatingMode.Basic.
这个转换器检查当前处理的节点是否是选项节点。如果是,他将检查OperatingMode是否有值。接着,"Complex"值变为OperatingMode.Advanced 并且其他值变为OperatingMode.Basic。
Now, consider another very common scenario when a node's property is renamed. Suppose, a Mode property declared in the Options node was renamed to OperatingMode. The following code snippet illustrates a possible XML converter:
现在,考虑另一种常用的情况,重命名一个节点的属性。假设,重命名一个定义在选项节点中模型属性。下面代码说明一个可能的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); } } } } Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Updating
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelXmlConverter '... Public Sub ConvertXml(ByVal parameters As ConvertXmlParameters) If GetType(IModelOptions).IsAssignableFrom(parameters.NodeType) Then Dim oldProperty As String = "Mode" Dim newProperty As String = "OperatingMode" If parameters.ContainsKey(oldProperty) Then parameters.Values(newProperty) = parameters.Values(oldProperty) parameters.Values.Remove(oldProperty) End If End If End Sub End Class Private < Mod Cod |
C# |
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); } } } } |
VB |
Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Updating
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelXmlConverter '... Public Sub ConvertXml(ByVal parameters As ConvertXmlParameters) If GetType(IModelOptions).IsAssignableFrom(parameters.NodeType) Then Dim oldProperty As String = "Mode" Dim newProperty As String = "OperatingMode" If parameters.ContainsKey(oldProperty) Then parameters.Values(newProperty) = parameters.Values(oldProperty) parameters.Values.Remove(oldProperty) End If End If End Sub End Class Private < Mod Cod |
As illustrated by the examples, an XML converter works with one node at a time. So, complex conversions, affecting several nodes at once, are not possible. In these cases, special updaters implementing the IModelNodeUpdater<T> interface should be used.
用例子说明,一个XML转换器一次由一个节点工作。接着,复杂的转换,不可能一次影响几个节点。在这种情况下,应当用指定的更新器实现IModelNodeUpdater<T>接口。
Implement a Node Updater(属性一个节点更新器)
A node updater is represented by a class implementing the IModelNodeUpdater<T> interface. The generic type parameter specifies the type of the nodes for which the updater is intended. The interface declares a single IModelNodeUpdater<T>.UpdateNode method, which takes two parameters. The first parameter is the Application Model node in process, represented by an object implementing the IModelNode interface. The second parameter is the application's Application Model, represented by an object, implementing the IModelApplication interface. Since you have access to the whole Application Model, complex conversions affecting multiple nodes can be performed. Consider the following example.
一个节点更新器表示一个类实现IModelNodeUpdater<T>接口。更新者的目的是泛型参数类型指定节点的类型。该接口定义一个IModelNodeUpdater<T>.UpdateNode方法,有两个参数。第一个参数是应用程序模型节点在处理过程中。表示一个对象实现了IModelNode接口。第二个参数是应用程序的应用程序模型,由一个对象实现IModelApplication接口。既然你已经访问整个应用程序模型,执行影响多个节点的复杂转换。看下面例子。
Suppose your application uses a module which adds an OperatingMode string property to the Application Model's Options node. Originally, the interface extending the Application Model looks like this:
假设你用了一个应用程序给应用程序模型的选项节点添加一个字符串属性OperatingMode的模块,最初,接口扩展应用程序模型如下:
| ||||||
public interface IModelMyOptions { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } Public Interface IModelMyOptions Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
C# |
public interface IModelMyOptions { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } |
VB |
Public Interface IModelMyOptions Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
Then, the module is updated to a new version which moves the property to a newly introduced child node:
然后,模组更新到一个新的版本,移动到新推出的子节点的属性:
| ||||||
using DevExpress.ExpressApp.Model;
public interface IModelMyOptions { IModelChildOptions ChildOptions { get; } } public interface IModelChildOptions : IModelNode { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } Imports DevExpress.ExpressApp.Model
Public Interface IModelMyOptions ReadOnly Property ChildOptions() As IModelChildOptions End Interface Public Interface IModelChildOptions Inherits IModelNode Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
C# |
using DevExpress.ExpressApp.Model;
public interface IModelMyOptions { IModelChildOptions ChildOptions { get; } } public interface IModelChildOptions : IModelNode { OperatingMode OperatingMode { get; set; } } public enum OperatingMode { Basic, Advanced } |
VB |
Imports DevExpress.ExpressApp.Model
Public Interface IModelMyOptions ReadOnly Property ChildOptions() As IModelChildOptions End Interface Public Interface IModelChildOptions Inherits IModelNode Property OperatingMode() As OperatingMode End Interface Public Enum OperatingMode Basic Advanced End Enum |
If you now recompile the application with the updated module and redistribute it, the original OperatingMode property values specified by users of the application will be lost. To avoid this, a node updater should be implemented. A possible place for the implementation is the updated module:
如果你用更新模块重新编辑应用程序并重新发布,已有的用户将不能用这个应用程序。原来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); } } } Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Model.Core
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelNodeUpdater(Of IModelOptions) '... Public Sub UpdateNode(ByVal node As IModelOptions, ByVal application As IModelApplication) Dim myProperty As String = "OperatingMode" If node.HasValue(myProperty) Then Dim value As String = node.GetValue(Of String)(myProperty) node.ClearValue(myProperty) CType(node, IModelMyOptions).ChildOptions.OperatingMode = _ CType(System.Enum.Parse(GetType(OperatingMode), value), OperatingMode) End If End Sub End Class |
C# |
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); } } } |
VB |
Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Model.Core
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelNodeUpdater(Of IModelOptions) '... Public Sub UpdateNode(ByVal node As IModelOptions, ByVal application As IModelApplication) Dim myProperty As String = "OperatingMode" If node.HasValue(myProperty) Then Dim value As String = node.GetValue(Of String)(myProperty) node.ClearValue(myProperty) CType(node, IModelMyOptions).ChildOptions.OperatingMode = _ CType(System.Enum.Parse(GetType(OperatingMode), value), OperatingMode) End If End Sub End Class |
The updater in the example checks whether the currently processed node has a legacy OperatingMode property value. If there is such a value, it is assigned to the new OperatingMode property, and the legacy property value is cleared.
在实例中检查是否当前处理节点有一个OperatingMode属性值。如果有值,它被标识为新的OperatingMode属性并清除之前的值。
Note that since a node updater can be implemented anywhere in the application, the updater should be registered via the ModuleBase.AddModelNodeUpdaters method:
注意,一个节点的更新可以在任何应用程序实现,这个更新者应该通过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); } }Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Model.Core
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelNodeUpdater(Of IModelOptions) '... Public Overrides Sub AddModelNodeUpdaters( _ ByVal updaterRegistrator As IModelNodeUpdaterRegistrator) MyBase.AddModelNodeUpdaters(updaterRegistrator) updaterRegistrator.AddUpdater(Of IModelOptions)(Me) End Sub End Class |
C# |
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); } } |
VB |
Imports DevExpress.ExpressApp.Model Imports DevExpress.ExpressApp.Model.Core
Public NotInheritable Partial Class MyModule Inherits ModuleBase Implements IModelNodeUpdater(Of IModelOptions) '... Public Overrides Sub AddModelNodeUpdaters( _ ByVal updaterRegistrator As IModelNodeUpdaterRegistrator) MyBase.AddModelNodeUpdaters(updaterRegistrator) updaterRegistrator.AddUpdater(Of IModelOptions)(Me) End Sub End Class |
欢迎转载,转载请注明出处:http://www.cnblogs.com/Tonyyang/