parent
eb762784e7
commit
76564675e0
55 changed files with 1006 additions and 935 deletions
@ -0,0 +1,46 @@ |
|||||||
|
{ |
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging |
||||||
|
// Use hover for the description of the existing attributes |
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md |
||||||
|
"version": "0.2.0", |
||||||
|
"configurations": [ |
||||||
|
{ |
||||||
|
"name": ".NET Core Launch (web)", |
||||||
|
"type": "coreclr", |
||||||
|
"request": "launch", |
||||||
|
"preLaunchTask": "build", |
||||||
|
// If you have changed target frameworks, make sure to update the program path. |
||||||
|
"program": "${workspaceFolder}/samples/ASPNetCoreExecutor/bin/Debug/netcoreapp2.2/ASPNetCoreExecutor.dll", |
||||||
|
"args": [], |
||||||
|
"cwd": "${workspaceFolder}/samples/ASPNetCoreExecutor", |
||||||
|
"stopAtEntry": false, |
||||||
|
"internalConsoleOptions": "openOnSessionStart", |
||||||
|
"launchBrowser": { |
||||||
|
"enabled": true, |
||||||
|
"args": "${auto-detect-url}", |
||||||
|
"windows": { |
||||||
|
"command": "cmd.exe", |
||||||
|
"args": "/C start ${auto-detect-url}" |
||||||
|
}, |
||||||
|
"osx": { |
||||||
|
"command": "open" |
||||||
|
}, |
||||||
|
"linux": { |
||||||
|
"command": "xdg-open" |
||||||
|
} |
||||||
|
}, |
||||||
|
"env": { |
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||||
|
}, |
||||||
|
"sourceFileMap": { |
||||||
|
"/Views": "${workspaceFolder}/Views" |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": ".NET Core Attach", |
||||||
|
"type": "coreclr", |
||||||
|
"request": "attach", |
||||||
|
"processId": "${command:pickProcess}" |
||||||
|
} |
||||||
|
,] |
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace {{namespace}} |
||||||
|
{ |
||||||
|
public class {{name}} |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
export class {{name}} { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
Imports System |
||||||
|
|
||||||
|
Namespace {{namespace}} |
||||||
|
|
||||||
|
Public Class {{name}} |
||||||
|
|
||||||
|
End Class |
||||||
|
|
||||||
|
End Namespace |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
export default {{name}} { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace {{namespace}} |
||||||
|
{ |
||||||
|
public enum {{name}} |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
|
namespace {{namespace}} |
||||||
|
{ |
||||||
|
public interface {{name}} |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
export interface {{name}} { |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,46 @@ |
|||||||
|
{ |
||||||
|
"templates": [ |
||||||
|
{ |
||||||
|
"name": "Class", |
||||||
|
"extension": "cs", |
||||||
|
"file": "./class.cs-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Interface", |
||||||
|
"extension": "cs", |
||||||
|
"file": "./interface.cs-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Enum", |
||||||
|
"extension": "cs", |
||||||
|
"file": "./enum.cs-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Class", |
||||||
|
"extension": "ts", |
||||||
|
"file": "./class.ts-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Interface", |
||||||
|
"extension": "ts", |
||||||
|
"file": "./interface.ts-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Default", |
||||||
|
"extension": "ts", |
||||||
|
"file": "./default.ts-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"name": "Class", |
||||||
|
"extension": "vb", |
||||||
|
"file": "./class.vb-template", |
||||||
|
"parameters": "./template-parameters.js" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
var path = require("path"); |
||||||
|
|
||||||
|
module.exports = function(filename, projectPath, folderPath) { |
||||||
|
var namespace = "Unknown"; |
||||||
|
if (projectPath) { |
||||||
|
namespace = path.basename(projectPath, path.extname(projectPath)); |
||||||
|
if (folderPath) { |
||||||
|
namespace += "." + folderPath.replace(path.dirname(projectPath), "").substring(1).replace(/[\\\/]/g, "."); |
||||||
|
} |
||||||
|
}
|
||||||
|
|
||||||
|
return { |
||||||
|
namespace: namespace, |
||||||
|
name: path.basename(filename, path.extname(filename)) |
||||||
|
} |
||||||
|
}; |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
{ |
||||||
|
"version": "2.0.0", |
||||||
|
"tasks": [ |
||||||
|
{ |
||||||
|
"label": "build", |
||||||
|
"command": "dotnet", |
||||||
|
"type": "process", |
||||||
|
"args": [ |
||||||
|
"build", |
||||||
|
"${workspaceFolder}/samples/ASPNetCoreExecutor/ASPNetCoreExecutor.csproj" |
||||||
|
], |
||||||
|
"problemMatcher": "$msCompile" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
@ -1,2 +1,168 @@ |
|||||||
# DotXxlJob |
# DotXxlJob |
||||||
xxl-job的dotnet core 执行器实现 |
xxl-job的dotnet core 执行器实现,支持XXL-JOB 2.0+ |
||||||
|
|
||||||
|
## 1 XXL-JOB概述 |
||||||
|
[XXL-JOB][1]是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。以下是它的架构图 |
||||||
|
 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 2. 关于DotXxlJob产生 |
||||||
|
在工作中调研过多个任务调度平台,如Hangfire、基于Quatz.NET的第三方扩展,都与实际的需求有一点差距。 之前一直使用Hangfire,Hangfire的执行器在同步调用业务服务时,如果即时业务服务正在重新部署或者重启,有一定概率会出现死锁,导致CPU100%,后来全部调整为异步,但是这样就无法获得执行结果,这样的设计有蛮大问题,XxlJob的回调机制很好的解决了这个问题。本身如果通过http的方式调用,只要部署springbootd的一个执行器就可以解决问题,但是扩展性较差。所以萌生了实现DotNet版本的执行器的想法,为避免重复造轮子,开始之前也进行过调研,以下仓库[https://github.com/yuniansheng/xxl-job-dotnet][2]给了较大的启发,但是该库只支持1.9版本的xxljob,还有一些其他小问题,所以还是自力更生。 |
||||||
|
|
||||||
|
## 3. 如何使用 |
||||||
|
|
||||||
|
目前只实现了BEAN的方式,即直接实现IJobHandler调用的方式,Glue源码的方式实际上实现起来也并不复杂(有需求再说把),或者各位有需求Fork 实现一下 |
||||||
|
|
||||||
|
可参考sample |
||||||
|
|
||||||
|
安装: |
||||||
|
|
||||||
|
> dotnet add package DotXxlJob.Core |
||||||
|
|
||||||
|
### 3.1 在AspNetCore中使用 |
||||||
|
|
||||||
|
1. 生命一个AspNet的Middleware中间件,并扩展ApplicationBuilder,本质是拦截Post请求,解析Body中的流信息 |
||||||
|
|
||||||
|
``` |
||||||
|
public class XxlJobExecutorMiddleware |
||||||
|
{ |
||||||
|
private readonly IServiceProvider _provider; |
||||||
|
private readonly RequestDelegate _next; |
||||||
|
|
||||||
|
private readonly XxlRpcServiceHandler _rpcService; |
||||||
|
public XxlJobExecutorMiddleware(IServiceProvider provider, RequestDelegate next) |
||||||
|
{ |
||||||
|
this._provider = provider; |
||||||
|
this._next = next; |
||||||
|
this._rpcService = _provider.GetRequiredService<XxlRpcServiceHandler>(); |
||||||
|
} |
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context) |
||||||
|
{ |
||||||
|
|
||||||
|
if ("POST".Equals(context.Request.Method, StringComparison.OrdinalIgnoreCase) && |
||||||
|
"application/octet-stream".Equals(context.Request.ContentType, StringComparison.OrdinalIgnoreCase)) |
||||||
|
{ |
||||||
|
var rsp = await _rpcService.HandlerAsync(context.Request.Body); |
||||||
|
context.Response.StatusCode = (int) HttpStatusCode.OK; |
||||||
|
context.Response.ContentType = "text/plain;utf-8"; |
||||||
|
await context.Response.Body.WriteAsync(rsp,0,rsp.Length); |
||||||
|
return; |
||||||
|
} |
||||||
|
await _next.Invoke(context); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
扩展ApplicationBuilderExtensions,可根据实际情况绑定在特殊的Url Path上 |
||||||
|
|
||||||
|
``` |
||||||
|
public static class ApplicationBuilderExtensions |
||||||
|
{ |
||||||
|
public static IApplicationBuilder UseXxlJobExecutor(this IApplicationBuilder @this) |
||||||
|
{ |
||||||
|
return @this.UseMiddleware<XxlJobExecutorMiddleware>(); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
在Startup中添加必要的引用,其中自动注册。 |
||||||
|
|
||||||
|
``` |
||||||
|
public class Startup |
||||||
|
{ |
||||||
|
public Startup(IConfiguration configuration) |
||||||
|
{ |
||||||
|
Configuration = configuration; |
||||||
|
} |
||||||
|
|
||||||
|
private IConfiguration Configuration { get; } |
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services) |
||||||
|
{ |
||||||
|
|
||||||
|
services.AddXxlJobExecutor(Configuration); |
||||||
|
services.AddSingleton<IJobHandler, DemoJobHandler>(); // 添加自定义的jobHandler |
||||||
|
services.AddAutoRegistry(); // 自动注册 |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app,IHostingEnvironment env) |
||||||
|
{ |
||||||
|
//启用XxlExecutor |
||||||
|
app.UseXxlJobExecutor(); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
编写JobHandler,继承AbstractJobHandler或者直接实现接口IJobHandler,通过context.JobLogger 记录执行过程和结果,在AdminWeb上可查看的哦 |
||||||
|
``` |
||||||
|
[JobHandler("demoJobHandler")] |
||||||
|
public class DemoJobHandler:AbstractJobHandler |
||||||
|
{ |
||||||
|
public override Task<ReturnT> Execute(JobExecuteContext context) |
||||||
|
{ |
||||||
|
context.JobLogger.Log("receive demo job handler,parameter:{0}",context.JobParameter); |
||||||
|
|
||||||
|
return Task.FromResult(ReturnT.SUCCESS); |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 3.2 配置信息 |
||||||
|
管理端地址和端口是必填信息,其他根据实际情况,选择配置,配置项说明见下代码中的注释 |
||||||
|
|
||||||
|
``` |
||||||
|
public class XxlJobExecutorOptions |
||||||
|
{ |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// 管理端地址,多个以;分隔 |
||||||
|
/// </summary> |
||||||
|
public string AdminAddresses { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// appName自动注册时要去管理端配置一致 |
||||||
|
/// </summary> |
||||||
|
public string AppName { get; set; } = "xxl-job-executor-dotnet"; |
||||||
|
/// <summary> |
||||||
|
/// 自动注册时提交的地址,为空会自动获取内网地址 |
||||||
|
/// </summary> |
||||||
|
public string SpecialBindAddress { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// 绑定端口 |
||||||
|
/// </summary> |
||||||
|
public int Port { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// 是否自动注册 |
||||||
|
/// </summary> |
||||||
|
public bool AutoRegistry { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// 认证票据 |
||||||
|
/// </summary> |
||||||
|
public string AccessToken { get; set; } |
||||||
|
/// <summary> |
||||||
|
/// 日志目录,默认为执行目录的logs子目录下,请配置绝对路径 |
||||||
|
/// </summary> |
||||||
|
public string LogPath { get; set; } = Path.Combine(AppContext.BaseDirectory, "./logs"); |
||||||
|
/// <summary> |
||||||
|
/// 日志保留天数 |
||||||
|
/// </summary> |
||||||
|
public int LogRetentionDays { get; set; } = 30; |
||||||
|
} |
||||||
|
``` |
||||||
|
## 在其他Http服务中使用 |
||||||
|
|
||||||
|
只需要实现Http请求的拦截,并判断post请求中content-Type="application/octet-stream",并使用XxlRpcServiceHandler来处理流 即可。 |
||||||
|
|
||||||
|
## 其他说明 |
||||||
|
XXL-JOB内置的RPC是使用Hessian协议,这个有点坑。很多都是java特有的属性和标识,比如类名什么的。在本项目中,并没有实现完整的Hessian2协议,只实现了使用到的类型,当然扩展起来也非常方便。如果有人要单独使用Hessian 这个类库的话,要特别注意这个问题。 |
||||||
|
|
||||||
|
有任何问题,可Issue反馈 ,最后感谢 xxl-job |
||||||
|
|
||||||
|
|
||||||
|
如果觉得对你有帮助,可以请我吃包肥仔快乐薯片 |
||||||
|
  |
||||||
|
|
||||||
|
[1]: http://www.xuxueli.com/xxl-job |
||||||
|
[2]: https://github.com/yuniansheng/xxl-job-dotnet |
||||||
@ -1,2 +1,4 @@ |
|||||||
#### 0.1.0 Jan 10, 2019 |
#### 1.0.0 Jan 19, 2019 |
||||||
- 首次发布,实现xxl-job-execute的实现 |
- 首次发布,实现xxl-job-execute的实现 |
||||||
|
- 实现基本的Bean JobHandler的功能 |
||||||
|
- 实现自动注册 |
||||||
@ -1,7 +1,13 @@ |
|||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
|
||||||
namespace DotXxlJob.Core |
namespace DotXxlJob.Core |
||||||
{ |
{ |
||||||
public interface IExecutorRegistry |
public interface IExecutorRegistry |
||||||
{ |
{ |
||||||
void Start(); |
|
||||||
|
Task RegistryAsync(CancellationToken cancellationToken); |
||||||
|
|
||||||
|
|
||||||
} |
} |
||||||
} |
} |
||||||
@ -1,7 +1,135 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Net; |
||||||
|
using System.Net.NetworkInformation; |
||||||
|
using System.Net.Sockets; |
||||||
|
|
||||||
namespace DotXxlJob.Core |
namespace DotXxlJob.Core |
||||||
{ |
{ |
||||||
public class IPUtility |
/// <summary> |
||||||
|
/// ip utility |
||||||
|
/// </summary> |
||||||
|
internal static class IPUtility |
||||||
{ |
{ |
||||||
|
#region Private Members |
||||||
|
/// <summary> |
||||||
|
/// A类: 10.0.0.0-10.255.255.255 |
||||||
|
/// </summary> |
||||||
|
private static long ipABegin, ipAEnd; |
||||||
|
/// <summary> |
||||||
|
/// B类: 172.16.0.0-172.31.255.255 |
||||||
|
/// </summary> |
||||||
|
private static long ipBBegin, ipBEnd; |
||||||
|
/// <summary> |
||||||
|
/// C类: 192.168.0.0-192.168.255.255 |
||||||
|
/// </summary> |
||||||
|
private static long ipCBegin, ipCEnd; |
||||||
|
#endregion |
||||||
|
|
||||||
|
#region Constructors |
||||||
|
/// <summary> |
||||||
|
/// static new |
||||||
|
/// </summary> |
||||||
|
static IPUtility() |
||||||
|
{ |
||||||
|
ipABegin = ConvertToNumber("10.0.0.0"); |
||||||
|
ipAEnd = ConvertToNumber("10.255.255.255"); |
||||||
|
|
||||||
|
ipBBegin = ConvertToNumber("172.16.0.0"); |
||||||
|
ipBEnd = ConvertToNumber("172.31.255.255"); |
||||||
|
|
||||||
|
ipCBegin = ConvertToNumber("192.168.0.0"); |
||||||
|
ipCEnd = ConvertToNumber("192.168.255.255"); |
||||||
|
} |
||||||
|
#endregion |
||||||
|
|
||||||
|
#region Public Methods |
||||||
|
/// <summary> |
||||||
|
/// ip address convert to long |
||||||
|
/// </summary> |
||||||
|
/// <param name="ipAddress"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private static long ConvertToNumber(string ipAddress) |
||||||
|
{ |
||||||
|
return ConvertToNumber(IPAddress.Parse(ipAddress)); |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// ip address convert to long |
||||||
|
/// </summary> |
||||||
|
/// <param name="ipAddress"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private static long ConvertToNumber(IPAddress ipAddress) |
||||||
|
{ |
||||||
|
var bytes = ipAddress.GetAddressBytes(); |
||||||
|
return bytes[0] * 256 * 256 * 256 + bytes[1] * 256 * 256 + bytes[2] * 256 + bytes[3]; |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// true表示为内网IP |
||||||
|
/// </summary> |
||||||
|
/// <param name="ipAddress"></param> |
||||||
|
/// <returns></returns> |
||||||
|
public static bool IsIntranet(string ipAddress) |
||||||
|
{ |
||||||
|
return IsIntranet(ConvertToNumber(ipAddress)); |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// true表示为内网IP |
||||||
|
/// </summary> |
||||||
|
/// <param name="ipAddress"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private static bool IsIntranet(IPAddress ipAddress) |
||||||
|
{ |
||||||
|
return IsIntranet(ConvertToNumber(ipAddress)); |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// true表示为内网IP |
||||||
|
/// </summary> |
||||||
|
/// <param name="longIP"></param> |
||||||
|
/// <returns></returns> |
||||||
|
private static bool IsIntranet(long longIP) |
||||||
|
{ |
||||||
|
return ((longIP >= ipABegin) && (longIP <= ipAEnd) || |
||||||
|
(longIP >= ipBBegin) && (longIP <= ipBEnd) || |
||||||
|
(longIP >= ipCBegin) && (longIP <= ipCEnd)); |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// 获取本机内网IP |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public static IPAddress GetLocalIntranetIP() |
||||||
|
{ |
||||||
|
return NetworkInterface |
||||||
|
.GetAllNetworkInterfaces() |
||||||
|
.Select(p => p.GetIPProperties()) |
||||||
|
.SelectMany(p => |
||||||
|
p.UnicastAddresses |
||||||
|
).FirstOrDefault(p => p.Address.AddressFamily == AddressFamily.InterNetwork |
||||||
|
&& !IPAddress.IsLoopback(p.Address) |
||||||
|
&& IsIntranet(p.Address))?.Address; |
||||||
|
} |
||||||
|
/// <summary> |
||||||
|
/// 获取本机内网IP列表 |
||||||
|
/// </summary> |
||||||
|
/// <returns></returns> |
||||||
|
public static List<IPAddress> GetLocalIntranetIPList() |
||||||
|
{ |
||||||
|
var infList =NetworkInterface.GetAllNetworkInterfaces() |
||||||
|
.Select(p => p.GetIPProperties()) |
||||||
|
.SelectMany(p => p.UnicastAddresses) |
||||||
|
.Where(p => |
||||||
|
p.Address.AddressFamily == AddressFamily.InterNetwork |
||||||
|
&& !IPAddress.IsLoopback(p.Address) |
||||||
|
&& IsIntranet(p.Address) |
||||||
|
); |
||||||
|
|
||||||
|
var result = new List<IPAddress>(); |
||||||
|
foreach (var child in infList) |
||||||
|
{ |
||||||
|
result.Add(child.Address); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
#endregion |
||||||
} |
} |
||||||
} |
} |
||||||
@ -1,7 +1,87 @@ |
|||||||
|
using System; |
||||||
|
|
||||||
namespace DotXxlJob.Core |
namespace DotXxlJob.Core |
||||||
{ |
{ |
||||||
public class Preconditions |
/// <summary> |
||||||
|
/// Utility methods to simplify checking preconditions in the code. |
||||||
|
/// </summary> |
||||||
|
internal static class Preconditions |
||||||
{ |
{ |
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="ArgumentException"/> if condition is false. |
||||||
|
/// </summary> |
||||||
|
/// <param name="condition">The condition.</param> |
||||||
|
public static void CheckArgument(bool condition) |
||||||
|
{ |
||||||
|
if (!condition) |
||||||
|
{ |
||||||
|
throw new ArgumentException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="ArgumentException"/> with given message if condition is false. |
||||||
|
/// </summary> |
||||||
|
/// <param name="condition">The condition.</param> |
||||||
|
/// <param name="errorMessage">The error message.</param> |
||||||
|
public static void CheckArgument(bool condition, string errorMessage) |
||||||
|
{ |
||||||
|
if (!condition) |
||||||
|
{ |
||||||
|
throw new ArgumentException(errorMessage); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="ArgumentNullException"/> if reference is null. |
||||||
|
/// </summary> |
||||||
|
/// <param name="reference">The reference.</param> |
||||||
|
public static T CheckNotNull<T>(T reference) |
||||||
|
{ |
||||||
|
if (reference == null) |
||||||
|
{ |
||||||
|
throw new ArgumentNullException(); |
||||||
|
} |
||||||
|
return reference; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="ArgumentNullException"/> if reference is null. |
||||||
|
/// </summary> |
||||||
|
/// <param name="reference">The reference.</param> |
||||||
|
/// <param name="paramName">The parameter name.</param> |
||||||
|
public static T CheckNotNull<T>(T reference, string paramName) |
||||||
|
{ |
||||||
|
if (reference == null) |
||||||
|
{ |
||||||
|
throw new ArgumentNullException(paramName); |
||||||
|
} |
||||||
|
return reference; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="InvalidOperationException"/> if condition is false. |
||||||
|
/// </summary> |
||||||
|
/// <param name="condition">The condition.</param> |
||||||
|
public static void CheckState(bool condition) |
||||||
|
{ |
||||||
|
if (!condition) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Throws <see cref="InvalidOperationException"/> with given message if condition is false. |
||||||
|
/// </summary> |
||||||
|
/// <param name="condition">The condition.</param> |
||||||
|
/// <param name="errorMessage">The error message.</param> |
||||||
|
public static void CheckState(bool condition, string errorMessage) |
||||||
|
{ |
||||||
|
if (!condition) |
||||||
|
{ |
||||||
|
throw new InvalidOperationException(errorMessage); |
||||||
|
} |
||||||
|
} |
||||||
} |
} |
||||||
} |
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
using System; |
||||||
|
using Xunit; |
||||||
|
|
||||||
|
namespace DotXxlJob.Core.Tests |
||||||
|
{ |
||||||
|
public class UnitTest1 |
||||||
|
{ |
||||||
|
[Fact] |
||||||
|
public void Test1() |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
using System; |
||||||
|
using Xunit; |
||||||
|
|
||||||
|
namespace Hessian.Tests |
||||||
|
{ |
||||||
|
public class UnitTest1 |
||||||
|
{ |
||||||
|
[Fact] |
||||||
|
public void Test1() |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<packages> |
||||||
|
<package id="Cake" version="0.32.1" /> |
||||||
|
</packages> |
||||||
Loading…
Reference in new issue