不可撤销下载,梵高的作品,农夫最新导航
never是纯c#语言开发的一个框架,同时可在netcore下运行。 该框架github地址:
同时,配合never_web,never_component,never_application (demo)可对比代码学习。
引用其图片说明该构架所涉及到的工具
使用emit技术所实现的核心功能点
其中使用包含了一些开发设计模式,比如message的订阅与发布,熔断机制等。
1、以applicationstartup开始,启动服务,注册不同组件,这里是netcore的部分代码
/// <summary> /// 该方法被configureservices里面的base.configureservicese调用,由于configureservices方法会使用不同的组件方案,所以在其后面启支,是将这些组件方案所注册的ioc规则加入到自己的ioc规则里面去 /// 同时替换了系统iservicecollection自己生成的iserviceprovider对象 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void startup_onstarting(object sender, never.startupeventargs e) { //ddd的command里面使用了恢复(即一些命令出错后被保存后过段时间再执行),当前使用sqlite本地数据库方式 var commandfile = new fileinfo(appcontext.basedirectory + "\\app_data\\command_demo.db"); //ddd的event跟上面的一样 var eventfile = new fileinfo(appcontext.basedirectory + "\\app_data\\event_demo.db"); //使用nlog组件 var logfile = new fileinfo(appcontext.basedirectory + "\\app_config\\nlog.config"); //配置文件的读取 var configreader = new appconfigreader(this.configuration); }
我们先对程序集过滤与开启ioc
//注册程序集过滤,因为整个启动过程会分析程序集里面的type对象,很多dll我们不用分析,只焦点到我们现在注入的2个规则就行,"never" + "b2c",正则只要匹配到该字符就加加载到待分析的dll集合中 e.startup.registerassemblyfilter("b2c".createassemblyfilter()).registerassemblyfilter("never".createassemblyfilter()); //ioc分2种启动方法,主要原因如下:(1)服务启动有先后顺序,不同的系统组件所注册的顺序不同的,但有些组件要求在所有环境下都只有第一或最后启动(2)由于使用环境自动注册这种设计下,一些组件要手动注册会带自己的规则就会被自动注册覆盖 e.startup.useeasyioc( (x, y, z) => { //先启动该服务注册组件, }, (x, y, z) => { //再按自己的个性化注册组件,比如controller在下面useapidependency后会自动注入,但是我想homecontroller注入的时候使用memecahed,这种情况就要手动注入了 //x.registertype<controllers.homecontroller, controllers.homecontroller>().withparameter<never.caching.icaching>("memcached"); //注入query与repository实例,为什么不用自动注入?哈哈,因为在framework或netcore等各种不同的环境下大家读取配置文件是不同的,一旦写死在b2c.message.sqldata.query里面读取配置文件,则使用不同的host技术就出现极大问题, //比如netcore没有connectionstring这种配置(或者有人说可以手动引用system.configuration,这不是嫌麻烦吗) x.registerinstance(new b2c.message.sqldata.query.querydaobuilder(infrastructure.sqldbtype.sqlserver, () => configreader["message_conn"])); x.registerinstance(new b2c.message.sqldata.repository.repositorydaobuilder(infrastructure.sqldbtype.sqlserver, () => configreader["message_conn"])); });
注册各种组件
//使用环境下自动注册组件, e.startup.useautoinjectingattributeusingioc(new iautoinjectingenvironmentprovider[] { //在message该环境下,所有单例注册组件只有匹配message的才注册,(1)有些组件是线程的,那么不会被描述和注入中,除非再加个线程provider;(2)即使是单例provider,但所运行不是message环境,所以也不会注入 singletonautoinjectingenvironmentprovider.usingrulecontainerautoinjectingenvironmentprovider("message"), }) //使用统一配置中心读取配置文件,实用性在后面有讲到 .useconfigclient(new ipendpoint(ipaddress.parse(configreader["config_host"]), configreader.intinappconfig("config_port")), out var configfileclient); configfileclient.startup(timespan.fromminutes(10), new[] { new configfileclientrequest { filename = "message_api" } }, (c, t) => { var content = t; if (c != null && c.filename == "message_api") { system.io.file.writealltext(system.io.path.combine(this.environment.contentrootpath, "appsettings.app.json"), content); } }).push("message_api").getawaiter().getresult(); e.startup .usecountercache() //使用countcache .useconcurrentcache() //使用安全countcache .usedatacontractjson() //使用datacontract技术的序列化,实现了ijsonserialize接口 .useeasyjson(string.empty) //使用easyjson技术的序列化,实现了ijsonserialize接口 .usenlog(logfile) //使用nlog .useappconfig(configreader) //将iconfigreader注入 .useforcecheckaggregaterootimplihandle() //这几个force都是为了检查ddd开发一些要求,比如是否继承某个类,某些接口 .useforcecheckcommandappdomainattribute() //检查所有的command是否带了特定attribute .useforcecheckcommandevenwithnoparamaterctor() //检查所有的commandhandler所要的构造参数是否被注入中 .useforcecheckcommandhandlerctor() //检查所有的eventhandler所要的构造参数是否被注入中 .useforcecheckeventappdomainattribute()//检查所有的event是否带了特定attribute .useforcecheckeventhandlerctor() //检查所有的eventhandler所要的构造参数是否被注入中 .useforcecheckmessagesubscriberctor() //使用消息的订单与发布 .useinjectingcommandhandlereventhandler(never.ioc.componentlifestyle.singleton) //注入所有的commandhandler,在commandbus执行其对象行为 .usesqliteeventprovidercommandbus<defaultcommandcontext>(new sqlitefailrecoverystorager(commandfile, eventfile)) //使用cqrs组件,指定sqlite作为恢复组件, .useapimodelstatevalidation() //mvc,webapi的模型参数验证 .useapiactioncustomroute(e.collector as iservicecollection) //自定义路由,相同于在controller可以使用httpget等route技术 .useapidependency(e.collector as iservicecollection);//注入所有的controller
最后启动过程中检查整个系统是否正常
//配置中心更新配置文件后,系统不一定马上能重新加载 e.startup.startup(timespan.fromseconds(1), (x) => { //我们在此启动看看所使用组件是否正常启动 using (var sc = x.servicelocator.beginlifetimescope()) { sc.resolve<icommandbus>(); sc.resolve<iloggerbuilder>(); sc.resolve<ijsonserializer>(); var home = sc.resolve<controllers.messagecontroller>(); var logger = sc.resolve<iloggerbuilder>().build(typeof(startup)); logger.info("startup at " + datetime.now.tostring("yyyy-mm-dd hh:mm:ss")); } }); }
2、controller的注入,使用构造函数的方法注入
private readonly iemailcodequery emailcodequery = null; private readonly imobilecodequery mobilecodequery = null; private readonly icommandbus commandbus = null; private readonly iloggerbuilder loggerbuilder = null; private readonly ijsonserializer jsonserializer = null; public vcodecontroller(icommandbus commandbus, iloggerbuilder loggerbuilder, ijsonserializer jsonserializer, iemailcodequery emailcodequery, imobilecodequery mobilecodequery) { this.commandbus = commandbus; this.loggerbuilder = loggerbuilder; this.jsonserializer = jsonserializer; this.emailcodequery = emailcodequery; this.mobilecodequery = mobilecodequery; }
3、action代码处理
/// <summary> /// 校验邮箱验证码 /// </summary> /// <param name="reqs"></param> /// <returns></returns> [apiactionremark("a9a900aee8c6", "httppost"), httppost] public apiresult<string> checkemailvalidatecode(checkemailvalidatecodereqs reqs) { if (!this.tryvalidatemodel(reqs)) { return anonymous.newapiresult(apistatus.fail, string.empty, this.modelerrormessage); } //实际上不用try + catch了,因为在startup统一日志处理了。 //发送命令后交给commandhandler去处理领域,commandbus + eventbus var handler = this.commandbus.send(new destroyemailcodecommand(newid.generateguid()) { email = reqs.email, usagetype = reqs.usagetype, vcode = reqs.vcode, }); if (handler == null) { return anonymous.newapiresult(apistatus.fail, string.empty, "验证失败"); } if (handler.status != commandhandlerstatus.success) { return anonymous.newapiresult(apistatus.error, string.empty, this.handlermerssage(handler)); } return anonymous.newapiresult(apistatus.success, string.empty); }
我们打开startup文件或global文件来看看,整个构架的初始化都在global或startup里面实现的,环境的搭建比较简单,可以直接开发业务而不关心组件实现方式。
摘要里面一些代码展示:
1、接口与实现使用ioc管理,加上灵活的aop,可统一日志管理的管理
[logger] public class emailcodecommandhandler : icommandhandler<createemailcodecommand>, icommandhandler<destroyemailcodecommand> { }
2、对远程方法的调用,封装成本地调用方式
//实际上这里是web远程方法,使用代理生成类,带熔断, var api = this.validatecodeservice.createmobilevalidatecode(new message.contract.request.createmobilevalidatecodereqs() { mobile = model.username, clientip = this.getappip(), platform = this.getappplatform(), length = 4, usagetype = message.contract.enumtypes.usagetype.注册, });
3、友好的参数验证,用户自己加验证参数规则。
/// <summary> /// 用户model /// </summary> [serializable, validator(typeof(requestvalidator))] public class userviewmodel { #region prop /// <summary> /// 用户名 /// </summary> [displayname("用户名")] public string username { get; set; } #endregion prop #region validator private class requestvalidator : validator<userviewmodel> { public override ienumerable<keyvaluepair<expression<func<userviewmodel, object>>, string>> rulefor(userviewmodel target) { if (target.username.isnullorwhitespace()) yield return new keyvaluepair<expression<func<userviewmodel, object>>, string>(m => m.username, "手机号码为空"); } } #endregion validator }
4、可靠的性能:json的序列化与反序列化,在反序列化timespan下(字符串:"00:10:00"), 2700x + 32g内存1000万次测试,jsonnet 使用12.6秒(gc=3.7万),easyser使用2.6秒(gc=3.7k),jil使用0.8秒(gc=1.2k)
5、简单的配置:系统初始化过程风格统一,还有组件eqsysql只需要xml文件 + 链接字符串,就可以实现orm管理(如queryforobject<t>,queryforenumerable<t>)
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
Blazor server side 自家的一些开源的, 实用型项目的进度之 CEF客户端
.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)
vue+.netcore可支持业务代码扩展的开发框架 VOL.Vue 2.0版本发布
网友评论