目标

采用了Restful WebApi的架构,再把业务逻辑状态转移放到后端就有点违背初衷了。实际上只要后端Api的权限设置的好,把状态转移放到前端也未尝不可。我考虑的结果是,一般如果变更这个状态本身就需要特定权限才可操作,比如xxx审批者,在前端处理状态逻辑问题不大,因为本身这个人就是有权限的,如果伪造请求破坏了数据也可以通过Log定位到个人,但是如果是面向大众的情况,比如团购下个订单什么的不对数据有特定权限要求的就需要在后端单独做动作类的WebApi了,欢迎讨论。

相关传送门:Restful WebApi开发实践

先来看下最后的请求效果:(插件服务总线用的是服务定位器设计模式)

var result = BundleServiceBus.Current.GetInstance<IEventHandleService>()
.EventTrigger<ExamEvent>(new ExamEvent
{
RootDataId = Id,
Type = ET,
});
if (result.IsSuccess)
{
Dispatcher.Invoke(() => MessageBox.Show("提交成功"));
UpdateStudentInfo(ThisStudentInfo.Id);
return;
}

实现

这里我们将用到 C#对WebApi数据操作 里的方案来解决数据操作环节。

首先定义状态基类:

abstract class StatusBase<RootDataType>
{
public ReturnHTTPData ReturnHttpData { get; set; }
public abstract string Name { get; }
public virtual bool Entry(BaseEvent arg) { return true; }
public virtual bool Exit() { return true; }
public abstract List<BaseTransferModel> TransferList { get; } public IModelDataService MDS = BundleServiceBus.Current.GetInstance<IModelDataService>(); public RootDataType RootData { get; set; }
}

这里需要根据具体数据类型自定义一个业务状态基类,比如:

abstract class XXXStatusBase : StatusBase<StudentInfoModel>
{
public LoginUserInfoModel CurrentLoginUser
{
get
{
return MDS.GetDataByModel<LoginUserInfoModel, string>(DataParameter.Empty).Data.SingleOrDefault();
}
} public bool ChangeRootDataStatus(string TargetStatus)
{
RootData.Status = TargetStatus;
return ChangeRootDataInfo(RootData);
} public bool ChangeRootDataInfo(StudentInfoModel model)
{
var result = MDS.PutDataByModel(new DataParameter<Guid>
{
TenantId = CurrentLoginUser.GetDataParameter().TenantId,
AggregationId = CurrentLoginUser.GetDataParameter().AggregationId,
SiteId = CurrentLoginUser.GetDataParameter().SiteId,
DataId = model.Id,
CMD = CurrentLoginUser.GetDataParameter().CMD
}, model);
//ReturnHttpData = new ReturnHTTPData { Content = result.Message };
return result.IsSuccess;
} public bool AddBusinessLog(BusinessLogModel model)
{
//TODO 提交队列
var result = MDS.PostDataByModel<BusinessLogModel, Guid>(CurrentLoginUser.GetDataParameter(), model);
//ReturnHttpData = new ReturnHTTPData { Content = result.Message };
return result.IsSuccess;
}
}

接下来定义一下状态转移模型基类:

abstract class BaseTransferModel
{
public string Target { get; set; }
public abstract bool Condition(BaseEvent arg);
public abstract bool Action(string target, BaseEvent arg);
}

实现一个基于事件的状态转移模型:

class TransferModel<TEvent> : BaseTransferModel
where TEvent : BaseEvent
{
public override bool Condition(BaseEvent arg)
{
var carg = arg as TEvent;
return carg != null && ConditionFunc(carg);
} public override bool Action(string target, BaseEvent arg)
{
var carg = arg as TEvent;
return carg != null && ActionFunc(target, carg);
} public Func<TEvent, bool> ConditionFunc { private get; set; }
public Func<string, TEvent, bool> ActionFunc { private get; set; }
}

剩下的就是状态机主体了,通过反射添加状态,并检查状态转移关系:

class StatusManager
{
public List<string> LostStatus { get; set; }
private Dictionary<string, XXXStatusBase> StatusData = new Dictionary<string, XXXStatusBase>();
private IModelDataService MDS = BundleServiceBus.Current.GetInstance<IModelDataService>(); LoginUserInfoModel CurrentLoginUser
{
get
{
return MDS.GetDataByModel<LoginUserInfoModel, string>(DataParameter.Empty).Data.SingleOrDefault();
}
} public StatusManager()
{
LostStatus = new List<string>(); var data = Assembly.GetExecutingAssembly().DefinedTypes.Where(s => s.BaseType == typeof(XXXStatusBase));
foreach (var typeInfo in data)
{
var status = Activator.CreateInstance(typeInfo) as XXXStatusBase;
try
{
StatusData.Add(status.Name, status);
}
catch (Exception)
{
LostStatus.Add("AddError " + status.Name);
}
} //自检
foreach (var st in StatusData.Values)
{
foreach (var tr in st.TransferList)
{
if (StatusData.All(s => s.Key != tr.Target))
{
LostStatus.Add(st.Name + " [To] " + tr.Target);
}
}
}
if (LostStatus.Count > 0)
{
Debug.WriteLine("Status has Errors!");
}
} public List<Type> GetCurrentCanUseEvent(string Status)
{
var data = new List<Type>();
if (StatusData.Keys.All(s => s != Status)) return data; foreach (var transfer in StatusData[Status].TransferList)
{
data.AddRange(transfer.GetType().GenericTypeArguments);
}
return data;
} public EventResult Trigger(BaseEvent arg)
{
//准备状态转移主体
var RootData = new StudentInfoModel { Status = "" }; if (arg.RootDataId != Guid.Empty)
{
var data = MDS.GetDataByModel<StudentInfoModel, Guid>(new DataParameter<Guid>
{
TenantId = CurrentLoginUser.GetDataParameter().TenantId,
SiteId = CurrentLoginUser.GetDataParameter().SiteId,
DataId = arg.RootDataId,
});
if (!data.IsSuccess)
{
return new EventResult
{
IsSuccess = false,
Result = data.Message
};
}
RootData = data.Data.SingleOrDefault();
} //状态转移引擎
if (StatusData.Keys.All(s => s != RootData.Status)) return new EventResult
{
IsSuccess = false,
Result = "状态[" + RootData.Status + "]不存在"
};
var status = StatusData[RootData.Status];
if (status == null)
{
return new EventResult
{
IsSuccess = false,
Result = "NotFoundStatus"
};
}
status.RootData = RootData; foreach (var transfer in status.TransferList)
{
if (!transfer.Condition(arg)) continue;
status.Exit();
if (!transfer.Action(transfer.Target, arg))
{ if (status.ReturnHttpData == null)
{
status.ReturnHttpData = new ReturnHTTPData();
}
return new EventResult
{
IsSuccess = false,
Result = status.ReturnHttpData.Content,
};
}
if (StatusData.Keys.All(s => s != RootData.Status))
{
if (status.ReturnHttpData == null)
{
status.ReturnHttpData = new ReturnHTTPData();
}
return new EventResult
{
IsSuccess = true,
Result = status.ReturnHttpData.Content,
};
}
if (StatusData[transfer.Target].Entry(arg))
{
if (status.ReturnHttpData == null)
{
status.ReturnHttpData = new ReturnHTTPData();
}
return new EventResult
{
IsSuccess = true,
Result = status.ReturnHttpData.Content,
};
}
if (status.ReturnHttpData == null)
{
status.ReturnHttpData = new ReturnHTTPData();
}
return new EventResult
{
IsSuccess = false,
Result = status.ReturnHttpData.Content,
};
}
return new EventResult
{
IsSuccess = false,
Result = "[" + arg.GetType() + "]操作不被允许"
};
}
}

具体添加一个状态:

class Status_XXX : XXXStatusBase
{
public override string Name
{
get { return "状态一"; }
} public override List<BaseTransferModel> TransferList
{
get
{
var data = new List<BaseTransferModel>();
data.Add(
new TransferModel<TargetEvent>
{
Target = "状态二",
ConditionFunc = arg => arg.Type == CommitType.Final,
ActionFunc = (target, model) =>
{
if (model != null)
{
//变更状态
if (!ChangeRootDataStatus(target)) return false; //日志
return true;
}
return false;
}
}); return data;
}
}
}

适合WebApi的简单的C#状态机实现的更多相关文章

  1. .NetCore WebApi——Swagger简单配置

    在前后端分离的大环境下,API接口文档成为了前后端交流的一个重点.Swagger让开发人员摆脱了写接口文档的痛苦. 官方网址:https://swagger.io/ 在.Net Core WebApi ...

  2. 简单三段式状态机实验3-Sequence Detect(序列检测)

    1.序列检测器的逻辑功能描述:序列检测指的就是将一个指定的序列从数字码流中识别出来.本例中,我们将设计一个"10010”序列的检测器.设x_in为数字码流输入,z_out为检出标记输出,高电 ...

  3. 简单三段式状态机实验2-LCD12864

    此实验是在“基于I2C EPPRPM(AT24C02B) + LCD12864实验”基础上,把LCD模块里的一段式状态机改成三段式,I2C EPPROM模块暂时未改出来,一步一步来吧,改完后代码下载到 ...

  4. 简单三段式状态机实验1-SOS

    一直想从一段式状态机切换到三段式状态机,从书上和网上不断搜寻三段式案例及方法,感觉很简单,就想拿之前做过的实验把一段式改成三段式,可是写起来并非那么简单,很棘手,改完后也没有成功,尤其状态机里面的计数 ...

  5. ASP.NET Core WebApi中简单像素转换跟踪实现

    像素跟踪虽然是最早用于跟踪营销转换的方法,但它仍然被广泛使用,像Facebook这样的大公司仍然将其视为跟踪网页转换的方法之一. 由于它的简单性,通过像素方法的跟踪转换仍然被广泛使用.它不需要任何复杂 ...

  6. WEBAPI 的简单示例

    一.webapi 1.在webapiconfig中移除xml的返回格式,返回格式就自动使用Json格式 config.Formatters.Remove(config.Formatters.XmlFo ...

  7. Asp.Net WebApi一个简单的Token验证

    1.前言: WebAPI主要开放数据给手机APP,Pad,其他需要得知数据的系统,或者软件应用.Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能.我上次写的<Asp.Net MV ...

  8. ajax 跨域webapi 最简单的demo(只介绍Get)

    这几天遇到一个nodejs的项目,使用VSCode开发,需要连接数据库的,但是用nodejs连接数据库比较繁琐,需要安装很多东西,本人也懒得去研究了.后来想到建一个WebAPI然后用ajax来调用,避 ...

  9. .NET Remoting、WebService、WCF、WebApi一些简单描述

    1. .NET Remoting是传输层协议TCP封装的,速度非常快,.NET Remoting基于.net反射机制,只方便.net使用,因此它有平台限制.(.NET Remoting的工作原理:服务 ...

随机推荐

  1. Shell替换

    如果表达式中包含特殊字符,Shell 将会进行替换.例如,在双引号中使用变量就是一种替换,转义字符也是一种替换. #!/bin/bash a= echo -e "Value of a is ...

  2. 谈谈DOMContentLoaded:Javascript中的domReady引入机制

    一.扯淡部分 回想当年,在摆脱写页面时js全靠从各种DEMO中copy出来然后东拼西凑的幽暗岁月之后,毅然决然地打算放弃这种处处“拿来主义”的不正之风,然后开启通往高大上的“前端攻城狮”的飞升之旅.想 ...

  3. HTML5 语义元素(二)文本内容

    上一篇介绍的是关于页面结构方面的语义元素,本篇介绍文本内容方面,包含:<bdi>.<details>.<summary>.<mark>.<outp ...

  4. svn 常用命令总结

    svn 命令篇 svn pget svn:ignore // 查看忽略项 svn commit -m "提交说明" // 提交修改 svn up(update) // 获取最新版本 ...

  5. CRL快速开发框架系列教程十一(大数据分库分表解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. 微信小程序前端源码逻辑和工作流

    看完微信小程序的前端代码真的让我热血沸腾啊,代码逻辑和设计一目了然,没有多余的东西,真的是大道至简. 废话不多说,直接分析前端代码.个人观点,难免有疏漏,仅供参考. 文件基本结构: 先看入口app.j ...

  7. 屌丝giser成长记-大学篇

    作为一名屌丝giser的我,刚接触gis专业是2007年的大一,好悲催,当时gis这个专业是被调剂的,我压根都不知道gis为何物,那时候gis冷门的一逼,报名这个专业的寥寥无几.记得那时候得知被调剂到 ...

  8. BPM体系文件管理解决方案分享

    一.方案概述 企业管理在很大程度上是通过文件化的形式表现出来,体系文件管理是管理体系存在的基础和证据,是规范企业管理活动和全体人员行为,达到管理目标的管理依据.对与公司质量.环境.职业健康安全等体系有 ...

  9. BAT“搅局”B2B市场,CIO们准备好了吗?

    "CIO必须灵活构建其所在企业的IT系统,深入业务,以应对日新月异的数字化业务环境."   BAT军团"搅局"B2B市场,CIO们准备好了吗? 庞大的企业级市场 ...

  10. 【从零开始学BPM,Day4】业务集成

    [课程主题] 主题:5天,一起从零开始学习BPM [课程形式] 1.为期5天的短任务学习 2.每天观看一个视频,视频学习时间自由安排. [第四天课程] 1.课程概要 Step 1 软件下载:H3 BP ...