这几天弄完了一个"自定义公式计算"的方案,觉得比较有意思,在这里就共享下!
该方案是针对一些需要用户自己去定义"计算公式",然后通过这个公式得出计算的结果这种需求的,特别适用与一些产品的BOM(在制作BOM的时候,需要去设定BOM中每个物料的数量,当这个BOM在比较复杂的时候直接去设定物料的数量,这点就不能去满足,因为有些物料是和其中的一些有关系的,所以这些的数据也想通过公式来建立关联关系并且得到数量)。
例如,A产品由B和C组成,在这里面B和C有一定的关联,C的数量是B数量的2陪,当出现这种情况的时候,用户就想通过修改B的数量,然后联动修改C的数量,但是这些公式需要允许用户自己去定义。这种类试的问题就是可以通过这个来搞定的。
大概可以解决的问题,大家应该了解了吧!那么如何使用和基本原理?
让我们先从用户使用的角度开始吧:
1、用户定义了下面结构的产品BOM:
2、现在去需要关联的产品设定数量的公式,从而达到联动改变的效果
相关产品的公式:
产品C: return [产品B]+[产品D];
产品E: return [产品C]*2 + 10;........
这里关于公式有个需要说明的地方:公式中的参数必须要在"[]"里面,必须要有return,除了这两个,其他定义的规范就是和C#一样,因为在内部是把这些转换为代码来执行的。其实在这里,公式的定义对于用户来说,还是需要做些简单培训的,因为要让用户去熟悉C#中简单的算术计算和一些基本的规范。
3、设置产品BOM中每个物料的数量,OK!调用我们的程序去完成工作吧。
那么用户部分的工作就全部结束了。
下面我们进入程序部分:
先看下程序的流程图:
程序将产品BOM中公式计算相关的所有物料都当成一个参数(CParamter)对象,对于其中有公式的参数,程序动态的将这些公式进行解析并生成公式类(继承于IFormula接口),然后将不同的公式类对象赋予到参数的Formula属性中,参数的PValue(值)通过这个公式对象的计算方法来得到值,而计算方法就是根据用户定义的公式来解析的。这里的核心部分就是动态的去利用自定义公式生成每个公式类并和参数建立起关系。
下面是所有的类图:
动态生成公式和模板类的代码:
Code
/// <summary>
/// 构造代码
/// </summary>
/// <returns></returns>
private static CodeCompileUnit BuildCodeUnit(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(BuildFormulaNamespace(templateName, paramters, expendParse));
codeUnit.Namespaces.Add(BuildTemplateNamespace(templateName, paramters, expendParse));
return codeUnit;
}
/// <summary>
/// 构造公式的名称空间树
/// </summary>
/// <returns></returns>
private static CodeNamespace BuildFormulaNamespace(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
CodeNamespace nameSpace = new CodeNamespace(CommonHelper.FormulaNameSpace);
AddImportNamespaces(nameSpace);
string fieldStr = "_ParamList";
string propertyStr = "ParamList";
string paramStr = "paramList";
string methodStr = "Calculate";
foreach (CParamter par in paramters)
{
//参数中存在公式
if (par.FormulaStr != null && par.FormulaStr.Trim().Length > 0)
{
//定义类
CodeTypeDeclaration mClass = new CodeTypeDeclaration(CommonHelper.GetFormulaClassStr(templateName, par.Key, expendParse));
mClass.BaseTypes.Add(typeof(IFormulaCalculate));
//构造函数
CodeConstructor mConstruct = new CodeConstructor();
mConstruct.Attributes = MemberAttributes.Public;
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IList<CParamter>), paramStr));
string conCodeStr = string.Format(@"this.{0} = {1}", fieldStr, paramStr);
mConstruct.Statements.Add(new CodeSnippetExpression(conCodeStr));
//定义字段
CodeMemberField paramList = new CodeMemberField();
paramList.Name = fieldStr;
paramList.Type = new CodeTypeReference(typeof(IList<CParamter>));
paramList.Attributes = MemberAttributes.Private;
//定义属性
CodeMemberProperty paramListProt = new CodeMemberProperty();
paramListProt.Name = propertyStr;
paramListProt.Type = new CodeTypeReference(typeof(IList<CParamter>));
paramListProt.Attributes = MemberAttributes.Public;
paramListProt.GetStatements.Add(new CodeSnippetExpression(string.Format("return this.{0}", fieldStr)));
//计算方法
CodeMemberMethod calMethod = new CodeMemberMethod();
calMethod.Name = methodStr;
calMethod.Attributes = MemberAttributes.Public;
calMethod.ReturnType = new CodeTypeReference(typeof(Object));
//解析公式
string strCode = expendParse.GetCompilerCode(par.FormulaStr, par.RefParams);
CodeSnippetExpression codeExpress = new CodeSnippetExpression(strCode);
calMethod.Statements.Add(codeExpress);
mClass.Members.Add(paramListProt);
mClass.Members.Add(mConstruct);
mClass.Members.Add(paramList);
mClass.Members.Add(calMethod);
nameSpace.Types.Add(mClass);
}
}
return nameSpace;
}
/// <summary>
/// 构造模板类代码的DOM
/// </summary>
/// <param name="templateName">模板名称</param>
/// <param name="paramters">模板中的所有参数</param>
/// <returns></returns>
private static CodeNamespace BuildTemplateNamespace(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
//添加名称空间
CodeNamespace nameSpace = new CodeNamespace(CommonHelper.TemplateNameSpace);
AddImportNamespaces(nameSpace);
//定义类
CodeTypeDeclaration mClass = new CodeTypeDeclaration(CommonHelper.GetTemplateClassStr(templateName));
mClass.BaseTypes.Add(typeof(BaseCTemplate));
//构造函数
CodeConstructor mConstruct = new CodeConstructor();
mConstruct.Attributes = MemberAttributes.Public;
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(String), "name"));
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IList<CParamter>), "paramters"));
mConstruct.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("name"));
//构造函数内的代码
StringBuilder strBuild = new StringBuilder();
int i = 0;
foreach (CParamter par in paramters)
{
string paramStr = CommonHelper.GetParamterStr(par.Key, expendParse);
string code =
string.Format(
@"DynamicCalculate.Implement.IFormulaCalculate formula{0} = new {2}(paramters[{1}].RefParams);
paramters[{1}].Formula = formula{0};
this._Paramters.Add(paramters[{1}]);",
paramStr, i, CommonHelper.GetFormulaClassStr(templateName, par.Key, expendParse));
i++;
strBuild.Append(code);
}
CodeSnippetExpression mExpress = new CodeSnippetExpression(strBuild.ToString());
mConstruct.Statements.Add(mExpress);
mClass.Members.Add(mConstruct);
//添加类
nameSpace.Types.Add(mClass);
return nameSpace;
}
/// <summary>
/// 引入名称空间
/// </summary>
/// <param name="codeNamespace"></param>
private static void AddImportNamespaces(CodeNamespace codeNamespace)
{
//引用的名称空间
codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections"));
codeNamespace.Imports.Add(new CodeNamespaceImport("DynamicCalculate.Implement"));
codeNamespace.Imports.Add(new CodeNamespaceImport("DynamicCalculate.Service"));
codeNamespace.Imports.Add(new CodeNamespaceImport(CommonHelper.FormulaNameSpace));
codeNamespace.Imports.Add(new CodeNamespaceImport(CommonHelper.TemplateNameSpace));
}
/// <summary>
/// 构造代码
/// </summary>
/// <returns></returns>
private static CodeCompileUnit BuildCodeUnit(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(BuildFormulaNamespace(templateName, paramters, expendParse));
codeUnit.Namespaces.Add(BuildTemplateNamespace(templateName, paramters, expendParse));
return codeUnit;
}
/// <summary>
/// 构造公式的名称空间树
/// </summary>
/// <returns></returns>
private static CodeNamespace BuildFormulaNamespace(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
CodeNamespace nameSpace = new CodeNamespace(CommonHelper.FormulaNameSpace);
AddImportNamespaces(nameSpace);
string fieldStr = "_ParamList";
string propertyStr = "ParamList";
string paramStr = "paramList";
string methodStr = "Calculate";
foreach (CParamter par in paramters)
{
//参数中存在公式
if (par.FormulaStr != null && par.FormulaStr.Trim().Length > 0)
{
//定义类
CodeTypeDeclaration mClass = new CodeTypeDeclaration(CommonHelper.GetFormulaClassStr(templateName, par.Key, expendParse));
mClass.BaseTypes.Add(typeof(IFormulaCalculate));
//构造函数
CodeConstructor mConstruct = new CodeConstructor();
mConstruct.Attributes = MemberAttributes.Public;
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IList<CParamter>), paramStr));
string conCodeStr = string.Format(@"this.{0} = {1}", fieldStr, paramStr);
mConstruct.Statements.Add(new CodeSnippetExpression(conCodeStr));
//定义字段
CodeMemberField paramList = new CodeMemberField();
paramList.Name = fieldStr;
paramList.Type = new CodeTypeReference(typeof(IList<CParamter>));
paramList.Attributes = MemberAttributes.Private;
//定义属性
CodeMemberProperty paramListProt = new CodeMemberProperty();
paramListProt.Name = propertyStr;
paramListProt.Type = new CodeTypeReference(typeof(IList<CParamter>));
paramListProt.Attributes = MemberAttributes.Public;
paramListProt.GetStatements.Add(new CodeSnippetExpression(string.Format("return this.{0}", fieldStr)));
//计算方法
CodeMemberMethod calMethod = new CodeMemberMethod();
calMethod.Name = methodStr;
calMethod.Attributes = MemberAttributes.Public;
calMethod.ReturnType = new CodeTypeReference(typeof(Object));
//解析公式
string strCode = expendParse.GetCompilerCode(par.FormulaStr, par.RefParams);
CodeSnippetExpression codeExpress = new CodeSnippetExpression(strCode);
calMethod.Statements.Add(codeExpress);
mClass.Members.Add(paramListProt);
mClass.Members.Add(mConstruct);
mClass.Members.Add(paramList);
mClass.Members.Add(calMethod);
nameSpace.Types.Add(mClass);
}
}
return nameSpace;
}
/// <summary>
/// 构造模板类代码的DOM
/// </summary>
/// <param name="templateName">模板名称</param>
/// <param name="paramters">模板中的所有参数</param>
/// <returns></returns>
private static CodeNamespace BuildTemplateNamespace(string templateName, IList<CParamter> paramters, IFormulaCodeParse expendParse)
{
//添加名称空间
CodeNamespace nameSpace = new CodeNamespace(CommonHelper.TemplateNameSpace);
AddImportNamespaces(nameSpace);
//定义类
CodeTypeDeclaration mClass = new CodeTypeDeclaration(CommonHelper.GetTemplateClassStr(templateName));
mClass.BaseTypes.Add(typeof(BaseCTemplate));
//构造函数
CodeConstructor mConstruct = new CodeConstructor();
mConstruct.Attributes = MemberAttributes.Public;
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(String), "name"));
mConstruct.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IList<CParamter>), "paramters"));
mConstruct.BaseConstructorArgs.Add(new CodeVariableReferenceExpression("name"));
//构造函数内的代码
StringBuilder strBuild = new StringBuilder();
int i = 0;
foreach (CParamter par in paramters)
{
string paramStr = CommonHelper.GetParamterStr(par.Key, expendParse);
string code =
string.Format(
@"DynamicCalculate.Implement.IFormulaCalculate formula{0} = new {2}(paramters[{1}].RefParams);
paramters[{1}].Formula = formula{0};
this._Paramters.Add(paramters[{1}]);",
paramStr, i, CommonHelper.GetFormulaClassStr(templateName, par.Key, expendParse));
i++;
strBuild.Append(code);
}
CodeSnippetExpression mExpress = new CodeSnippetExpression(strBuild.ToString());
mConstruct.Statements.Add(mExpress);
mClass.Members.Add(mConstruct);
//添加类
nameSpace.Types.Add(mClass);
return nameSpace;
}
/// <summary>
/// 引入名称空间
/// </summary>
/// <param name="codeNamespace"></param>
private static void AddImportNamespaces(CodeNamespace codeNamespace)
{
//引用的名称空间
codeNamespace.Imports.Add(new CodeNamespaceImport("System"));
codeNamespace.Imports.Add(new CodeNamespaceImport("System.Collections"));
codeNamespace.Imports.Add(new CodeNamespaceImport("DynamicCalculate.Implement"));
codeNamespace.Imports.Add(new CodeNamespaceImport("DynamicCalculate.Service"));
codeNamespace.Imports.Add(new CodeNamespaceImport(CommonHelper.FormulaNameSpace));
codeNamespace.Imports.Add(new CodeNamespaceImport(CommonHelper.TemplateNameSpace));
}
生成后的公式类和模板类
Code
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.832
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace DynamicCalculate.Formula {
using System;
using System.Collections;
using DynamicCalculate.Implement;
using DynamicCalculate.Service;
using DynamicCalculate.Formula;
using DynamicCalculate.Template;
public class C_testT_Formula__Key1 : DynamicCalculate.Implement.IFormulaCalculate {
private System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> _ParamList;
public C_testT_Formula__Key1(System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramList) {
this._ParamList = paramList;
}
public virtual System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> ParamList {
get {
return this._ParamList;
}
}
public virtual object Calculate() {
return Convert.ToInt64(_ParamList[0].PValue) + 10;
}
}
public class C_testT_Formula__Key2 : DynamicCalculate.Implement.IFormulaCalculate {
private System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> _ParamList;
public C_testT_Formula__Key2(System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramList) {
this._ParamList = paramList;
}
public virtual System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> ParamList {
get {
return this._ParamList;
}
}
public virtual object Calculate() {
return Convert.ToInt64(_ParamList[0].PValue) + Convert.ToInt64(_ParamList[1].PValue);
}
}
}
namespace DynamicCalculate.Template {
using System;
using System.Collections;
using DynamicCalculate.Implement;
using DynamicCalculate.Service;
using DynamicCalculate.Formula;
using DynamicCalculate.Template;
public class C_testT : DynamicCalculate.Implement.BaseCTemplate {
public C_testT(string name, System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramters) :
base(name) {
DynamicCalculate.Implement.IFormulaCalculate formulaC__Key1 = new C_testT_Formula__Key1(paramters[0].RefParams);
paramters[0].Formula = formulaC__Key1;
this._Paramters.Add(paramters[0]);DynamicCalculate.Implement.IFormulaCalculate formulaC__Key2 = new C_testT_Formula__Key2(paramters[1].RefParams);
paramters[1].Formula = formulaC__Key2;
this._Paramters.Add(paramters[1]);;
}
}
}
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.832
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace DynamicCalculate.Formula {
using System;
using System.Collections;
using DynamicCalculate.Implement;
using DynamicCalculate.Service;
using DynamicCalculate.Formula;
using DynamicCalculate.Template;
public class C_testT_Formula__Key1 : DynamicCalculate.Implement.IFormulaCalculate {
private System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> _ParamList;
public C_testT_Formula__Key1(System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramList) {
this._ParamList = paramList;
}
public virtual System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> ParamList {
get {
return this._ParamList;
}
}
public virtual object Calculate() {
return Convert.ToInt64(_ParamList[0].PValue) + 10;
}
}
public class C_testT_Formula__Key2 : DynamicCalculate.Implement.IFormulaCalculate {
private System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> _ParamList;
public C_testT_Formula__Key2(System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramList) {
this._ParamList = paramList;
}
public virtual System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> ParamList {
get {
return this._ParamList;
}
}
public virtual object Calculate() {
return Convert.ToInt64(_ParamList[0].PValue) + Convert.ToInt64(_ParamList[1].PValue);
}
}
}
namespace DynamicCalculate.Template {
using System;
using System.Collections;
using DynamicCalculate.Implement;
using DynamicCalculate.Service;
using DynamicCalculate.Formula;
using DynamicCalculate.Template;
public class C_testT : DynamicCalculate.Implement.BaseCTemplate {
public C_testT(string name, System.Collections.Generic.IList<DynamicCalculate.Service.CParamter> paramters) :
base(name) {
DynamicCalculate.Implement.IFormulaCalculate formulaC__Key1 = new C_testT_Formula__Key1(paramters[0].RefParams);
paramters[0].Formula = formulaC__Key1;
this._Paramters.Add(paramters[0]);DynamicCalculate.Implement.IFormulaCalculate formulaC__Key2 = new C_testT_Formula__Key2(paramters[1].RefParams);
paramters[1].Formula = formulaC__Key2;
this._Paramters.Add(paramters[1]);;
}
}
}
单元测试的代码:
Code
string templateName = "testT";
IList<CParamter> parList = new List<CParamter>();
CParamter cpar = new CParamter();
cpar.Key = "[Key1]";
cpar.PValue = 1;
cpar.PType = DataTypeEnum.Interger;
cpar.FormulaStr = "return [Key1] + 10";
parList.Add(cpar);
CParamter cpar1 = new CParamter();
cpar1.Key = "[Key2]";
cpar1.PValue = 1;
cpar1.PType = DataTypeEnum.Interger;
cpar1.FormulaStr = "return [Key2] + [Key1]";
parList.Add(cpar1);
ICTemplate tem = CTemplateFactory.BuildTemplate(templateName, parList);
tem.CalcaluteParamters(ref parList);
Assert.AreEqual(11, Convert.ToInt32(parList[0].PValue));
Assert.AreEqual(12, Convert.ToInt32(parList[1].PValue));
string templateName = "testT";
IList<CParamter> parList = new List<CParamter>();
CParamter cpar = new CParamter();
cpar.Key = "[Key1]";
cpar.PValue = 1;
cpar.PType = DataTypeEnum.Interger;
cpar.FormulaStr = "return [Key1] + 10";
parList.Add(cpar);
CParamter cpar1 = new CParamter();
cpar1.Key = "[Key2]";
cpar1.PValue = 1;
cpar1.PType = DataTypeEnum.Interger;
cpar1.FormulaStr = "return [Key2] + [Key1]";
parList.Add(cpar1);
ICTemplate tem = CTemplateFactory.BuildTemplate(templateName, parList);
tem.CalcaluteParamters(ref parList);
Assert.AreEqual(11, Convert.ToInt32(parList[0].PValue));
Assert.AreEqual(12, Convert.ToInt32(parList[1].PValue));
觉得这个方案对于需要进行自定义公式进行计算的用户,还是不错的!
源代码:DynamicCalculate.rar