适合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的工作原理:服务 ...
随机推荐
- dotNET跨平台相关文档整理
一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...
- Jquery的事件操作和文档操作
对于熟悉前端开发的小伙伴,相信对于Jquery一定不陌生,相对于JavaScript的繁琐,Jquery更加的简洁,当然简洁不意味着简单,我们可以使用Jquery完成我们想要实现全部功能,这里为小白们 ...
- jQuery学习之路(7)- 用原生JavaScript实现jQuery的某些简单功能
▓▓▓▓▓▓ 大致介绍 学习了妙味,用原生的JavaScript实现jQuery中的某些部分功能 定义自己的函数库lQuery ▓▓▓▓▓▓ $()选择器的实现 jQuery是面向对象的,所以自己编写 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付
微信支付教程系列之扫码支付 今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添 ...
- AFNetworking 3.0 源码解读(六)之 AFHTTPSessionManager
AFHTTPSessionManager相对来说比较好理解,代码也比较短.但却是我们平时可能使用最多的类. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilit ...
- VICA 架构设计(1)
本文记录最近完成的一个通用实时通信客户端的架构. 背景 我们公司是做税务相关的软件,有针对大客户 MIS 系统,也有针对中小客户的 SaaS 平台.这些系统虽然都是 B/S 的,但是也需要使用 A ...
- “此网页上的某个 Web 部件或 Web 表单控件无法显示或导入。找不到该类型,或该类型未注册为安全类型。”
自从vs装了Resharper,看见提示总是手贱的想去改掉它.于是乎手一抖,把一个 可视web部件的命名空间给改了. 喏,从LibrarySharePoint.WebPart.LibraryAddEd ...
- Android—Service与Activity的交互
service-Android的四大组件之一.人称"后台服务"指其本身的运行并不依赖于用户可视的UI界面 实际开发中我们经常需要service和activity之间可以相互传递数据 ...
- Android6.0运行时权限管理
自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装 ...
- linux系统oracle-ora12505问题解决方案一
说明:(1)Linux版本 Linux version 2.6.32.12-0.7-default (geeko@buildhost) (gcc version 4.3.4 [gcc-4_3-bran ...