适合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的工作原理:服务 ...
随机推荐
- 【小程序分享篇 二 】web在线踢人小程序,维持用户只能在一个台电脑持登录状态
最近离职了, 突然记起来还一个小功能没做, 想想也挺简单,留下代码和思路给同事做个参考. 换工作心里挺忐忑, 对未来也充满了憧憬与担忧.(虽然已是老人, 换了N次工作了,但每次心里都和忐忑). 写写代 ...
- 【探索】机器指令翻译成 JavaScript
前言 前些时候研究脚本混淆时,打算先学一些「程序流程」相关的概念.为了不因太枯燥而放弃,决定想一个有趣的案例,可以边探索边学. 于是想了一个话题:尝试将机器指令 1:1 翻译 成 JavaScript ...
- HTML5 localStorage本地存储
介绍 localStorage(本地存储)的使用方式.包括对存储对象的添加.修改.删除.事件触发等操作. 目录 1. 介绍 1.1 说明 1.2 特点 1.3 浏览器最小版本支持 1.4 适合场景 2 ...
- Hawk 6. 高级话题:子流程系统
子流程的定义 当流程设计的越来越复杂,越来越长时,就难以进行管理了.因此,采用模块化的设计才会更加合理.本节我们介绍子流程的原理和使用. 所谓子流程,就是能先构造出一个流程,然后被其他流程调用.被调用 ...
- SignalR SelfHost实时消息,集成到web中,实现服务器消息推送
先前用过两次SignalR,但是中途有段时间没弄了,今天重新弄,发现已经忘得差不多了,做个笔记! 首先创建一个控制台项目Nuget添加引用联机搜索:Microsoft.AspNet.SignalR.S ...
- XSS分析及预防
XSS(Cross Site Scripting),又称跨站脚本,XSS的重点不在于跨站点,而是在于脚本的执行.在WEB前端应用日益发展的今天,XSS漏洞尤其容易被开发人员忽视,最终可能造成对个人信息 ...
- [原创]关于Hibernate中的级联操作以及懒加载
Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...
- listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局 android系统为listview提供了addfootview ...
- SQL中字符串拼接
1. 概述 在SQL语句中经常需要进行字符串拼接,以sqlserver,oracle,mysql三种数据库为例,因为这三种数据库具有代表性. sqlserver: select '123'+'456' ...
- [Xamarin] 透過Native Code呼叫 JavaScript function (转帖)
今天我們來聊聊關於如何使用WebView 中的Javascript 來呼叫 Native Code 的部分 首先,你得先來看看這篇[Xamarin] 使用Webview 來做APP因為這篇文章至少講解 ...