程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 動態進行“自定義公式”計算

動態進行“自定義公式”計算

編輯:關於.NET

這幾天弄完了一個"自定義公式計算"的方案,覺得比較有意思,在這裡就共享下!

該方案是針對一些需要用戶自己去定義"計算公式",然後通過這個公式得出計算的結果這種需求的,特別適用與一些產品的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(值)通過這個公式對象的計算方法來得到值,而計算方法就是根據用戶定義的公式來解析的。這裡的核心部分就是動態的去利用自定義公式生成每個公式類並和參數建立起關系。

下面是所有的類圖:

動態生成公式和模板類的代碼:

/// <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));
}

生成後的公式類和模板類

//------------------------------------------------------------------------------
// <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]);;
}
}
}

單元測試的代碼:

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));

覺得這個方案對於需要進行自定義公式進行計算的用戶,還是不錯的!

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved