适合WebApi的简单的C#状态机实现
目标
采用了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#状态机实现的更多相关文章
- .NetCore WebApi——Swagger简单配置
在前后端分离的大环境下,API接口文档成为了前后端交流的一个重点.Swagger让开发人员摆脱了写接口文档的痛苦. 官方网址:https://swagger.io/ 在.Net Core WebApi ...
- 简单三段式状态机实验3-Sequence Detect(序列检测)
1.序列检测器的逻辑功能描述:序列检测指的就是将一个指定的序列从数字码流中识别出来.本例中,我们将设计一个"10010”序列的检测器.设x_in为数字码流输入,z_out为检出标记输出,高电 ...
- 简单三段式状态机实验2-LCD12864
此实验是在“基于I2C EPPRPM(AT24C02B) + LCD12864实验”基础上,把LCD模块里的一段式状态机改成三段式,I2C EPPROM模块暂时未改出来,一步一步来吧,改完后代码下载到 ...
- 简单三段式状态机实验1-SOS
一直想从一段式状态机切换到三段式状态机,从书上和网上不断搜寻三段式案例及方法,感觉很简单,就想拿之前做过的实验把一段式改成三段式,可是写起来并非那么简单,很棘手,改完后也没有成功,尤其状态机里面的计数 ...
- ASP.NET Core WebApi中简单像素转换跟踪实现
像素跟踪虽然是最早用于跟踪营销转换的方法,但它仍然被广泛使用,像Facebook这样的大公司仍然将其视为跟踪网页转换的方法之一. 由于它的简单性,通过像素方法的跟踪转换仍然被广泛使用.它不需要任何复杂 ...
- WEBAPI 的简单示例
一.webapi 1.在webapiconfig中移除xml的返回格式,返回格式就自动使用Json格式 config.Formatters.Remove(config.Formatters.XmlFo ...
- Asp.Net WebApi一个简单的Token验证
1.前言: WebAPI主要开放数据给手机APP,Pad,其他需要得知数据的系统,或者软件应用.Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能.我上次写的<Asp.Net MV ...
- ajax 跨域webapi 最简单的demo(只介绍Get)
这几天遇到一个nodejs的项目,使用VSCode开发,需要连接数据库的,但是用nodejs连接数据库比较繁琐,需要安装很多东西,本人也懒得去研究了.后来想到建一个WebAPI然后用ajax来调用,避 ...
- .NET Remoting、WebService、WCF、WebApi一些简单描述
1. .NET Remoting是传输层协议TCP封装的,速度非常快,.NET Remoting基于.net反射机制,只方便.net使用,因此它有平台限制.(.NET Remoting的工作原理:服务 ...
随机推荐
- LeetCode-3LongestSubstringWithoutRepeatingCharacters(C#)
# 题目 3. Longest Substring Without Repeating Characters Given a string, find the length of the longes ...
- 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)
其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...
- .Net Core上也可以使用的二维码组件
我Fork了QRCoder,并且兼容了.Net Core,图形库用的是ZKWeb.System.Drawing Github: https://github.com/zkweb-framework/Q ...
- 设计模式之结构类模式大PK
结构类模式大PK 结构类模式包括适配器模式.桥梁模式.组合模式.装饰模式.门面模式.享元模式和代理模式.之所以称其为结构类模式,是因 ...
- Kooboo CMS技术文档之三:切换数据存储方式
切换数据存储方式包括以下几种: 将文本内容存储在SqlServer.MySQL.MongoDB等数据库中 将站点配置信息存储在数据库中 将后台用户信息存储在数据库中 将会员信息存储在数据库中 将图片. ...
- [原创]关于Hibernate中的级联操作以及懒加载
Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...
- GJM : C#设计模式汇总整理——导航 【原创】
感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...
- UML图中经常用到几种的关系图例
学习这个东西挺奇怪的,时间一长就容易忘记,或者记不清楚.今天看到一些UML图的关系,发现有些出入了,索性就写下来,以后再忘记的时候过来看看. 在UML的类图中,常见的有以下几种关系: 继承(Gener ...
- Android开发学习——动画
帧动画> 一张张图片不断的切换,形成动画效果* 在drawable目录下定义xml文件,子节点为animation-list,在这里定义要显示的图片和每张图片的显示时长 ...
- 微信小程序监控 - HotApp统计
HotApp小程序统计,第一个专业的微信第三方小程序监控统计工具 1.什么是HotApp小程序统计 HotApp小程序统计是第一个微信第三方小程序统计工具,就像做android 和 ios开发的人知道 ...