目标

采用了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. C#4.0泛型的协变,逆变深入剖析

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  2. 基于DDD的现代ASP.NET开发框架--ABP系列文章总目录

    ABP相关岗位招聘:给热爱.NET新技术和ABP框架的朋友带来一个高薪的工作机会 ABP交流会录像视频:ABP架构设计交流群-7月18日上海线下交流会的内容分享(有高清录像视频的链接) 代码自动生成: ...

  3. centos7+mono4+jexus5.6.2安装过程中的遇到的问题

    过程参考: http://www.linuxdot.net/ http://www.jexus.org/ http://www.mono-project.com/docs/getting-starte ...

  4. JavaScript动画-磁性吸附

    ▓▓▓▓▓▓ 大致介绍 磁性吸附是以模拟拖拽为基础添加一个拖拽时范围的限定而来的一个效果,如果对模拟拖拽有疑问的同学请移步模拟拖拽. 源代码.效果:点这里 ▓▓▓▓▓▓ 范围限定(可视区) 先来看一个 ...

  5. JQuery(2)

    JQuery下拉框操作: 取值赋值操作 body代码: <select id="sel"> <option value="北京">北京& ...

  6. 接口--interface

    “interface”(接口)关键字使抽象的概念更深入了一层.我们可将其想象为一个“纯”抽象类.它允许创建者规定一个类的基本形式:方法名.自变量列表以及返回类型,但不规定方法主体.接口也包含了基本数据 ...

  7. 从源码浅析MVC的MvcRouteHandler、MvcHandler和MvcHttpHandler

    熟悉WebForm开发的朋友一定都知道,Page类必须实现一个接口,就是IHttpHandler.HttpHandler是一个HTTP请求的真正处理中心,在HttpHandler容器中,ASP.NET ...

  8. SQL面试笔试经典题(Part 1)

    本文是在Cat Qi的原贴的基础之上,经本人逐题分别在MySql数据库中实现的笔记,持续更新... 参考原贴:http://www.cnblogs.com/qixuejia/p/3637735.htm ...

  9. NodeJS使用mysql

    1.环境准备 手动添加数据库依赖: 在package.json的dependencies中新增, "mysql" : "latest", { "nam ...

  10. http协议(十一)http与https

    一.http的缺点 之前有介绍过http协议相关的一些知识,http是相当优秀和方便的,但它也有缺点,主要不足表现在如下几个方面: △ 通信使用明文(不加密),内容可能会被窃听 △ 不验证通信方的身份 ...