@@ -1,13 +1,16 @@ | |||||
| | ||||
Microsoft Visual Studio Solution File, Format Version 12.00 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
# Visual Studio 15 | # Visual Studio 15 | ||||
VisualStudioVersion = 15.0.26430.6 | |||||
VisualStudioVersion = 15.0.26430.13 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9B2AE124-6636-4DE9-83A3-70360DABD0C4}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9B2AE124-6636-4DE9-83A3-70360DABD0C4}" | ||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0}" | ||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57A8A8E5-5715-41BF-A0A6-46B819933FBC}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{57A8A8E5-5715-41BF-A0A6-46B819933FBC}" | ||||
ProjectSection(SolutionItems) = preProject | |||||
CAP.vssettings = CAP.vssettings | |||||
EndProjectSection | |||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE}" | ||||
ProjectSection(SolutionItems) = preProject | ProjectSection(SolutionItems) = preProject | ||||
@@ -18,21 +21,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9E5A7F | |||||
{82A7F48D-3B50-4B1E-B82E-3ADA8210C358} = {82A7F48D-3B50-4B1E-B82E-3ADA8210C358} | {82A7F48D-3B50-4B1E-B82E-3ADA8210C358} = {82A7F48D-3B50-4B1E-B82E-3ADA8210C358} | ||||
EndProjectSection | EndProjectSection | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cap.Consistency", "src\Cap.Consistency\Cap.Consistency.csproj", "{E8AF8611-0EA4-4B19-BC48-87C57A87DC66}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cap.Consistency.Test", "test\Cap.Consistency.Test\Cap.Consistency.Test.csproj", "{3A444CF8-1611-407F-8D32-5D0CDC3DD49D}" | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP", "src\DotNetCore.CAP\DotNetCore.CAP.csproj", "{E8AF8611-0EA4-4B19-BC48-87C57A87DC66}" | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cap.Consistency.EntityFrameworkCore", "src\Cap.Consistency.EntityFrameworkCore\Cap.Consistency.EntityFrameworkCore.csproj", "{96111249-C4C3-4DC9-A887-32D583723AB1}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cap.Consistency.EntityFrameworkCore.Test", "test\Cap.Consistency.EntityFrameworkCore.Test\Cap.Consistency.EntityFrameworkCore.Test.csproj", "{7442C942-1DDC-40E4-8F1B-654E721EAA45}" | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.EntityFrameworkCore", "src\DotNetCore.CAP.EntityFrameworkCore\DotNetCore.CAP.EntityFrameworkCore.csproj", "{96111249-C4C3-4DC9-A887-32D583723AB1}" | |||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{3A6B6931-A123-477A-9469-8B468B5385AF}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{3A6B6931-A123-477A-9469-8B468B5385AF}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Kafka", "samples\Sample.Kafka\Sample.Kafka.csproj", "{2F095ED9-5BC9-4512-9013-A47685FB2508}" | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Kafka", "samples\Sample.Kafka\Sample.Kafka.csproj", "{2F095ED9-5BC9-4512-9013-A47685FB2508}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cap.Consistency.Kafka", "src\Cap.Consistency.Kafka\Cap.Consistency.Kafka.csproj", "{C42CDE33-0878-4BA0-96F2-4CB7C8FDEAAD}" | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.Kafka", "src\DotNetCore.CAP.Kafka\DotNetCore.CAP.Kafka.csproj", "{C42CDE33-0878-4BA0-96F2-4CB7C8FDEAAD}" | |||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cap.Consistency.RabbitMQ", "src\Cap.Consistency.RabbitMQ\Cap.Consistency.RabbitMQ.csproj", "{9961B80E-0718-4280-B2A0-271B003DE26B}" | |||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetCore.CAP.RabbitMQ", "src\DotNetCore.CAP.RabbitMQ\DotNetCore.CAP.RabbitMQ.csproj", "{9961B80E-0718-4280-B2A0-271B003DE26B}" | |||||
EndProject | EndProject | ||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{10C0818D-9160-4B80-BB86-DDE925B64D43}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{10C0818D-9160-4B80-BB86-DDE925B64D43}" | ||||
ProjectSection(SolutionItems) = preProject | ProjectSection(SolutionItems) = preProject | ||||
@@ -46,6 +45,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{10C0818D | |||||
build\version.props = build\version.props | build\version.props = build\version.props | ||||
EndProjectSection | EndProjectSection | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.EntityFrameworkCore.Test", "test\DotNetCore.CAP.EntityFrameworkCore.Test\DotNetCore.CAP.EntityFrameworkCore.Test.csproj", "{69370370-9873-4D6A-965D-D1E16694047D}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.Test", "test\DotNetCore.CAP.Test\DotNetCore.CAP.Test.csproj", "{F608B509-A99B-4AC7-8227-42051DD4A578}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -56,18 +59,10 @@ Global | |||||
{E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Debug|Any CPU.Build.0 = Debug|Any CPU | {E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Release|Any CPU.ActiveCfg = Release|Any CPU | {E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Release|Any CPU.Build.0 = Release|Any CPU | {E8AF8611-0EA4-4B19-BC48-87C57A87DC66}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{3A444CF8-1611-407F-8D32-5D0CDC3DD49D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{3A444CF8-1611-407F-8D32-5D0CDC3DD49D}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{3A444CF8-1611-407F-8D32-5D0CDC3DD49D}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{3A444CF8-1611-407F-8D32-5D0CDC3DD49D}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{96111249-C4C3-4DC9-A887-32D583723AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {96111249-C4C3-4DC9-A887-32D583723AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{96111249-C4C3-4DC9-A887-32D583723AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU | {96111249-C4C3-4DC9-A887-32D583723AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{96111249-C4C3-4DC9-A887-32D583723AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU | {96111249-C4C3-4DC9-A887-32D583723AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{96111249-C4C3-4DC9-A887-32D583723AB1}.Release|Any CPU.Build.0 = Release|Any CPU | {96111249-C4C3-4DC9-A887-32D583723AB1}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{7442C942-1DDC-40E4-8F1B-654E721EAA45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{7442C942-1DDC-40E4-8F1B-654E721EAA45}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{7442C942-1DDC-40E4-8F1B-654E721EAA45}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{7442C942-1DDC-40E4-8F1B-654E721EAA45}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{2F095ED9-5BC9-4512-9013-A47685FB2508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {2F095ED9-5BC9-4512-9013-A47685FB2508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{2F095ED9-5BC9-4512-9013-A47685FB2508}.Debug|Any CPU.Build.0 = Debug|Any CPU | {2F095ED9-5BC9-4512-9013-A47685FB2508}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{2F095ED9-5BC9-4512-9013-A47685FB2508}.Release|Any CPU.ActiveCfg = Release|Any CPU | {2F095ED9-5BC9-4512-9013-A47685FB2508}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
@@ -80,6 +75,14 @@ Global | |||||
{9961B80E-0718-4280-B2A0-271B003DE26B}.Debug|Any CPU.Build.0 = Debug|Any CPU | {9961B80E-0718-4280-B2A0-271B003DE26B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{9961B80E-0718-4280-B2A0-271B003DE26B}.Release|Any CPU.ActiveCfg = Release|Any CPU | {9961B80E-0718-4280-B2A0-271B003DE26B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{9961B80E-0718-4280-B2A0-271B003DE26B}.Release|Any CPU.Build.0 = Release|Any CPU | {9961B80E-0718-4280-B2A0-271B003DE26B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{69370370-9873-4D6A-965D-D1E16694047D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{69370370-9873-4D6A-965D-D1E16694047D}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{69370370-9873-4D6A-965D-D1E16694047D}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{69370370-9873-4D6A-965D-D1E16694047D}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{F608B509-A99B-4AC7-8227-42051DD4A578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{F608B509-A99B-4AC7-8227-42051DD4A578}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{F608B509-A99B-4AC7-8227-42051DD4A578}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{F608B509-A99B-4AC7-8227-42051DD4A578}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -87,11 +90,11 @@ Global | |||||
GlobalSection(NestedProjects) = preSolution | GlobalSection(NestedProjects) = preSolution | ||||
{9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | {9E5A7F49-8E31-4A71-90CC-1DA9AEDA99EE} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | ||||
{E8AF8611-0EA4-4B19-BC48-87C57A87DC66} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {E8AF8611-0EA4-4B19-BC48-87C57A87DC66} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{3A444CF8-1611-407F-8D32-5D0CDC3DD49D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | |||||
{96111249-C4C3-4DC9-A887-32D583723AB1} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {96111249-C4C3-4DC9-A887-32D583723AB1} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{7442C942-1DDC-40E4-8F1B-654E721EAA45} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | |||||
{2F095ED9-5BC9-4512-9013-A47685FB2508} = {3A6B6931-A123-477A-9469-8B468B5385AF} | {2F095ED9-5BC9-4512-9013-A47685FB2508} = {3A6B6931-A123-477A-9469-8B468B5385AF} | ||||
{C42CDE33-0878-4BA0-96F2-4CB7C8FDEAAD} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {C42CDE33-0878-4BA0-96F2-4CB7C8FDEAAD} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{9961B80E-0718-4280-B2A0-271B003DE26B} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {9961B80E-0718-4280-B2A0-271B003DE26B} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{69370370-9873-4D6A-965D-D1E16694047D} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | |||||
{F608B509-A99B-4AC7-8227-42051DD4A578} = {C09CDAB0-6DD4-46E9-B7F3-3EF2A4741EA0} | |||||
EndGlobalSection | EndGlobalSection | ||||
EndGlobal | EndGlobal |
@@ -2,7 +2,7 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using JetBrains.Annotations; | using JetBrains.Annotations; | ||||
using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||
@@ -1,8 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency; | |||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Kafka; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Kafka; | |||||
using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||
namespace Sample.Kafka.Controllers | namespace Sample.Kafka.Controllers | ||||
@@ -10,9 +9,9 @@ namespace Sample.Kafka.Controllers | |||||
[Route("api/[controller]")] | [Route("api/[controller]")] | ||||
public class ValuesController : Controller, IConsumerService | public class ValuesController : Controller, IConsumerService | ||||
{ | { | ||||
private readonly IProducerClient _producer; | |||||
private readonly ICapProducerService _producer; | |||||
public ValuesController(IProducerClient producer) { | |||||
public ValuesController(ICapProducerService producer) { | |||||
_producer = producer; | _producer = producer; | ||||
} | } | ||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | using Microsoft.EntityFrameworkCore.Metadata; | ||||
using Microsoft.EntityFrameworkCore.Migrations; | using Microsoft.EntityFrameworkCore.Migrations; | ||||
using Sample.Kafka; | using Sample.Kafka; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Sample.Kafka.Migrations | namespace Sample.Kafka.Migrations | ||||
{ | { | ||||
@@ -18,7 +18,7 @@ namespace Sample.Kafka.Migrations | |||||
.HasAnnotation("ProductVersion", "1.1.2") | .HasAnnotation("ProductVersion", "1.1.2") | ||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | ||||
modelBuilder.Entity("Cap.Consistency.Infrastructure.ConsistencyMessage", b => | |||||
modelBuilder.Entity("DotNetCore.CAP.Infrastructure.ConsistencyMessage", b => | |||||
{ | { | ||||
b.Property<string>("Id") | b.Property<string>("Id") | ||||
.ValueGeneratedOnAdd(); | .ValueGeneratedOnAdd(); | ||||
@@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore.Metadata; | using Microsoft.EntityFrameworkCore.Metadata; | ||||
using Microsoft.EntityFrameworkCore.Migrations; | using Microsoft.EntityFrameworkCore.Migrations; | ||||
using Sample.Kafka; | using Sample.Kafka; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Sample.Kafka.Migrations | namespace Sample.Kafka.Migrations | ||||
{ | { | ||||
@@ -17,7 +17,7 @@ namespace Sample.Kafka.Migrations | |||||
.HasAnnotation("ProductVersion", "1.1.2") | .HasAnnotation("ProductVersion", "1.1.2") | ||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); | ||||
modelBuilder.Entity("Cap.Consistency.Infrastructure.ConsistencyMessage", b => | |||||
modelBuilder.Entity("DotNetCore.CAP.Infrastructure.ConsistencyMessage", b => | |||||
{ | { | ||||
b.Property<string>("Id") | b.Property<string>("Id") | ||||
.ValueGeneratedOnAdd(); | .ValueGeneratedOnAdd(); | ||||
@@ -23,10 +23,10 @@ | |||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> | <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\..\src\Cap.Consistency.EntityFrameworkCore\Cap.Consistency.EntityFrameworkCore.csproj" /> | |||||
<ProjectReference Include="..\..\src\Cap.Consistency.Kafka\Cap.Consistency.Kafka.csproj" /> | |||||
<ProjectReference Include="..\..\src\Cap.Consistency.RabbitMQ\Cap.Consistency.RabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\src\Cap.Consistency\Cap.Consistency.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.EntityFrameworkCore\DotNetCore.CAP.EntityFrameworkCore.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.Kafka\DotNetCore.CAP.Kafka.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.RabbitMQ\DotNetCore.CAP.RabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
</Project> | </Project> |
@@ -1,25 +0,0 @@ | |||||
using Cap.Consistency; | |||||
using Cap.Consistency.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore; | |||||
namespace Microsoft.Extensions.DependencyInjection | |||||
{ | |||||
/// <summary> | |||||
/// Contains extension methods to <see cref="ConsistencyBuilder"/> for adding entity framework stores. | |||||
/// </summary> | |||||
public static class ConsistencyEntityFrameworkBuilderExtensions | |||||
{ | |||||
/// <summary> | |||||
/// Adds an Entity Framework implementation of message stores. | |||||
/// </summary> | |||||
/// <typeparam name="TContext">The Entity Framework database context to use.</typeparam> | |||||
/// <param name="services">The <see cref="ConsistencyBuilder"/> instance this method extends.</param> | |||||
/// <returns>The <see cref="ConsistencyBuilder"/> instance this method extends.</returns> | |||||
public static ConsistencyBuilder AddEntityFrameworkStores<TContext>(this ConsistencyBuilder builder) | |||||
where TContext : DbContext { | |||||
builder.Services.AddScoped<IConsistencyMessageStore, ConsistencyMessageStore<TContext>>(); | |||||
return builder; | |||||
} | |||||
} | |||||
} |
@@ -1,16 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard1.6</TargetFramework> | |||||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Confluent.Kafka" Version="0.9.5" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\Cap.Consistency\Cap.Consistency.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,15 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
namespace Cap.Consistency.RabbitMQ | |||||
{ | |||||
public class RabbitMQTopicAttribute : TopicAttribute | |||||
{ | |||||
public RabbitMQTopicAttribute(string routingKey) : base(routingKey) { | |||||
} | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Cap.Consistency.Abstractions | |||||
{ | |||||
public interface IConsumerInvoker | |||||
{ | |||||
Task InvokeAsync(); | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Cap.Consistency.Abstractions.ModelBinding | |||||
{ | |||||
public interface IModelBinder | |||||
{ | |||||
Task BindModelAsync(ModelBindingContext bindingContext); | |||||
} | |||||
} |
@@ -1,10 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Consumer | |||||
{ | |||||
public interface IConsumerService | |||||
{ | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Cap.Consistency | |||||
{ | |||||
public interface IProcessingServer : IDisposable | |||||
{ | |||||
void Start(); | |||||
} | |||||
} |
@@ -1,13 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using Cap.Consistency.Infrastructure; | |||||
namespace Cap.Consistency | |||||
{ | |||||
public interface IProducerClient | |||||
{ | |||||
Task SendAsync(string topic, string content); | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
namespace Cap.Consistency.Infrastructure | |||||
{ | |||||
public interface IConsumerInvokerFactory | |||||
{ | |||||
IConsumerInvoker CreateInvoker(ConsumerContext actionContext); | |||||
} | |||||
} |
@@ -1,12 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
namespace Cap.Consistency.Job | |||||
{ | |||||
public interface IJobProcessor | |||||
{ | |||||
Task ProcessAsync(ProcessingContext context); | |||||
} | |||||
} |
@@ -1,55 +0,0 @@ | |||||
using System; | |||||
using Cap.Consistency; | |||||
using Cap.Consistency.Job; | |||||
namespace Microsoft.Extensions.DependencyInjection | |||||
{ | |||||
/// <summary> | |||||
/// Used to verify Consistency service was called on a ServiceCollection | |||||
/// </summary> | |||||
public class ConsistencyMarkerService { } | |||||
public class ConsistencyBuilder | |||||
{ | |||||
public ConsistencyBuilder(IServiceCollection services) { | |||||
Services = services; | |||||
} | |||||
public IServiceCollection Services { get; private set; } | |||||
private ConsistencyBuilder AddScoped(Type serviceType, Type concreteType) { | |||||
Services.AddScoped(serviceType, concreteType); | |||||
return this; | |||||
} | |||||
private ConsistencyBuilder AddSingleton<TService, TImplementation>() | |||||
where TService : class | |||||
where TImplementation : class, TService { | |||||
Services.AddSingleton<TService, TImplementation>(); | |||||
return this; | |||||
} | |||||
/// <summary> | |||||
/// Adds an <see cref="IConsistencyMessageStore"/> . | |||||
/// </summary> | |||||
/// <typeparam name="T">The type for the <see cref="IConsistencyMessageStore"/> to add. </typeparam> | |||||
/// <returns>The current <see cref="ConsistencyBuilder"/> instance.</returns> | |||||
public virtual ConsistencyBuilder AddMessageStore<T>() | |||||
where T : class, IConsistencyMessageStore { | |||||
return AddScoped(typeof(IConsistencyMessageStore), typeof(T)); | |||||
} | |||||
public virtual ConsistencyBuilder AddJobs<T>() | |||||
where T : class, IJob { | |||||
return AddSingleton<IJob, T>(); | |||||
} | |||||
public virtual ConsistencyBuilder AddProducerClient<T>() | |||||
where T:class, IProducerClient { | |||||
return AddScoped(typeof(IProducerClient), typeof(T)); | |||||
} | |||||
} | |||||
} |
@@ -1,8 +1,7 @@ | |||||
using System; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||
namespace Cap.Consistency.EntityFrameworkCore | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Base class for the Entity Framework database context used for consistency. | /// Base class for the Entity Framework database context used for consistency. | ||||
@@ -33,8 +32,10 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <param name="modelBuilder"> | /// <param name="modelBuilder"> | ||||
/// The builder being used to construct the model for this context. | /// The builder being used to construct the model for this context. | ||||
/// </param> | /// </param> | ||||
protected override void OnModelCreating(ModelBuilder modelBuilder) { | |||||
modelBuilder.Entity<ConsistencyMessage>(b => { | |||||
protected override void OnModelCreating(ModelBuilder modelBuilder) | |||||
{ | |||||
modelBuilder.Entity<ConsistencyMessage>(b => | |||||
{ | |||||
b.HasKey(m => m.Id); | b.HasKey(m => m.Id); | ||||
b.ToTable("ConsistencyMessages"); | b.ToTable("ConsistencyMessages"); | ||||
}); | }); |
@@ -0,0 +1,26 @@ | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.EntityFrameworkCore; | |||||
using Microsoft.EntityFrameworkCore; | |||||
namespace Microsoft.Extensions.DependencyInjection | |||||
{ | |||||
/// <summary> | |||||
/// Contains extension methods to <see cref="CapBuilder"/> for adding entity framework stores. | |||||
/// </summary> | |||||
public static class ConsistencyEntityFrameworkBuilderExtensions | |||||
{ | |||||
/// <summary> | |||||
/// Adds an Entity Framework implementation of message stores. | |||||
/// </summary> | |||||
/// <typeparam name="TContext">The Entity Framework database context to use.</typeparam> | |||||
/// <param name="services">The <see cref="CapBuilder"/> instance this method extends.</param> | |||||
/// <returns>The <see cref="CapBuilder"/> instance this method extends.</returns> | |||||
public static CapBuilder AddEntityFrameworkStores<TContext>(this CapBuilder builder) | |||||
where TContext : DbContext | |||||
{ | |||||
builder.Services.AddScoped<ICapMessageStore, ConsistencyMessageStore<TContext>>(); | |||||
return builder; | |||||
} | |||||
} | |||||
} |
@@ -2,10 +2,10 @@ | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||
namespace Cap.Consistency.EntityFrameworkCore | |||||
namespace DotNetCore.CAP.EntityFrameworkCore | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents a new instance of a persistence store for the specified message types. | /// Represents a new instance of a persistence store for the specified message types. | ||||
@@ -13,7 +13,7 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <typeparam name="ConsistencyMessage">The type representing a message.</typeparam> | /// <typeparam name="ConsistencyMessage">The type representing a message.</typeparam> | ||||
/// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam> | /// <typeparam name="TContext">The type of the data context class used to access the store.</typeparam> | ||||
/// <typeparam name="TKey">The type of the primary key for a message.</typeparam> | /// <typeparam name="TKey">The type of the primary key for a message.</typeparam> | ||||
public class ConsistencyMessageStore<TContext> : IConsistencyMessageStore where TContext : DbContext | |||||
public class ConsistencyMessageStore<TContext> : ICapMessageStore where TContext : DbContext | |||||
{ | { | ||||
private bool _disposed; | private bool _disposed; | ||||
@@ -21,8 +21,10 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// Constructs a new instance of <see cref="ConsistencyMessageStore{ConsistencyMessage, TContext, TKey}"/>. | /// Constructs a new instance of <see cref="ConsistencyMessageStore{ConsistencyMessage, TContext, TKey}"/>. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="context">The <see cref="DbContext"/>.</param> | /// <param name="context">The <see cref="DbContext"/>.</param> | ||||
public ConsistencyMessageStore(TContext context) { | |||||
if (context == null) { | |||||
public ConsistencyMessageStore(TContext context) | |||||
{ | |||||
if (context == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(context)); | throw new ArgumentNullException(nameof(context)); | ||||
} | } | ||||
Context = context; | Context = context; | ||||
@@ -38,10 +40,12 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <param name="message">The message to create.</param> | /// <param name="message">The message to create.</param> | ||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | ||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the creation operation.</returns> | /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the creation operation.</returns> | ||||
public async virtual Task<OperateResult> CreateAsync(ConsistencyMessage message, CancellationToken cancellationToken) { | |||||
public async virtual Task<OperateResult> CreateAsync(ConsistencyMessage message, CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
if (message == null) { | |||||
if (message == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(message)); | throw new ArgumentNullException(nameof(message)); | ||||
} | } | ||||
Context.Add(message); | Context.Add(message); | ||||
@@ -55,18 +59,22 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <param name="message">The message to delete.</param> | /// <param name="message">The message to delete.</param> | ||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | ||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the update operation.</returns> | /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the update operation.</returns> | ||||
public async virtual Task<OperateResult> DeleteAsync(ConsistencyMessage message, CancellationToken cancellationToken) { | |||||
public async virtual Task<OperateResult> DeleteAsync(ConsistencyMessage message, CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
if (message == null) { | |||||
if (message == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(message)); | throw new ArgumentNullException(nameof(message)); | ||||
} | } | ||||
Context.Remove(message); | Context.Remove(message); | ||||
try { | |||||
try | |||||
{ | |||||
await SaveChanges(cancellationToken); | await SaveChanges(cancellationToken); | ||||
} | } | ||||
catch (DbUpdateConcurrencyException ex) { | |||||
catch (DbUpdateConcurrencyException ex) | |||||
{ | |||||
return OperateResult.Failed(new OperateError() { Code = "DbUpdateConcurrencyException", Description = ex.Message }); | return OperateResult.Failed(new OperateError() { Code = "DbUpdateConcurrencyException", Description = ex.Message }); | ||||
} | } | ||||
return OperateResult.Success; | return OperateResult.Success; | ||||
@@ -80,7 +88,8 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <returns> | /// <returns> | ||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the message matching the specified <paramref name="messageId"/> if it exists. | /// The <see cref="Task"/> that represents the asynchronous operation, containing the message matching the specified <paramref name="messageId"/> if it exists. | ||||
/// </returns> | /// </returns> | ||||
public virtual Task<ConsistencyMessage> FindByIdAsync(string messageId, CancellationToken cancellationToken) { | |||||
public virtual Task<ConsistencyMessage> FindByIdAsync(string messageId, CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
return MessageSet.FindAsync(new object[] { messageId }, cancellationToken); | return MessageSet.FindAsync(new object[] { messageId }, cancellationToken); | ||||
@@ -92,10 +101,12 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <param name="message">The message whose identifier should be retrieved.</param> | /// <param name="message">The message whose identifier should be retrieved.</param> | ||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | ||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the identifier for the specified <paramref name="message"/>.</returns> | /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the identifier for the specified <paramref name="message"/>.</returns> | ||||
public Task<string> GeConsistencyMessageIdAsync(ConsistencyMessage message, CancellationToken cancellationToken) { | |||||
public Task<string> GeConsistencyMessageIdAsync(ConsistencyMessage message, CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
if (message == null) { | |||||
if (message == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(message)); | throw new ArgumentNullException(nameof(message)); | ||||
} | } | ||||
return Task.FromResult(message.Id); | return Task.FromResult(message.Id); | ||||
@@ -107,26 +118,31 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <param name="message">The message to update.</param> | /// <param name="message">The message to update.</param> | ||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | ||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the update operation.</returns> | /// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="OperateResult"/> of the update operation.</returns> | ||||
public async virtual Task<OperateResult> UpdateAsync(ConsistencyMessage message, CancellationToken cancellationToken) { | |||||
public async virtual Task<OperateResult> UpdateAsync(ConsistencyMessage message, CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
ThrowIfDisposed(); | ThrowIfDisposed(); | ||||
if (message == null) { | |||||
if (message == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(message)); | throw new ArgumentNullException(nameof(message)); | ||||
} | } | ||||
Context.Attach(message); | Context.Attach(message); | ||||
message.UpdateTime = DateTime.Now; | message.UpdateTime = DateTime.Now; | ||||
Context.Update(message); | Context.Update(message); | ||||
try { | |||||
try | |||||
{ | |||||
await SaveChanges(cancellationToken); | await SaveChanges(cancellationToken); | ||||
} | } | ||||
catch (DbUpdateConcurrencyException ex) { | |||||
catch (DbUpdateConcurrencyException ex) | |||||
{ | |||||
return OperateResult.Failed(new OperateError() { Code = "DbUpdateConcurrencyException", Description = ex.Message }); | return OperateResult.Failed(new OperateError() { Code = "DbUpdateConcurrencyException", Description = ex.Message }); | ||||
} | } | ||||
return OperateResult.Success; | return OperateResult.Success; | ||||
} | } | ||||
public Task<ConsistencyMessage> GetFirstEnqueuedMessageAsync(CancellationToken cancellationToken) { | |||||
public Task<ConsistencyMessage> GetFirstEnqueuedMessageAsync(CancellationToken cancellationToken) | |||||
{ | |||||
cancellationToken.ThrowIfCancellationRequested(); | cancellationToken.ThrowIfCancellationRequested(); | ||||
return MessageSet.AsNoTracking().Where(x => x.Status == MessageStatus.WaitForSend).FirstOrDefaultAsync(cancellationToken); | return MessageSet.AsNoTracking().Where(x => x.Status == MessageStatus.WaitForSend).FirstOrDefaultAsync(cancellationToken); | ||||
} | } | ||||
@@ -155,15 +171,18 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <summary>Saves the current store.</summary> | /// <summary>Saves the current store.</summary> | ||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | /// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param> | ||||
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> | /// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns> | ||||
protected Task SaveChanges(CancellationToken cancellationToken) { | |||||
protected Task SaveChanges(CancellationToken cancellationToken) | |||||
{ | |||||
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask; | return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.CompletedTask; | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Throws if this class has been disposed. | /// Throws if this class has been disposed. | ||||
/// </summary> | /// </summary> | ||||
protected void ThrowIfDisposed() { | |||||
if (_disposed) { | |||||
protected void ThrowIfDisposed() | |||||
{ | |||||
if (_disposed) | |||||
{ | |||||
throw new ObjectDisposedException(GetType().Name); | throw new ObjectDisposedException(GetType().Name); | ||||
} | } | ||||
} | } | ||||
@@ -171,7 +190,8 @@ namespace Cap.Consistency.EntityFrameworkCore | |||||
/// <summary> | /// <summary> | ||||
/// Dispose the store | /// Dispose the store | ||||
/// </summary> | /// </summary> | ||||
public void Dispose() { | |||||
public void Dispose() | |||||
{ | |||||
_disposed = true; | _disposed = true; | ||||
} | } | ||||
} | } |
@@ -2,8 +2,8 @@ | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>netstandard1.6</TargetFramework> | <TargetFramework>netstandard1.6</TargetFramework> | ||||
<AssemblyName>Cap.Consistency.EntityFrameworkCore</AssemblyName> | |||||
<PackageId>Cap.Consistency.EntityFrameworkCore</PackageId> | |||||
<AssemblyName>DotNetCore.CAP.EntityFrameworkCore</AssemblyName> | |||||
<PackageId>DotNetCore.CAP.EntityFrameworkCore</PackageId> | |||||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | <NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | ||||
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> | <PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> | ||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> | <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> | ||||
@@ -11,14 +11,14 @@ | |||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> | <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | |||||
<ProjectReference Include="..\Cap.Consistency\Cap.Consistency.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | <ItemGroup> | ||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" /> | <PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" /> | ||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" /> | <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" /> | ||||
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" /> | <PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<ProjectReference Include="..\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | |||||
</Project> | </Project> |
@@ -0,0 +1,22 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netstandard1.6</TargetFramework> | |||||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | |||||
<AssemblyName>DotNetCore.CAP.Kafka</AssemblyName> | |||||
<PackageId>DotNetCore.CAP.Kafka</PackageId> | |||||
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> | |||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> | |||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> | |||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Confluent.Kafka" Version="0.9.5" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -4,19 +4,18 @@ using System.Diagnostics; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using Cap.Consistency.Job; | |||||
using Confluent.Kafka; | using Confluent.Kafka; | ||||
using Confluent.Kafka.Serialization; | using Confluent.Kafka.Serialization; | ||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Job; | |||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace Cap.Consistency.Kafka | |||||
namespace DotNetCore.CAP.Kafka | |||||
{ | { | ||||
public class KafkaJobProcessor : IJobProcessor | public class KafkaJobProcessor : IJobProcessor | ||||
{ | { | ||||
private readonly ConsistencyOptions _options; | private readonly ConsistencyOptions _options; | ||||
private readonly CancellationTokenSource _cts; | private readonly CancellationTokenSource _cts; | ||||
@@ -29,8 +28,8 @@ namespace Cap.Consistency.Kafka | |||||
public KafkaJobProcessor( | public KafkaJobProcessor( | ||||
IOptions<ConsistencyOptions> options, | IOptions<ConsistencyOptions> options, | ||||
ILogger<KafkaJobProcessor> logger, | ILogger<KafkaJobProcessor> logger, | ||||
IServiceProvider provider) { | |||||
IServiceProvider provider) | |||||
{ | |||||
_logger = logger; | _logger = logger; | ||||
_options = options.Value; | _options = options.Value; | ||||
_provider = provider; | _provider = provider; | ||||
@@ -40,45 +39,53 @@ namespace Cap.Consistency.Kafka | |||||
public bool Waiting { get; private set; } | public bool Waiting { get; private set; } | ||||
public Task ProcessAsync(ProcessingContext context) { | |||||
public Task ProcessAsync(ProcessingContext context) | |||||
{ | |||||
if (context == null) throw new ArgumentNullException(nameof(context)); | if (context == null) throw new ArgumentNullException(nameof(context)); | ||||
context.ThrowIfStopping(); | context.ThrowIfStopping(); | ||||
return ProcessCoreAsync(context); | return ProcessCoreAsync(context); | ||||
} | } | ||||
public async Task ProcessCoreAsync(ProcessingContext context) { | |||||
try { | |||||
public async Task ProcessCoreAsync(ProcessingContext context) | |||||
{ | |||||
try | |||||
{ | |||||
var worked = await Step(context); | var worked = await Step(context); | ||||
context.ThrowIfStopping(); | context.ThrowIfStopping(); | ||||
Waiting = true; | Waiting = true; | ||||
if (!worked) { | |||||
if (!worked) | |||||
{ | |||||
var token = GetTokenToWaitOn(context); | var token = GetTokenToWaitOn(context); | ||||
} | |||||
} | |||||
await WaitHandleEx.WaitAnyAsync(WaitHandleEx.PulseEvent, context.CancellationToken.WaitHandle, _pollingDelay); | await WaitHandleEx.WaitAnyAsync(WaitHandleEx.PulseEvent, context.CancellationToken.WaitHandle, _pollingDelay); | ||||
} | } | ||||
finally { | |||||
finally | |||||
{ | |||||
Waiting = false; | Waiting = false; | ||||
} | } | ||||
} | } | ||||
protected virtual CancellationToken GetTokenToWaitOn(ProcessingContext context) { | |||||
protected virtual CancellationToken GetTokenToWaitOn(ProcessingContext context) | |||||
{ | |||||
return context.CancellationToken; | return context.CancellationToken; | ||||
} | } | ||||
private async Task<bool> Step(ProcessingContext context) { | |||||
using (var scopedContext = context.CreateScope()) { | |||||
private async Task<bool> Step(ProcessingContext context) | |||||
{ | |||||
using (var scopedContext = context.CreateScope()) | |||||
{ | |||||
var provider = scopedContext.Provider; | var provider = scopedContext.Provider; | ||||
var messageStore = provider.GetRequiredService<IConsistencyMessageStore>(); | |||||
try { | |||||
var messageStore = provider.GetRequiredService<ICapMessageStore>(); | |||||
try | |||||
{ | |||||
var message = await messageStore.GetFirstEnqueuedMessageAsync(_cts.Token); | var message = await messageStore.GetFirstEnqueuedMessageAsync(_cts.Token); | ||||
if (message != null) { | |||||
if (message != null) | |||||
{ | |||||
var sp = Stopwatch.StartNew(); | var sp = Stopwatch.StartNew(); | ||||
message.Status = MessageStatus.Processing; | message.Status = MessageStatus.Processing; | ||||
await messageStore.UpdateAsync(message, _cts.Token); | await messageStore.UpdateAsync(message, _cts.Token); | ||||
@@ -87,10 +94,12 @@ namespace Cap.Consistency.Kafka | |||||
sp.Stop(); | sp.Stop(); | ||||
if (!jobResult) { | |||||
if (!jobResult) | |||||
{ | |||||
_logger.JobFailed(new Exception("topic send failed")); | _logger.JobFailed(new Exception("topic send failed")); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
message.Status = MessageStatus.Successed; | message.Status = MessageStatus.Successed; | ||||
await messageStore.UpdateAsync(message, _cts.Token); | await messageStore.UpdateAsync(message, _cts.Token); | ||||
//await messageStore.DeleteAsync(message, _cts.Token); | //await messageStore.DeleteAsync(message, _cts.Token); | ||||
@@ -98,31 +107,37 @@ namespace Cap.Consistency.Kafka | |||||
} | } | ||||
} | } | ||||
} | } | ||||
catch (Exception ex) { | |||||
catch (Exception ) | |||||
{ | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
private bool ExecuteJob(string topic, string content) { | |||||
try { | |||||
private bool ExecuteJob(string topic, string content) | |||||
{ | |||||
try | |||||
{ | |||||
var config = new Dictionary<string, object> { { "bootstrap.servers", _options.BrokerUrlList } }; | var config = new Dictionary<string, object> { { "bootstrap.servers", _options.BrokerUrlList } }; | ||||
using (var producer = new Producer<Null, string>(config, null, new StringSerializer(Encoding.UTF8))) { | |||||
using (var producer = new Producer<Null, string>(config, null, new StringSerializer(Encoding.UTF8))) | |||||
{ | |||||
var message = producer.ProduceAsync(topic, null, content).Result; | var message = producer.ProduceAsync(topic, null, content).Result; | ||||
if (message.Error.Code == ErrorCode.NoError) { | |||||
if (message.Error.Code == ErrorCode.NoError) | |||||
{ | |||||
return true; | return true; | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
catch (Exception ex) { | |||||
catch (Exception ex) | |||||
{ | |||||
_logger.ExceptionOccuredWhileExecutingJob(topic, ex); | _logger.ExceptionOccuredWhileExecutingJob(topic, ex); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,12 +1,11 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Infrastructure; | |||||
using Confluent.Kafka; | using Confluent.Kafka; | ||||
using Confluent.Kafka.Serialization; | using Confluent.Kafka.Serialization; | ||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Cap.Consistency.Kafka | |||||
namespace DotNetCore.CAP.Kafka | |||||
{ | { | ||||
public class KafkaConsumerClient : IConsumerClient | public class KafkaConsumerClient : IConsumerClient | ||||
{ | { | ||||
@@ -19,38 +18,46 @@ namespace Cap.Consistency.Kafka | |||||
public IDeserializer<string> StringDeserializer { get; set; } | public IDeserializer<string> StringDeserializer { get; set; } | ||||
public KafkaConsumerClient(string groupId, string bootstrapServers) { | |||||
public KafkaConsumerClient(string groupId, string bootstrapServers) | |||||
{ | |||||
_groupId = groupId; | _groupId = groupId; | ||||
_bootstrapServers = bootstrapServers; | _bootstrapServers = bootstrapServers; | ||||
StringDeserializer = new StringDeserializer(Encoding.UTF8); | StringDeserializer = new StringDeserializer(Encoding.UTF8); | ||||
} | } | ||||
public void Subscribe(string topic) { | |||||
public void Subscribe(string topic) | |||||
{ | |||||
Subscribe(topic, 0); | Subscribe(topic, 0); | ||||
} | } | ||||
public void Subscribe(string topicName, int partition) { | |||||
if (_consumerClient == null) { | |||||
public void Subscribe(string topicName, int partition) | |||||
{ | |||||
if (_consumerClient == null) | |||||
{ | |||||
InitKafkaClient(); | InitKafkaClient(); | ||||
} | } | ||||
_consumerClient.Assignment.Add(new TopicPartition(topicName, partition)); | _consumerClient.Assignment.Add(new TopicPartition(topicName, partition)); | ||||
_consumerClient.Subscribe(topicName); | _consumerClient.Subscribe(topicName); | ||||
} | } | ||||
public void Listening(TimeSpan timeout) { | |||||
while (true) { | |||||
public void Listening(TimeSpan timeout) | |||||
{ | |||||
while (true) | |||||
{ | |||||
_consumerClient.Poll(timeout); | _consumerClient.Poll(timeout); | ||||
} | } | ||||
} | } | ||||
public void Dispose() { | |||||
public void Dispose() | |||||
{ | |||||
_consumerClient.Dispose(); | _consumerClient.Dispose(); | ||||
} | } | ||||
#region private methods | #region private methods | ||||
private void InitKafkaClient() { | |||||
private void InitKafkaClient() | |||||
{ | |||||
var config = new Dictionary<string, object>{ | var config = new Dictionary<string, object>{ | ||||
{ "group.id", _groupId }, | { "group.id", _groupId }, | ||||
{ "bootstrap.servers", _bootstrapServers } | { "bootstrap.servers", _bootstrapServers } | ||||
@@ -60,8 +67,10 @@ namespace Cap.Consistency.Kafka | |||||
_consumerClient.OnMessage += ConsumerClient_OnMessage; | _consumerClient.OnMessage += ConsumerClient_OnMessage; | ||||
} | } | ||||
private void ConsumerClient_OnMessage(object sender, Message<Null, string> e) { | |||||
var message = new DeliverMessage { | |||||
private void ConsumerClient_OnMessage(object sender, Message<Null, string> e) | |||||
{ | |||||
var message = new DeliverMessage | |||||
{ | |||||
MessageKey = e.Topic, | MessageKey = e.Topic, | ||||
Value = e.Value, | Value = e.Value, | ||||
Body = Encoding.UTF8.GetBytes(e.Value) | Body = Encoding.UTF8.GetBytes(e.Value) |
@@ -1,10 +1,9 @@ | |||||
using Cap.Consistency.Consumer; | |||||
namespace Cap.Consistency.Kafka | |||||
namespace DotNetCore.CAP.Kafka | |||||
{ | { | ||||
public class KafkaConsumerClientFactory : IConsumerClientFactory | public class KafkaConsumerClientFactory : IConsumerClientFactory | ||||
{ | { | ||||
public IConsumerClient Create(string groupId, string clientHostAddress) { | |||||
public IConsumerClient Create(string groupId, string clientHostAddress) | |||||
{ | |||||
return new KafkaConsumerClient(groupId, clientHostAddress); | return new KafkaConsumerClient(groupId, clientHostAddress); | ||||
} | } | ||||
} | } |
@@ -1,6 +1,6 @@ | |||||
using Cap.Consistency.Abstractions; | |||||
using DotNetCore.CAP.Abstractions; | |||||
namespace Cap.Consistency.Kafka | |||||
namespace DotNetCore.CAP.Kafka | |||||
{ | { | ||||
public class KafkaTopicAttribute : TopicAttribute | public class KafkaTopicAttribute : TopicAttribute | ||||
{ | { |
@@ -1,9 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency.Kafka | |||||
namespace DotNetCore.CAP.Kafka | |||||
{ | { | ||||
internal static class LoggerExtensions | internal static class LoggerExtensions | ||||
{ | { | ||||
@@ -20,7 +18,8 @@ namespace Cap.Consistency.Kafka | |||||
private static Action<ILogger, int, Exception> _jobCouldNotBeLoaded; | private static Action<ILogger, int, Exception> _jobCouldNotBeLoaded; | ||||
private static Action<ILogger, string, Exception> _exceptionOccuredWhileExecutingJob; | private static Action<ILogger, string, Exception> _exceptionOccuredWhileExecutingJob; | ||||
static LoggerExtensions() { | |||||
static LoggerExtensions() | |||||
{ | |||||
_collectingExpiredEntities = LoggerMessage.Define( | _collectingExpiredEntities = LoggerMessage.Define( | ||||
LogLevel.Debug, | LogLevel.Debug, | ||||
1, | 1, | ||||
@@ -73,45 +72,54 @@ namespace Cap.Consistency.Kafka | |||||
"Requeuing for another retry."); | "Requeuing for another retry."); | ||||
} | } | ||||
public static void CollectingExpiredEntities(this ILogger logger) { | |||||
public static void CollectingExpiredEntities(this ILogger logger) | |||||
{ | |||||
_collectingExpiredEntities(logger, null); | _collectingExpiredEntities(logger, null); | ||||
} | } | ||||
public static void Installing(this ILogger logger) { | |||||
public static void Installing(this ILogger logger) | |||||
{ | |||||
_installing(logger, null); | _installing(logger, null); | ||||
} | } | ||||
public static void InstallingError(this ILogger logger, Exception ex) { | |||||
public static void InstallingError(this ILogger logger, Exception ex) | |||||
{ | |||||
_installingError(logger, ex); | _installingError(logger, ex); | ||||
} | } | ||||
public static void InstallingSuccess(this ILogger logger) { | |||||
public static void InstallingSuccess(this ILogger logger) | |||||
{ | |||||
_installingSuccess(logger, null); | _installingSuccess(logger, null); | ||||
} | } | ||||
public static void JobFailed(this ILogger logger, Exception ex) { | |||||
public static void JobFailed(this ILogger logger, Exception ex) | |||||
{ | |||||
_jobFailed(logger, ex); | _jobFailed(logger, ex); | ||||
} | } | ||||
public static void JobFailedWillRetry(this ILogger logger, Exception ex) { | |||||
public static void JobFailedWillRetry(this ILogger logger, Exception ex) | |||||
{ | |||||
_jobFailedWillRetry(logger, ex); | _jobFailedWillRetry(logger, ex); | ||||
} | } | ||||
public static void JobRetrying(this ILogger logger, int retries) { | |||||
public static void JobRetrying(this ILogger logger, int retries) | |||||
{ | |||||
_jobRetrying(logger, retries, null); | _jobRetrying(logger, retries, null); | ||||
} | } | ||||
public static void JobExecuted(this ILogger logger, double seconds) { | |||||
public static void JobExecuted(this ILogger logger, double seconds) | |||||
{ | |||||
_jobExecuted(logger, seconds, null); | _jobExecuted(logger, seconds, null); | ||||
} | } | ||||
public static void JobCouldNotBeLoaded(this ILogger logger, int jobId, Exception ex) { | |||||
public static void JobCouldNotBeLoaded(this ILogger logger, int jobId, Exception ex) | |||||
{ | |||||
_jobCouldNotBeLoaded(logger, jobId, ex); | _jobCouldNotBeLoaded(logger, jobId, ex); | ||||
} | } | ||||
public static void ExceptionOccuredWhileExecutingJob(this ILogger logger, string jobId, Exception ex) { | |||||
public static void ExceptionOccuredWhileExecutingJob(this ILogger logger, string jobId, Exception ex) | |||||
{ | |||||
_exceptionOccuredWhileExecutingJob(logger, jobId, ex); | _exceptionOccuredWhileExecutingJob(logger, jobId, ex); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,13 +1,13 @@ | |||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Job; | |||||
using Cap.Consistency.Kafka; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Job; | |||||
using DotNetCore.CAP.Kafka; | |||||
namespace Microsoft.Extensions.DependencyInjection | namespace Microsoft.Extensions.DependencyInjection | ||||
{ | { | ||||
public static class ConsistencyBuilderExtensions | public static class ConsistencyBuilderExtensions | ||||
{ | { | ||||
public static ConsistencyBuilder AddKafka(this ConsistencyBuilder builder) { | |||||
public static CapBuilder AddKafka(this CapBuilder builder) | |||||
{ | |||||
builder.Services.AddSingleton<IConsumerClientFactory, KafkaConsumerClientFactory>(); | builder.Services.AddSingleton<IConsumerClientFactory, KafkaConsumerClientFactory>(); | ||||
builder.Services.AddTransient<IJobProcessor, KafkaJobProcessor>(); | builder.Services.AddTransient<IJobProcessor, KafkaJobProcessor>(); |
@@ -10,7 +10,7 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\Cap.Consistency\Cap.Consistency.csproj" /> | |||||
<ProjectReference Include="..\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> |
@@ -1,11 +1,10 @@ | |||||
using System; | using System; | ||||
using System.Text; | using System.Text; | ||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using RabbitMQ.Client; | using RabbitMQ.Client; | ||||
using RabbitMQ.Client.Events; | using RabbitMQ.Client.Events; | ||||
namespace Cap.Consistency.RabbitMQ | |||||
namespace DotNetCore.CAP.RabbitMQ | |||||
{ | { | ||||
public class RabbitMQConsumerClient : IConsumerClient | public class RabbitMQConsumerClient : IConsumerClient | ||||
{ | { | ||||
@@ -22,14 +21,16 @@ namespace Cap.Consistency.RabbitMQ | |||||
public event EventHandler<DeliverMessage> MessageReceieved; | public event EventHandler<DeliverMessage> MessageReceieved; | ||||
public RabbitMQConsumerClient(string exchange, string hostName) { | |||||
public RabbitMQConsumerClient(string exchange, string hostName) | |||||
{ | |||||
_exchange = exchange; | _exchange = exchange; | ||||
_hostName = hostName; | _hostName = hostName; | ||||
InitClient(); | InitClient(); | ||||
} | } | ||||
private void InitClient() { | |||||
private void InitClient() | |||||
{ | |||||
_connectionFactory = new ConnectionFactory { HostName = _hostName }; | _connectionFactory = new ConnectionFactory { HostName = _hostName }; | ||||
_connection = _connectionFactory.CreateConnection(); | _connection = _connectionFactory.CreateConnection(); | ||||
_channel = _connection.CreateModel(); | _channel = _connection.CreateModel(); | ||||
@@ -37,7 +38,8 @@ namespace Cap.Consistency.RabbitMQ | |||||
_queueName = _channel.QueueDeclare().QueueName; | _queueName = _channel.QueueDeclare().QueueName; | ||||
} | } | ||||
public void Listening(TimeSpan timeout) { | |||||
public void Listening(TimeSpan timeout) | |||||
{ | |||||
// Task.Delay(timeout).Wait(); | // Task.Delay(timeout).Wait(); | ||||
var consumer = new EventingBasicConsumer(_channel); | var consumer = new EventingBasicConsumer(_channel); | ||||
@@ -45,21 +47,26 @@ namespace Cap.Consistency.RabbitMQ | |||||
_channel.BasicConsume(_queueName, true, consumer); | _channel.BasicConsume(_queueName, true, consumer); | ||||
} | } | ||||
public void Subscribe(string topic) { | |||||
public void Subscribe(string topic) | |||||
{ | |||||
_channel.QueueBind(_queueName, _exchange, topic); | _channel.QueueBind(_queueName, _exchange, topic); | ||||
} | } | ||||
public void Subscribe(string topic, int partition) { | |||||
public void Subscribe(string topic, int partition) | |||||
{ | |||||
_channel.QueueBind(_queueName, _exchange, topic); | _channel.QueueBind(_queueName, _exchange, topic); | ||||
} | } | ||||
public void Dispose() { | |||||
public void Dispose() | |||||
{ | |||||
_channel.Dispose(); | _channel.Dispose(); | ||||
_connection.Dispose(); | _connection.Dispose(); | ||||
} | } | ||||
private void OnConsumerReceived(object sender, BasicDeliverEventArgs e) { | |||||
var message = new DeliverMessage { | |||||
private void OnConsumerReceived(object sender, BasicDeliverEventArgs e) | |||||
{ | |||||
var message = new DeliverMessage | |||||
{ | |||||
MessageKey = e.RoutingKey, | MessageKey = e.RoutingKey, | ||||
Body = e.Body, | Body = e.Body, | ||||
Value = Encoding.UTF8.GetString(e.Body) | Value = Encoding.UTF8.GetString(e.Body) |
@@ -1,10 +1,9 @@ | |||||
using Cap.Consistency.Consumer; | |||||
namespace Cap.Consistency.RabbitMQ | |||||
namespace DotNetCore.CAP.RabbitMQ | |||||
{ | { | ||||
public class RabbitMQConsumerClientFactory : IConsumerClientFactory | public class RabbitMQConsumerClientFactory : IConsumerClientFactory | ||||
{ | { | ||||
public IConsumerClient Create(string groupId, string clientHostAddress) { | |||||
public IConsumerClient Create(string groupId, string clientHostAddress) | |||||
{ | |||||
return new RabbitMQConsumerClient(groupId, clientHostAddress); | return new RabbitMQConsumerClient(groupId, clientHostAddress); | ||||
} | } | ||||
} | } |
@@ -1,26 +1,29 @@ | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
using RabbitMQ.Client; | using RabbitMQ.Client; | ||||
namespace Cap.Consistency.RabbitMQ | |||||
namespace DotNetCore.CAP.RabbitMQ | |||||
{ | { | ||||
public class RabbitMQProducerClient : IProducerClient | |||||
public class RabbitMQProducerClient : ICapProducerService | |||||
{ | { | ||||
private readonly ConsistencyOptions _options; | private readonly ConsistencyOptions _options; | ||||
private readonly ILogger _logger; | private readonly ILogger _logger; | ||||
public RabbitMQProducerClient(IOptions<ConsistencyOptions> options, ILoggerFactory loggerFactory) { | |||||
public RabbitMQProducerClient(IOptions<ConsistencyOptions> options, ILoggerFactory loggerFactory) | |||||
{ | |||||
_options = options.Value; | _options = options.Value; | ||||
_logger = loggerFactory.CreateLogger(nameof(RabbitMQProducerClient)); | _logger = loggerFactory.CreateLogger(nameof(RabbitMQProducerClient)); | ||||
} | } | ||||
public Task SendAsync(string topic, string content) { | |||||
public Task SendAsync(string topic, string content) | |||||
{ | |||||
var factory = new ConnectionFactory() { HostName = _options.BrokerUrlList }; | var factory = new ConnectionFactory() { HostName = _options.BrokerUrlList }; | ||||
using (var connection = factory.CreateConnection()) | using (var connection = factory.CreateConnection()) | ||||
using (var channel = connection.CreateModel()) { | |||||
using (var channel = connection.CreateModel()) | |||||
{ | |||||
channel.ExchangeDeclare(exchange: "topic_logs", | channel.ExchangeDeclare(exchange: "topic_logs", | ||||
type: "topic"); | type: "topic"); | ||||
@@ -0,0 +1,11 @@ | |||||
using DotNetCore.CAP.Abstractions; | |||||
namespace DotNetCore.CAP.RabbitMQ | |||||
{ | |||||
public class RabbitMQTopicAttribute : TopicAttribute | |||||
{ | |||||
public RabbitMQTopicAttribute(string routingKey) : base(routingKey) | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -1,15 +1,12 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Cap.Consistency.Abstractions | |||||
namespace DotNetCore.CAP.Abstractions | |||||
{ | { | ||||
public class ConsumerContext | public class ConsumerContext | ||||
{ | { | ||||
public ConsumerContext(ConsumerExecutorDescriptor descriptor, DeliverMessage message) { | |||||
public ConsumerContext(ConsumerExecutorDescriptor descriptor, DeliverMessage message) | |||||
{ | |||||
ConsumerDescriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor)); | ConsumerDescriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor)); | ||||
DeliverMessage = message ?? throw new ArgumentNullException(nameof(message)); | DeliverMessage = message ?? throw new ArgumentNullException(nameof(message)); | ||||
} | } | ||||
@@ -18,4 +15,4 @@ namespace Cap.Consistency.Abstractions | |||||
public DeliverMessage DeliverMessage { get; set; } | public DeliverMessage DeliverMessage { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,9 +1,6 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
using System.Text; | |||||
using System.Reflection; | |||||
namespace Cap.Consistency.Abstractions | |||||
namespace DotNetCore.CAP.Abstractions | |||||
{ | { | ||||
public class ConsumerExecutorDescriptor | public class ConsumerExecutorDescriptor | ||||
{ | { | ||||
@@ -13,4 +10,4 @@ namespace Cap.Consistency.Abstractions | |||||
public TopicAttribute Attribute { get; set; } | public TopicAttribute Attribute { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,20 +1,17 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Abstractions | |||||
namespace DotNetCore.CAP.Abstractions | |||||
{ | { | ||||
public class ConsumerInvokerContext | public class ConsumerInvokerContext | ||||
{ | { | ||||
public ConsumerInvokerContext(ConsumerContext consumerContext) { | |||||
ConsumerContext = consumerContext ?? | |||||
public ConsumerInvokerContext(ConsumerContext consumerContext) | |||||
{ | |||||
ConsumerContext = consumerContext ?? | |||||
throw new ArgumentNullException(nameof(consumerContext)); | throw new ArgumentNullException(nameof(consumerContext)); | ||||
} | } | ||||
public ConsumerContext ConsumerContext { get; set; } | public ConsumerContext ConsumerContext { get; set; } | ||||
public IConsumerInvoker Result { get; set; } | public IConsumerInvoker Result { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System.Threading.Tasks; | |||||
namespace DotNetCore.CAP.Abstractions | |||||
{ | |||||
public interface IConsumerInvoker | |||||
{ | |||||
Task InvokeAsync(); | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System.Threading.Tasks; | |||||
namespace DotNetCore.CAP.Abstractions.ModelBinding | |||||
{ | |||||
public interface IModelBinder | |||||
{ | |||||
Task BindModelAsync(ModelBindingContext bindingContext); | |||||
} | |||||
} |
@@ -1,9 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Microsoft.Extensions.Primitives; | using Microsoft.Extensions.Primitives; | ||||
namespace Cap.Consistency.Abstractions.ModelBinding | |||||
namespace DotNetCore.CAP.Abstractions.ModelBinding | |||||
{ | { | ||||
public class ModelBindingContext | public class ModelBindingContext | ||||
{ | { | ||||
@@ -17,12 +15,14 @@ namespace Cap.Consistency.Abstractions.ModelBinding | |||||
public object Result { get; set; } | public object Result { get; set; } | ||||
public static ModelBindingContext CreateBindingContext(string values, string modelName, Type modelType) { | |||||
return new ModelBindingContext() { | |||||
public static ModelBindingContext CreateBindingContext(string values, string modelName, Type modelType) | |||||
{ | |||||
return new ModelBindingContext() | |||||
{ | |||||
ModelName = modelName, | ModelName = modelName, | ||||
ModelType = modelType, | ModelType = modelType, | ||||
Values = values | Values = values | ||||
}; | }; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,20 +1,19 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Abstractions | |||||
namespace DotNetCore.CAP.Abstractions | |||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] | ||||
public abstract class TopicAttribute : Attribute | public abstract class TopicAttribute : Attribute | ||||
{ | { | ||||
readonly string _name; | |||||
private readonly string _name; | |||||
public TopicAttribute(string topicName) { | |||||
public TopicAttribute(string topicName) | |||||
{ | |||||
this._name = topicName; | this._name = topicName; | ||||
} | } | ||||
public string Name { | |||||
public string Name | |||||
{ | |||||
get { return _name; } | get { return _name; } | ||||
} | } | ||||
@@ -22,4 +21,4 @@ namespace Cap.Consistency.Abstractions | |||||
public bool IsOneWay { get; set; } | public bool IsOneWay { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,28 +1,30 @@ | |||||
using System; | using System; | ||||
using Cap.Consistency; | |||||
using DotNetCore.CAP; | |||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
// ReSharper disable once CheckNamespace | |||||
namespace Microsoft.AspNetCore.Builder | namespace Microsoft.AspNetCore.Builder | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Consistence extensions for <see cref="IApplicationBuilder"/> | /// Consistence extensions for <see cref="IApplicationBuilder"/> | ||||
/// </summary> | /// </summary> | ||||
public static class BuilderExtensions | |||||
public static class AppBuilderExtensions | |||||
{ | { | ||||
///<summary> | ///<summary> | ||||
/// Enables Consistence for the current application | /// Enables Consistence for the current application | ||||
/// </summary> | /// </summary> | ||||
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param> | /// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param> | ||||
/// <returns>The <see cref="IApplicationBuilder"/> instance this method extends.</returns> | /// <returns>The <see cref="IApplicationBuilder"/> instance this method extends.</returns> | ||||
public static IApplicationBuilder UseConsistency(this IApplicationBuilder app) { | |||||
if (app == null) { | |||||
public static IApplicationBuilder UseConsistency(this IApplicationBuilder app) | |||||
{ | |||||
if (app == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(app)); | throw new ArgumentNullException(nameof(app)); | ||||
} | } | ||||
var marker = app.ApplicationServices.GetService<ConsistencyMarkerService>(); | |||||
if (marker == null) { | |||||
var marker = app.ApplicationServices.GetService<CapMarkerService>(); | |||||
if (marker == null) | |||||
{ | |||||
throw new InvalidOperationException("Add Consistency must be called on the service collection."); | throw new InvalidOperationException("Add Consistency must be called on the service collection."); | ||||
} | } | ||||
@@ -0,0 +1,58 @@ | |||||
using System; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Job; | |||||
namespace Microsoft.Extensions.DependencyInjection | |||||
{ | |||||
/// <summary> | |||||
/// Used to verify Consistency service was called on a ServiceCollection | |||||
/// </summary> | |||||
public class CapMarkerService { } | |||||
public class CapBuilder | |||||
{ | |||||
public CapBuilder(IServiceCollection services) | |||||
{ | |||||
Services = services; | |||||
} | |||||
public IServiceCollection Services { get; private set; } | |||||
private CapBuilder AddScoped(Type serviceType, Type concreteType) | |||||
{ | |||||
Services.AddScoped(serviceType, concreteType); | |||||
return this; | |||||
} | |||||
private CapBuilder AddSingleton<TService, TImplementation>() | |||||
where TService : class | |||||
where TImplementation : class, TService | |||||
{ | |||||
Services.AddSingleton<TService, TImplementation>(); | |||||
return this; | |||||
} | |||||
/// <summary> | |||||
/// Adds an <see cref="ICapMessageStore"/> . | |||||
/// </summary> | |||||
/// <typeparam name="T">The type for the <see cref="ICapMessageStore"/> to add. </typeparam> | |||||
/// <returns>The current <see cref="CapBuilder"/> instance.</returns> | |||||
public virtual CapBuilder AddMessageStore<T>() | |||||
where T : class, ICapMessageStore | |||||
{ | |||||
return AddScoped(typeof(ICapMessageStore), typeof(T)); | |||||
} | |||||
public virtual CapBuilder AddJobs<T>() | |||||
where T : class, IJob | |||||
{ | |||||
return AddSingleton<IJob, T>(); | |||||
} | |||||
public virtual CapBuilder AddProducerClient<T>() | |||||
where T : class, ICapProducerService | |||||
{ | |||||
return AddScoped(typeof(ICapProducerService), typeof(T)); | |||||
} | |||||
} | |||||
} |
@@ -1,12 +1,11 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Reflection; | using System.Reflection; | ||||
using Cap.Consistency; | |||||
using Cap.Consistency.Abstractions.ModelBinding; | |||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Infrastructure; | |||||
using Cap.Consistency.Internal; | |||||
using Cap.Consistency.Job; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Internal; | |||||
using DotNetCore.CAP.Job; | |||||
using Microsoft.Extensions.DependencyInjection.Extensions; | using Microsoft.Extensions.DependencyInjection.Extensions; | ||||
namespace Microsoft.Extensions.DependencyInjection | namespace Microsoft.Extensions.DependencyInjection | ||||
@@ -20,11 +19,12 @@ namespace Microsoft.Extensions.DependencyInjection | |||||
/// Adds and configures the consistence services for the consitence. | /// Adds and configures the consistence services for the consitence. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="services">The services available in the application.</param> | /// <param name="services">The services available in the application.</param> | ||||
/// <returns>An <see cref="ConsistencyBuilder"/> for application services.</returns> | |||||
public static ConsistencyBuilder AddConsistency(this IServiceCollection services) { | |||||
/// <returns>An <see cref="CapBuilder"/> for application services.</returns> | |||||
public static CapBuilder AddConsistency(this IServiceCollection services) | |||||
{ | |||||
services.AddConsistency(x => new ConsistencyOptions()); | services.AddConsistency(x => new ConsistencyOptions()); | ||||
return new ConsistencyBuilder(services); | |||||
return new CapBuilder(services); | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -32,12 +32,12 @@ namespace Microsoft.Extensions.DependencyInjection | |||||
/// </summary> | /// </summary> | ||||
/// <param name="services">The services available in the application.</param> | /// <param name="services">The services available in the application.</param> | ||||
/// <param name="setupAction">An action to configure the <see cref="ConsistencyOptions"/>.</param> | /// <param name="setupAction">An action to configure the <see cref="ConsistencyOptions"/>.</param> | ||||
/// <returns>An <see cref="ConsistencyBuilder"/> for application services.</returns> | |||||
public static ConsistencyBuilder AddConsistency( | |||||
/// <returns>An <see cref="CapBuilder"/> for application services.</returns> | |||||
public static CapBuilder AddConsistency( | |||||
this IServiceCollection services, | this IServiceCollection services, | ||||
Action<ConsistencyOptions> setupAction) { | |||||
services.TryAddSingleton<ConsistencyMarkerService>(); | |||||
Action<ConsistencyOptions> setupAction) | |||||
{ | |||||
services.TryAddSingleton<CapMarkerService>(); | |||||
services.Configure(setupAction); | services.Configure(setupAction); | ||||
AddConsumerServices(services); | AddConsumerServices(services); | ||||
@@ -55,27 +55,32 @@ namespace Microsoft.Extensions.DependencyInjection | |||||
services.TryAddSingleton<IJob, CapJob>(); | services.TryAddSingleton<IJob, CapJob>(); | ||||
services.TryAddTransient<DefaultCronJobRegistry>(); | services.TryAddTransient<DefaultCronJobRegistry>(); | ||||
services.TryAddScoped<IProducerClient, DefaultProducerClient>(); | |||||
services.TryAddScoped<ICapProducerService, DefaultProducerService>(); | |||||
return new ConsistencyBuilder(services); | |||||
return new CapBuilder(services); | |||||
} | } | ||||
private static void AddConsumerServices(IServiceCollection services) { | |||||
private static void AddConsumerServices(IServiceCollection services) | |||||
{ | |||||
var consumerListenerServices = new Dictionary<Type, Type>(); | var consumerListenerServices = new Dictionary<Type, Type>(); | ||||
foreach (var rejectedServices in services) { | |||||
foreach (var rejectedServices in services) | |||||
{ | |||||
if (rejectedServices.ImplementationType != null | if (rejectedServices.ImplementationType != null | ||||
&& typeof(IConsumerService).IsAssignableFrom(rejectedServices.ImplementationType)) | && typeof(IConsumerService).IsAssignableFrom(rejectedServices.ImplementationType)) | ||||
consumerListenerServices.Add(typeof(IConsumerService), rejectedServices.ImplementationType); | consumerListenerServices.Add(typeof(IConsumerService), rejectedServices.ImplementationType); | ||||
} | } | ||||
foreach (var service in consumerListenerServices) { | |||||
foreach (var service in consumerListenerServices) | |||||
{ | |||||
services.AddSingleton(service.Key, service.Value); | services.AddSingleton(service.Key, service.Value); | ||||
} | } | ||||
var types = Assembly.GetEntryAssembly().ExportedTypes; | var types = Assembly.GetEntryAssembly().ExportedTypes; | ||||
foreach (var type in types) { | |||||
if (typeof(IConsumerService).IsAssignableFrom(type)) { | |||||
foreach (var type in types) | |||||
{ | |||||
if (typeof(IConsumerService).IsAssignableFrom(type)) | |||||
{ | |||||
services.AddSingleton(typeof(IConsumerService), type); | services.AddSingleton(typeof(IConsumerService), type); | ||||
} | } | ||||
} | } |
@@ -1,7 +1,7 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | <Project Sdk="Microsoft.NET.Sdk"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<PackageId>Cap.Consistency</PackageId> | |||||
<PackageId>DotNetCore.CAP</PackageId> | |||||
<TargetFramework>netstandard1.6</TargetFramework> | <TargetFramework>netstandard1.6</TargetFramework> | ||||
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | <NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion> | ||||
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> | <PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback> |
@@ -1,14 +1,13 @@ | |||||
using System; | using System; | ||||
using System.Linq; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public class DefaultBootstrapper : IBootstrapper | public class DefaultBootstrapper : IBootstrapper | ||||
{ | { | ||||
@@ -19,39 +18,44 @@ namespace Cap.Consistency | |||||
public DefaultBootstrapper( | public DefaultBootstrapper( | ||||
IOptions<ConsistencyOptions> options, | IOptions<ConsistencyOptions> options, | ||||
IConsistencyMessageStore storage, | |||||
ICapMessageStore storage, | |||||
IApplicationLifetime appLifetime, | IApplicationLifetime appLifetime, | ||||
IServiceProvider provider) { | |||||
IServiceProvider provider) | |||||
{ | |||||
Options = options.Value; | Options = options.Value; | ||||
Storage = storage; | Storage = storage; | ||||
_appLifetime = appLifetime; | _appLifetime = appLifetime; | ||||
Provider = provider; | Provider = provider; | ||||
Servers = Provider.GetServices<IProcessingServer>(); | Servers = Provider.GetServices<IProcessingServer>(); | ||||
_cts = new CancellationTokenSource(); | _cts = new CancellationTokenSource(); | ||||
_ctsRegistration = appLifetime.ApplicationStopping.Register(() => { | |||||
_ctsRegistration = appLifetime.ApplicationStopping.Register(() => | |||||
{ | |||||
_cts.Cancel(); | _cts.Cancel(); | ||||
try { | |||||
try | |||||
{ | |||||
_bootstrappingTask?.Wait(); | _bootstrappingTask?.Wait(); | ||||
} | } | ||||
catch (OperationCanceledException) { | |||||
catch (OperationCanceledException) | |||||
{ | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
protected ConsistencyOptions Options { get; } | protected ConsistencyOptions Options { get; } | ||||
protected IConsistencyMessageStore Storage { get; } | |||||
protected ICapMessageStore Storage { get; } | |||||
protected IEnumerable<IProcessingServer> Servers { get; } | protected IEnumerable<IProcessingServer> Servers { get; } | ||||
public IServiceProvider Provider { get; private set; } | public IServiceProvider Provider { get; private set; } | ||||
public Task BootstrapAsync() { | |||||
public Task BootstrapAsync() | |||||
{ | |||||
return (_bootstrappingTask = BootstrapTaskAsync()); | return (_bootstrappingTask = BootstrapTaskAsync()); | ||||
} | } | ||||
private async Task BootstrapTaskAsync() { | |||||
private async Task BootstrapTaskAsync() | |||||
{ | |||||
if (_cts.IsCancellationRequested) return; | if (_cts.IsCancellationRequested) return; | ||||
if (_cts.IsCancellationRequested) return; | if (_cts.IsCancellationRequested) return; | ||||
@@ -60,11 +64,14 @@ namespace Cap.Consistency | |||||
if (_cts.IsCancellationRequested) return; | if (_cts.IsCancellationRequested) return; | ||||
foreach (var item in Servers) { | |||||
try { | |||||
foreach (var item in Servers) | |||||
{ | |||||
try | |||||
{ | |||||
item.Start(); | item.Start(); | ||||
} | } | ||||
catch (Exception) { | |||||
catch (Exception) | |||||
{ | |||||
} | } | ||||
} | } | ||||
@@ -72,9 +79,12 @@ namespace Cap.Consistency | |||||
_cts.Dispose(); | _cts.Dispose(); | ||||
} | } | ||||
public virtual Task BootstrapCoreAsync() { | |||||
_appLifetime.ApplicationStopping.Register(() => { | |||||
foreach (var item in Servers) { | |||||
public virtual Task BootstrapCoreAsync() | |||||
{ | |||||
_appLifetime.ApplicationStopping.Register(() => | |||||
{ | |||||
foreach (var item in Servers) | |||||
{ | |||||
item.Dispose(); | item.Dispose(); | ||||
} | } | ||||
}); | }); |
@@ -1,6 +1,6 @@ | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents bootstrapping logic. For example, adding initial state to the storage or querying certain entities. | /// Represents bootstrapping logic. For example, adding initial state to the storage or querying certain entities. |
@@ -1,15 +1,14 @@ | |||||
using System; | |||||
using System.Threading; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Provides an abstraction for a store which manages consistent message. | /// Provides an abstraction for a store which manages consistent message. | ||||
/// </summary> | /// </summary> | ||||
/// <typeparam name="ConsistencyMessage"></typeparam> | /// <typeparam name="ConsistencyMessage"></typeparam> | ||||
public interface IConsistencyMessageStore | |||||
public interface ICapMessageStore | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Finds and returns a message, if any, who has the specified <paramref name="messageId"/>. | /// Finds and returns a message, if any, who has the specified <paramref name="messageId"/>. | ||||
@@ -20,7 +19,6 @@ namespace Cap.Consistency | |||||
/// The <see cref="Task"/> that represents the asynchronous operation, containing the message matching the specified <paramref name="messageId"/> if it exists. | /// The <see cref="Task"/> that represents the asynchronous operation, containing the message matching the specified <paramref name="messageId"/> if it exists. | ||||
/// </returns> | /// </returns> | ||||
Task<ConsistencyMessage> FindByIdAsync(string messageId, CancellationToken cancellationToken); | Task<ConsistencyMessage> FindByIdAsync(string messageId, CancellationToken cancellationToken); | ||||
/// <summary> | /// <summary> | ||||
/// Creates a new message in a store as an asynchronous operation. | /// Creates a new message in a store as an asynchronous operation. | ||||
@@ -56,6 +54,6 @@ namespace Cap.Consistency | |||||
Task<ConsistencyMessage> GetFirstEnqueuedMessageAsync(CancellationToken cancellationToken); | Task<ConsistencyMessage> GetFirstEnqueuedMessageAsync(CancellationToken cancellationToken); | ||||
// void ChangeState(ConsistencyMessage message, MessageStatus status); | |||||
// void ChangeState(ConsistencyMessage message, MessageStatus status); | |||||
} | } | ||||
} | } |
@@ -1,34 +1,36 @@ | |||||
using System; | using System; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public class DefaultProducerClient : IProducerClient | |||||
public class DefaultProducerService : ICapProducerService | |||||
{ | { | ||||
private readonly IConsistencyMessageStore _store; | |||||
private readonly ICapMessageStore _store; | |||||
private readonly ILogger _logger; | private readonly ILogger _logger; | ||||
private readonly CancellationTokenSource _cts; | private readonly CancellationTokenSource _cts; | ||||
public DefaultProducerClient( | |||||
IConsistencyMessageStore store, | |||||
ILogger<DefaultProducerClient> logger) { | |||||
public DefaultProducerService( | |||||
ICapMessageStore store, | |||||
ILogger<DefaultProducerService> logger) | |||||
{ | |||||
_store = store; | _store = store; | ||||
_logger = logger; | _logger = logger; | ||||
_cts = new CancellationTokenSource(); | _cts = new CancellationTokenSource(); | ||||
} | } | ||||
public Task SendAsync(string topic, string content) { | |||||
public Task SendAsync(string topic, string content) | |||||
{ | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | if (topic == null) throw new ArgumentNullException(nameof(topic)); | ||||
if (content == null) throw new ArgumentNullException(nameof(content)); | if (content == null) throw new ArgumentNullException(nameof(content)); | ||||
return StoreMessage(topic, content); | return StoreMessage(topic, content); | ||||
} | } | ||||
public Task SendAsync<T>(string topic, T obj) { | |||||
public Task SendAsync<T>(string topic, T obj) | |||||
{ | |||||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | if (topic == null) throw new ArgumentNullException(nameof(topic)); | ||||
var content = Helper.ToJson(obj); | var content = Helper.ToJson(obj); | ||||
@@ -38,9 +40,10 @@ namespace Cap.Consistency | |||||
return StoreMessage(topic, content); | return StoreMessage(topic, content); | ||||
} | } | ||||
private async Task StoreMessage(string topic, string content) { | |||||
var message = new ConsistencyMessage { | |||||
private async Task StoreMessage(string topic, string content) | |||||
{ | |||||
var message = new ConsistencyMessage | |||||
{ | |||||
Topic = topic, | Topic = topic, | ||||
Payload = content | Payload = content | ||||
}; | }; | ||||
@@ -49,9 +52,10 @@ namespace Cap.Consistency | |||||
WaitHandleEx.PulseEvent.Set(); | WaitHandleEx.PulseEvent.Set(); | ||||
if (_logger.IsEnabled(LogLevel.Debug)) { | |||||
if (_logger.IsEnabled(LogLevel.Debug)) | |||||
{ | |||||
_logger.LogDebug("Enqueuing a topic to be store. topic:{topic}, content:{content}", topic, content); | _logger.LogDebug("Enqueuing a topic to be store. topic:{topic}, content:{content}", topic, content); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System.Threading.Tasks; | |||||
namespace DotNetCore.CAP | |||||
{ | |||||
public interface ICapProducerService | |||||
{ | |||||
Task SendAsync(string topic, string content); | |||||
} | |||||
} |
@@ -1,9 +1,9 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | using System.Text; | ||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Cap.Consistency.Consumer | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public interface IConsumerClient : IDisposable | public interface IConsumerClient : IDisposable | ||||
{ | { |
@@ -3,7 +3,7 @@ using System.Collections.Generic; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Cap.Consistency.Consumer | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public interface IConsumerClientFactory | public interface IConsumerClientFactory | ||||
{ | { |
@@ -3,13 +3,13 @@ using System.Linq; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Abstractions; | |||||
using Cap.Consistency.Infrastructure; | |||||
using Cap.Consistency.Internal; | |||||
using DotNetCore.CAP.Abstractions; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Internal; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace Cap.Consistency.Consumer | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public class ConsumerHandler : IConsumerHandler, IDisposable | public class ConsumerHandler : IConsumerHandler, IDisposable | ||||
{ | { | ||||
@@ -21,7 +21,7 @@ namespace Cap.Consistency.Consumer | |||||
private readonly MethodMatcherCache _selector; | private readonly MethodMatcherCache _selector; | ||||
private readonly ConsistencyOptions _options; | private readonly ConsistencyOptions _options; | ||||
private readonly IConsistencyMessageStore _messageStore; | |||||
private readonly ICapMessageStore _messageStore; | |||||
private readonly CancellationTokenSource _cts; | private readonly CancellationTokenSource _cts; | ||||
public event EventHandler<ConsistencyMessage> MessageReceieved; | public event EventHandler<ConsistencyMessage> MessageReceieved; | ||||
@@ -35,7 +35,7 @@ namespace Cap.Consistency.Consumer | |||||
IConsumerInvokerFactory consumerInvokerFactory, | IConsumerInvokerFactory consumerInvokerFactory, | ||||
IConsumerClientFactory consumerClientFactory, | IConsumerClientFactory consumerClientFactory, | ||||
ILoggerFactory loggerFactory, | ILoggerFactory loggerFactory, | ||||
IConsistencyMessageStore messageStore, | |||||
ICapMessageStore messageStore, | |||||
MethodMatcherCache selector, | MethodMatcherCache selector, | ||||
IOptions<ConsistencyOptions> options) { | IOptions<ConsistencyOptions> options) { | ||||
_selector = selector; | _selector = selector; |
@@ -1,7 +1,6 @@ | |||||
namespace Cap.Consistency.Consumer | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public interface IConsumerHandler : IProcessingServer | public interface IConsumerHandler : IProcessingServer | ||||
{ | { | ||||
} | } | ||||
} | } |
@@ -0,0 +1,6 @@ | |||||
namespace DotNetCore.CAP | |||||
{ | |||||
public interface IConsumerService | |||||
{ | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System; | |||||
namespace DotNetCore.CAP | |||||
{ | |||||
public interface IProcessingServer : IDisposable | |||||
{ | |||||
void Start(); | |||||
} | |||||
} |
@@ -1,6 +1,6 @@ | |||||
using System; | using System; | ||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// The default implementation of <see cref="ConsistencyMessage{TKey}"/> which uses a string as a primary key. | /// The default implementation of <see cref="ConsistencyMessage{TKey}"/> which uses a string as a primary key. | ||||
@@ -13,7 +13,8 @@ namespace Cap.Consistency.Infrastructure | |||||
/// <remarks> | /// <remarks> | ||||
/// The Id property is initialized to from a new GUID string value. | /// The Id property is initialized to from a new GUID string value. | ||||
/// </remarks> | /// </remarks> | ||||
public ConsistencyMessage() { | |||||
public ConsistencyMessage() | |||||
{ | |||||
Id = Guid.NewGuid().ToString(); | Id = Guid.NewGuid().ToString(); | ||||
SendTime = DateTime.Now; | SendTime = DateTime.Now; | ||||
UpdateTime = SendTime; | UpdateTime = SendTime; |
@@ -1,6 +1,6 @@ | |||||
using Cap.Consistency.Job; | |||||
using DotNetCore.CAP.Job; | |||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents all the options you can use to configure the system. | /// Represents all the options you can use to configure the system. |
@@ -1,8 +1,4 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
public class DeliverMessage | public class DeliverMessage | ||||
{ | { | ||||
@@ -14,9 +10,8 @@ namespace Cap.Consistency.Infrastructure | |||||
/// </summary> | /// </summary> | ||||
public string MessageKey { get; set; } | public string MessageKey { get; set; } | ||||
public byte[] Body { get; set; } | public byte[] Body { get; set; } | ||||
public string Value { get; set; } | public string Value { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,32 +1,34 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
internal static class Helper | internal static class Helper | ||||
{ | { | ||||
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); | ||||
private static JsonSerializerSettings SerializerSettings; | private static JsonSerializerSettings SerializerSettings; | ||||
public static void SetSerializerSettings(JsonSerializerSettings setting) { | |||||
public static void SetSerializerSettings(JsonSerializerSettings setting) | |||||
{ | |||||
SerializerSettings = setting; | SerializerSettings = setting; | ||||
} | } | ||||
public static string ToJson(object value) { | |||||
public static string ToJson(object value) | |||||
{ | |||||
return value != null | return value != null | ||||
? JsonConvert.SerializeObject(value, SerializerSettings) | ? JsonConvert.SerializeObject(value, SerializerSettings) | ||||
: null; | : null; | ||||
} | } | ||||
public static T FromJson<T>(string value) { | |||||
public static T FromJson<T>(string value) | |||||
{ | |||||
return value != null | return value != null | ||||
? JsonConvert.DeserializeObject<T>(value, SerializerSettings) | ? JsonConvert.DeserializeObject<T>(value, SerializerSettings) | ||||
: default(T); | : default(T); | ||||
} | } | ||||
public static object FromJson(string value, Type type) { | |||||
public static object FromJson(string value, Type type) | |||||
{ | |||||
if (type == null) throw new ArgumentNullException(nameof(type)); | if (type == null) throw new ArgumentNullException(nameof(type)); | ||||
return value != null | return value != null | ||||
@@ -34,13 +36,15 @@ namespace Cap.Consistency.Infrastructure | |||||
: null; | : null; | ||||
} | } | ||||
public static long ToTimestamp(DateTime value) { | |||||
public static long ToTimestamp(DateTime value) | |||||
{ | |||||
var elapsedTime = value - Epoch; | var elapsedTime = value - Epoch; | ||||
return (long)elapsedTime.TotalSeconds; | return (long)elapsedTime.TotalSeconds; | ||||
} | } | ||||
public static DateTime FromTimestamp(long value) { | |||||
public static DateTime FromTimestamp(long value) | |||||
{ | |||||
return Epoch.AddSeconds(value); | return Epoch.AddSeconds(value); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,9 +1,7 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
using System.Collections.Generic; | |||||
using DotNetCore.CAP.Abstractions; | |||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
public interface IConsumerExcutorSelector | public interface IConsumerExcutorSelector | ||||
{ | { | ||||
@@ -11,4 +9,4 @@ namespace Cap.Consistency.Infrastructure | |||||
ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor); | ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor); | ||||
} | } | ||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using DotNetCore.CAP.Abstractions; | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | |||||
public interface IConsumerInvokerFactory | |||||
{ | |||||
IConsumerInvoker CreateInvoker(ConsumerContext actionContext); | |||||
} | |||||
} |
@@ -1,24 +1,25 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Cap.Consistency.Infrastructure | |||||
namespace DotNetCore.CAP.Infrastructure | |||||
{ | { | ||||
public static class WaitHandleEx | public static class WaitHandleEx | ||||
{ | { | ||||
public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); | public static readonly AutoResetEvent PulseEvent = new AutoResetEvent(true); | ||||
public static Task WaitAnyAsync(WaitHandle handle1, WaitHandle handle2, TimeSpan timeout) { | |||||
public static Task WaitAnyAsync(WaitHandle handle1, WaitHandle handle2, TimeSpan timeout) | |||||
{ | |||||
var t1 = handle1.WaitOneAsync(timeout); | var t1 = handle1.WaitOneAsync(timeout); | ||||
var t2 = handle2.WaitOneAsync(timeout); | var t2 = handle2.WaitOneAsync(timeout); | ||||
return Task.WhenAny(t1, t2); | return Task.WhenAny(t1, t2); | ||||
} | } | ||||
public static async Task<bool> WaitOneAsync(this WaitHandle handle, TimeSpan timeout) { | |||||
public static async Task<bool> WaitOneAsync(this WaitHandle handle, TimeSpan timeout) | |||||
{ | |||||
RegisteredWaitHandle registeredHandle = null; | RegisteredWaitHandle registeredHandle = null; | ||||
try { | |||||
try | |||||
{ | |||||
var tcs = new TaskCompletionSource<bool>(); | var tcs = new TaskCompletionSource<bool>(); | ||||
registeredHandle = ThreadPool.RegisterWaitForSingleObject( | registeredHandle = ThreadPool.RegisterWaitForSingleObject( | ||||
handle, | handle, | ||||
@@ -28,11 +29,13 @@ namespace Cap.Consistency.Infrastructure | |||||
true); | true); | ||||
return await tcs.Task; | return await tcs.Task; | ||||
} | } | ||||
finally { | |||||
if (registeredHandle != null) { | |||||
finally | |||||
{ | |||||
if (registeredHandle != null) | |||||
{ | |||||
registeredHandle.Unregister(null); | registeredHandle.Unregister(null); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -2,39 +2,41 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
using Cap.Consistency.Consumer; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Abstractions; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class ConsumerExcutorSelector : IConsumerExcutorSelector | public class ConsumerExcutorSelector : IConsumerExcutorSelector | ||||
{ | { | ||||
private readonly IServiceProvider _serviceProvider; | private readonly IServiceProvider _serviceProvider; | ||||
public ConsumerExcutorSelector(IServiceProvider serviceProvider) { | |||||
public ConsumerExcutorSelector(IServiceProvider serviceProvider) | |||||
{ | |||||
_serviceProvider = serviceProvider; | _serviceProvider = serviceProvider; | ||||
} | } | ||||
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) { | |||||
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||||
{ | |||||
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); | return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); | ||||
} | } | ||||
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates(TopicContext context) { | |||||
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates(TopicContext context) | |||||
{ | |||||
var consumerServices = context.ServiceProvider.GetServices<IConsumerService>(); | var consumerServices = context.ServiceProvider.GetServices<IConsumerService>(); | ||||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | ||||
foreach (var service in consumerServices) { | |||||
foreach (var service in consumerServices) | |||||
{ | |||||
var typeInfo = service.GetType().GetTypeInfo(); | var typeInfo = service.GetType().GetTypeInfo(); | ||||
if (!typeof(IConsumerService).GetTypeInfo().IsAssignableFrom(typeInfo)) { | |||||
if (!typeof(IConsumerService).GetTypeInfo().IsAssignableFrom(typeInfo)) | |||||
{ | |||||
continue; | continue; | ||||
} | } | ||||
foreach (var method in typeInfo.DeclaredMethods) { | |||||
foreach (var method in typeInfo.DeclaredMethods) | |||||
{ | |||||
var topicAttr = method.GetCustomAttribute<TopicAttribute>(true); | var topicAttr = method.GetCustomAttribute<TopicAttribute>(true); | ||||
if (topicAttr == null) continue; | if (topicAttr == null) continue; | ||||
@@ -44,16 +46,20 @@ namespace Cap.Consistency.Internal | |||||
return executorDescriptorList; | return executorDescriptorList; | ||||
} | } | ||||
private ConsumerExecutorDescriptor InitDescriptor(TopicAttribute attr, | |||||
MethodInfo methodInfo, TypeInfo implType | |||||
) { | |||||
var descriptor = new ConsumerExecutorDescriptor(); | |||||
descriptor.Attribute = attr; | |||||
descriptor.MethodInfo = methodInfo; | |||||
descriptor.ImplTypeInfo = implType; | |||||
private ConsumerExecutorDescriptor InitDescriptor( | |||||
TopicAttribute attr, | |||||
MethodInfo methodInfo, | |||||
TypeInfo implType) | |||||
{ | |||||
var descriptor = new ConsumerExecutorDescriptor() | |||||
{ | |||||
Attribute = attr, | |||||
MethodInfo = methodInfo, | |||||
ImplTypeInfo = implType | |||||
}; | |||||
return descriptor; | return descriptor; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,15 +1,12 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Reflection; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Abstractions; | |||||
using Cap.Consistency.Abstractions.ModelBinding; | |||||
using DotNetCore.CAP.Abstractions; | |||||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Newtonsoft.Json; | |||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class ConsumerInvoker : IConsumerInvoker | public class ConsumerInvoker : IConsumerInvoker | ||||
{ | { | ||||
@@ -22,8 +19,8 @@ namespace Cap.Consistency.Internal | |||||
public ConsumerInvoker(ILogger logger, | public ConsumerInvoker(ILogger logger, | ||||
IServiceProvider serviceProvider, | IServiceProvider serviceProvider, | ||||
IModelBinder modelBinder, | IModelBinder modelBinder, | ||||
ConsumerContext consumerContext) { | |||||
ConsumerContext consumerContext) | |||||
{ | |||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||||
_serviceProvider = serviceProvider; | _serviceProvider = serviceProvider; | ||||
_modelBinder = modelBinder; | _modelBinder = modelBinder; | ||||
@@ -32,43 +29,45 @@ namespace Cap.Consistency.Internal | |||||
_consumerContext.ConsumerDescriptor.ImplTypeInfo); | _consumerContext.ConsumerDescriptor.ImplTypeInfo); | ||||
} | } | ||||
public Task InvokeAsync() { | |||||
try { | |||||
using (_logger.BeginScope("consumer invoker begin")) { | |||||
public Task InvokeAsync() | |||||
{ | |||||
try | |||||
{ | |||||
using (_logger.BeginScope("consumer invoker begin")) | |||||
{ | |||||
_logger.LogDebug("Executing consumer Topic: {0}", _consumerContext.ConsumerDescriptor.Attribute); | _logger.LogDebug("Executing consumer Topic: {0}", _consumerContext.ConsumerDescriptor.Attribute); | ||||
try { | |||||
try | |||||
{ | |||||
var obj = ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, _consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType()); | var obj = ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, _consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType()); | ||||
var bodyString = Encoding.UTF8.GetString(_consumerContext.DeliverMessage.Body); | var bodyString = Encoding.UTF8.GetString(_consumerContext.DeliverMessage.Body); | ||||
if (_executor.MethodParameters.Length > 0) { | |||||
if (_executor.MethodParameters.Length > 0) | |||||
{ | |||||
var firstParameter = _executor.MethodParameters[0]; | var firstParameter = _executor.MethodParameters[0]; | ||||
var bindingContext = ModelBindingContext.CreateBindingContext(bodyString, | |||||
var bindingContext = ModelBindingContext.CreateBindingContext(bodyString, | |||||
firstParameter.Name, firstParameter.ParameterType); | firstParameter.Name, firstParameter.ParameterType); | ||||
_modelBinder.BindModelAsync(bindingContext); | _modelBinder.BindModelAsync(bindingContext); | ||||
_executor.Execute(obj, bindingContext.Result); | _executor.Execute(obj, bindingContext.Result); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
_executor.Execute(obj); | _executor.Execute(obj); | ||||
} | } | ||||
return Task.CompletedTask; | return Task.CompletedTask; | ||||
} | } | ||||
finally { | |||||
finally | |||||
{ | |||||
_logger.LogDebug("Executed consumer method ."); | _logger.LogDebug("Executed consumer method ."); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
finally { | |||||
finally | |||||
{ | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,12 +1,10 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
using Cap.Consistency.Abstractions.ModelBinding; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Abstractions; | |||||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class ConsumerInvokerFactory : IConsumerInvokerFactory | public class ConsumerInvokerFactory : IConsumerInvokerFactory | ||||
{ | { | ||||
@@ -17,15 +15,15 @@ namespace Cap.Consistency.Internal | |||||
public ConsumerInvokerFactory( | public ConsumerInvokerFactory( | ||||
ILoggerFactory loggerFactory, | ILoggerFactory loggerFactory, | ||||
IModelBinder modelBinder, | IModelBinder modelBinder, | ||||
IServiceProvider serviceProvider) { | |||||
IServiceProvider serviceProvider) | |||||
{ | |||||
_logger = loggerFactory.CreateLogger<ConsumerInvokerFactory>(); | _logger = loggerFactory.CreateLogger<ConsumerInvokerFactory>(); | ||||
_modelBinder = modelBinder; | _modelBinder = modelBinder; | ||||
_serviceProvider = serviceProvider; | _serviceProvider = serviceProvider; | ||||
} | } | ||||
public IConsumerInvoker CreateInvoker(ConsumerContext consumerContext) { | |||||
public IConsumerInvoker CreateInvoker(ConsumerContext consumerContext) | |||||
{ | |||||
var context = new ConsumerInvokerContext(consumerContext); | var context = new ConsumerInvokerContext(consumerContext); | ||||
context.Result = new ConsumerInvoker(_logger, _serviceProvider, _modelBinder, consumerContext); | context.Result = new ConsumerInvoker(_logger, _serviceProvider, _modelBinder, consumerContext); | ||||
@@ -33,4 +31,4 @@ namespace Cap.Consistency.Internal | |||||
return context.Result; | return context.Result; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,26 +1,28 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Collections.Generic; | |||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class ConsumerMethodExecutor | public class ConsumerMethodExecutor | ||||
{ | { | ||||
public static object[] PrepareArguments( | public static object[] PrepareArguments( | ||||
IDictionary<string, object> actionParameters, | IDictionary<string, object> actionParameters, | ||||
ObjectMethodExecutor actionMethodExecutor) { | |||||
ObjectMethodExecutor actionMethodExecutor) | |||||
{ | |||||
var declaredParameterInfos = actionMethodExecutor.MethodParameters; | var declaredParameterInfos = actionMethodExecutor.MethodParameters; | ||||
var count = declaredParameterInfos.Length; | var count = declaredParameterInfos.Length; | ||||
if (count == 0) { | |||||
if (count == 0) | |||||
{ | |||||
return null; | return null; | ||||
} | } | ||||
var arguments = new object[count]; | var arguments = new object[count]; | ||||
for (var index = 0; index < count; index++) { | |||||
for (var index = 0; index < count; index++) | |||||
{ | |||||
var parameterInfo = declaredParameterInfos[index]; | var parameterInfo = declaredParameterInfos[index]; | ||||
object value; | object value; | ||||
if (!actionParameters.TryGetValue(parameterInfo.Name, out value)) { | |||||
if (!actionParameters.TryGetValue(parameterInfo.Name, out value)) | |||||
{ | |||||
value = actionMethodExecutor.GetDefaultValueForParameter(index); | value = actionMethodExecutor.GetDefaultValueForParameter(index); | ||||
} | } | ||||
@@ -30,4 +32,4 @@ namespace Cap.Consistency.Internal | |||||
return arguments; | return arguments; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,39 +1,40 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Linq.Expressions; | using System.Linq.Expressions; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Abstractions.ModelBinding; | |||||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class DefaultModelBinder : IModelBinder | public class DefaultModelBinder : IModelBinder | ||||
{ | { | ||||
private Func<object> _modelCreator; | private Func<object> _modelCreator; | ||||
public Task BindModelAsync(ModelBindingContext bindingContext) { | |||||
if (bindingContext.Model == null) { | |||||
public Task BindModelAsync(ModelBindingContext bindingContext) | |||||
{ | |||||
if (bindingContext.Model == null) | |||||
{ | |||||
bindingContext.Model = CreateModel(bindingContext); | bindingContext.Model = CreateModel(bindingContext); | ||||
} | } | ||||
bindingContext.Result = JsonConvert.DeserializeObject(bindingContext.Values, bindingContext.ModelType); | bindingContext.Result = JsonConvert.DeserializeObject(bindingContext.Values, bindingContext.ModelType); | ||||
return Task.CompletedTask; | return Task.CompletedTask; | ||||
} | } | ||||
protected virtual object CreateModel(ModelBindingContext bindingContext) { | |||||
if (bindingContext == null) { | |||||
protected virtual object CreateModel(ModelBindingContext bindingContext) | |||||
{ | |||||
if (bindingContext == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(bindingContext)); | throw new ArgumentNullException(nameof(bindingContext)); | ||||
} | } | ||||
if (_modelCreator == null) { | |||||
if (_modelCreator == null) | |||||
{ | |||||
var modelTypeInfo = bindingContext.ModelType.GetTypeInfo(); | var modelTypeInfo = bindingContext.ModelType.GetTypeInfo(); | ||||
if (modelTypeInfo.IsAbstract || modelTypeInfo.GetConstructor(Type.EmptyTypes) == null) { | |||||
if (modelTypeInfo.IsAbstract || modelTypeInfo.GetConstructor(Type.EmptyTypes) == null) | |||||
{ | |||||
throw new InvalidOperationException(); | throw new InvalidOperationException(); | ||||
} | } | ||||
@@ -45,4 +46,4 @@ namespace Cap.Consistency.Internal | |||||
return _modelCreator(); | return _modelCreator(); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,37 +1,37 @@ | |||||
using System; | using System; | ||||
using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Abstractions; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Abstractions; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class MethodMatcherCache | public class MethodMatcherCache | ||||
{ | { | ||||
private readonly IConsumerExcutorSelector _selector; | private readonly IConsumerExcutorSelector _selector; | ||||
public MethodMatcherCache(IConsumerExcutorSelector selector) { | |||||
public MethodMatcherCache(IConsumerExcutorSelector selector) | |||||
{ | |||||
_selector = selector; | _selector = selector; | ||||
} | } | ||||
public ConcurrentDictionary<string, ConsumerExecutorDescriptor> GetCandidatesMethods(TopicContext routeContext) { | |||||
if (Entries.Count == 0) { | |||||
public ConcurrentDictionary<string, ConsumerExecutorDescriptor> GetCandidatesMethods(TopicContext routeContext) | |||||
{ | |||||
if (Entries.Count == 0) | |||||
{ | |||||
var executorCollection = _selector.SelectCandidates(routeContext); | var executorCollection = _selector.SelectCandidates(routeContext); | ||||
foreach (var item in executorCollection) { | |||||
foreach (var item in executorCollection) | |||||
{ | |||||
Entries.GetOrAdd(item.Attribute.Name, item); | Entries.GetOrAdd(item.Attribute.Name, item); | ||||
} | } | ||||
} | } | ||||
return Entries; | return Entries; | ||||
} | } | ||||
public ConsumerExecutorDescriptor GetTopicExector(string topicName) { | |||||
if (Entries == null) { | |||||
public ConsumerExecutorDescriptor GetTopicExector(string topicName) | |||||
{ | |||||
if (Entries == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(Entries)); | throw new ArgumentNullException(nameof(Entries)); | ||||
} | } | ||||
@@ -41,4 +41,4 @@ namespace Cap.Consistency.Internal | |||||
public ConcurrentDictionary<string, ConsumerExecutorDescriptor> Entries { get; } = | public ConcurrentDictionary<string, ConsumerExecutorDescriptor> Entries { get; } = | ||||
new ConcurrentDictionary<string, ConsumerExecutorDescriptor>(); | new ConcurrentDictionary<string, ConsumerExecutorDescriptor>(); | ||||
} | } | ||||
} | |||||
} |
@@ -2,9 +2,8 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text; | |||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Provides access to the combined list of attributes associated a <see cref="Type"/> or property. | /// Provides access to the combined list of attributes associated a <see cref="Type"/> or property. | ||||
@@ -15,8 +14,10 @@ namespace Cap.Consistency.Internal | |||||
/// Creates a new <see cref="ModelAttributes"/> for a <see cref="Type"/>. | /// Creates a new <see cref="ModelAttributes"/> for a <see cref="Type"/>. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="typeAttributes">The set of attributes for the <see cref="Type"/>.</param> | /// <param name="typeAttributes">The set of attributes for the <see cref="Type"/>.</param> | ||||
public ModelAttributes(IEnumerable<object> typeAttributes) { | |||||
if (typeAttributes == null) { | |||||
public ModelAttributes(IEnumerable<object> typeAttributes) | |||||
{ | |||||
if (typeAttributes == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(typeAttributes)); | throw new ArgumentNullException(nameof(typeAttributes)); | ||||
} | } | ||||
@@ -31,12 +32,15 @@ namespace Cap.Consistency.Internal | |||||
/// <param name="typeAttributes"> | /// <param name="typeAttributes"> | ||||
/// The set of attributes for the property's <see cref="Type"/>. See <see cref="PropertyInfo.PropertyType"/>. | /// The set of attributes for the property's <see cref="Type"/>. See <see cref="PropertyInfo.PropertyType"/>. | ||||
/// </param> | /// </param> | ||||
public ModelAttributes(IEnumerable<object> propertyAttributes, IEnumerable<object> typeAttributes) { | |||||
if (propertyAttributes == null) { | |||||
public ModelAttributes(IEnumerable<object> propertyAttributes, IEnumerable<object> typeAttributes) | |||||
{ | |||||
if (propertyAttributes == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(propertyAttributes)); | throw new ArgumentNullException(nameof(propertyAttributes)); | ||||
} | } | ||||
if (typeAttributes == null) { | |||||
if (typeAttributes == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(typeAttributes)); | throw new ArgumentNullException(nameof(typeAttributes)); | ||||
} | } | ||||
@@ -72,12 +76,15 @@ namespace Cap.Consistency.Internal | |||||
/// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved. | /// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved. | ||||
/// </param> | /// </param> | ||||
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns> | /// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns> | ||||
public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property) { | |||||
if (type == null) { | |||||
public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property) | |||||
{ | |||||
if (type == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(type)); | throw new ArgumentNullException(nameof(type)); | ||||
} | } | ||||
if (property == null) { | |||||
if (property == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(property)); | throw new ArgumentNullException(nameof(property)); | ||||
} | } | ||||
@@ -93,8 +100,10 @@ namespace Cap.Consistency.Internal | |||||
/// <param name="type">The <see cref="Type"/> for which attributes need to be resolved. | /// <param name="type">The <see cref="Type"/> for which attributes need to be resolved. | ||||
/// </param> | /// </param> | ||||
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="Type"/>.</returns> | /// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="Type"/>.</returns> | ||||
public static ModelAttributes GetAttributesForType(Type type) { | |||||
if (type == null) { | |||||
public static ModelAttributes GetAttributesForType(Type type) | |||||
{ | |||||
if (type == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(type)); | throw new ArgumentNullException(nameof(type)); | ||||
} | } | ||||
@@ -103,4 +112,4 @@ namespace Cap.Consistency.Internal | |||||
return new ModelAttributes(attributes); | return new ModelAttributes(attributes); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -4,10 +4,9 @@ using System.ComponentModel; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Linq.Expressions; | using System.Linq.Expressions; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Cap.Consistency.Internal | |||||
namespace DotNetCore.CAP.Internal | |||||
{ | { | ||||
public class ObjectMethodExecutor | public class ObjectMethodExecutor | ||||
{ | { | ||||
@@ -18,8 +17,10 @@ namespace Cap.Consistency.Internal | |||||
private static readonly MethodInfo _convertOfTMethod = | private static readonly MethodInfo _convertOfTMethod = | ||||
typeof(ObjectMethodExecutor).GetRuntimeMethods().Single(methodInfo => methodInfo.Name == nameof(ObjectMethodExecutor.Convert)); | typeof(ObjectMethodExecutor).GetRuntimeMethods().Single(methodInfo => methodInfo.Name == nameof(ObjectMethodExecutor.Convert)); | ||||
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) { | |||||
if (methodInfo == null) { | |||||
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) | |||||
{ | |||||
if (methodInfo == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(methodInfo)); | throw new ArgumentNullException(nameof(methodInfo)); | ||||
} | } | ||||
@@ -31,13 +32,15 @@ namespace Cap.Consistency.Internal | |||||
TaskGenericType = IsMethodAsync ? GetTaskInnerTypeOrNull(MethodReturnType) : null; | TaskGenericType = IsMethodAsync ? GetTaskInnerTypeOrNull(MethodReturnType) : null; | ||||
//IsTypeAssignableFromIActionResult = typeof(IActionResult).IsAssignableFrom(TaskGenericType ?? MethodReturnType); | //IsTypeAssignableFromIActionResult = typeof(IActionResult).IsAssignableFrom(TaskGenericType ?? MethodReturnType); | ||||
if (IsMethodAsync && TaskGenericType != null) { | |||||
if (IsMethodAsync && TaskGenericType != null) | |||||
{ | |||||
// For backwards compatibility we're creating a sync-executor for an async method. This was | // For backwards compatibility we're creating a sync-executor for an async method. This was | ||||
// supported in the past even though MVC wouldn't have called it. | // supported in the past even though MVC wouldn't have called it. | ||||
_executor = GetExecutor(methodInfo, targetTypeInfo); | _executor = GetExecutor(methodInfo, targetTypeInfo); | ||||
_executorAsync = GetExecutorAsync(TaskGenericType, methodInfo, targetTypeInfo); | _executorAsync = GetExecutorAsync(TaskGenericType, methodInfo, targetTypeInfo); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
_executor = GetExecutor(methodInfo, targetTypeInfo); | _executor = GetExecutor(methodInfo, targetTypeInfo); | ||||
} | } | ||||
@@ -65,28 +68,34 @@ namespace Cap.Consistency.Internal | |||||
//public bool IsTypeAssignableFromIActionResult { get; } | //public bool IsTypeAssignableFromIActionResult { get; } | ||||
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) { | |||||
public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) | |||||
{ | |||||
var executor = new ObjectMethodExecutor(methodInfo, targetTypeInfo); | var executor = new ObjectMethodExecutor(methodInfo, targetTypeInfo); | ||||
return executor; | return executor; | ||||
} | } | ||||
public Task<object> ExecuteAsync(object target, params object[] parameters) { | |||||
public Task<object> ExecuteAsync(object target, params object[] parameters) | |||||
{ | |||||
return _executorAsync(target, parameters); | return _executorAsync(target, parameters); | ||||
} | } | ||||
public object Execute(object target, params object[] parameters) { | |||||
public object Execute(object target, params object[] parameters) | |||||
{ | |||||
return _executor(target, parameters); | return _executor(target, parameters); | ||||
} | } | ||||
public object GetDefaultValueForParameter(int index) { | |||||
if (index < 0 || index > MethodParameters.Length - 1) { | |||||
public object GetDefaultValueForParameter(int index) | |||||
{ | |||||
if (index < 0 || index > MethodParameters.Length - 1) | |||||
{ | |||||
throw new ArgumentOutOfRangeException(nameof(index)); | throw new ArgumentOutOfRangeException(nameof(index)); | ||||
} | } | ||||
return _parameterDefaultValues[index]; | return _parameterDefaultValues[index]; | ||||
} | } | ||||
private static ConsumerMethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) { | |||||
private static ConsumerMethodExecutor GetExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) | |||||
{ | |||||
// Parameters to executor | // Parameters to executor | ||||
var targetParameter = Expression.Parameter(typeof(object), "target"); | var targetParameter = Expression.Parameter(typeof(object), "target"); | ||||
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); | var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); | ||||
@@ -94,7 +103,8 @@ namespace Cap.Consistency.Internal | |||||
// Build parameter list | // Build parameter list | ||||
var parameters = new List<Expression>(); | var parameters = new List<Expression>(); | ||||
var paramInfos = methodInfo.GetParameters(); | var paramInfos = methodInfo.GetParameters(); | ||||
for (int i = 0; i < paramInfos.Length; i++) { | |||||
for (int i = 0; i < paramInfos.Length; i++) | |||||
{ | |||||
var paramInfo = paramInfos[i]; | var paramInfo = paramInfos[i]; | ||||
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); | var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); | ||||
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); | var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); | ||||
@@ -109,12 +119,14 @@ namespace Cap.Consistency.Internal | |||||
// methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" | // methodCall is "((Ttarget) target) method((T0) parameters[0], (T1) parameters[1], ...)" | ||||
// Create function | // Create function | ||||
if (methodCall.Type == typeof(void)) { | |||||
if (methodCall.Type == typeof(void)) | |||||
{ | |||||
var lambda = Expression.Lambda<VoidActionExecutor>(methodCall, targetParameter, parametersParameter); | var lambda = Expression.Lambda<VoidActionExecutor>(methodCall, targetParameter, parametersParameter); | ||||
var voidExecutor = lambda.Compile(); | var voidExecutor = lambda.Compile(); | ||||
return WrapVoidAction(voidExecutor); | return WrapVoidAction(voidExecutor); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
// must coerce methodCall to match ActionExecutor signature | // must coerce methodCall to match ActionExecutor signature | ||||
var castMethodCall = Expression.Convert(methodCall, typeof(object)); | var castMethodCall = Expression.Convert(methodCall, typeof(object)); | ||||
var lambda = Expression.Lambda<ConsumerMethodExecutor>(castMethodCall, targetParameter, parametersParameter); | var lambda = Expression.Lambda<ConsumerMethodExecutor>(castMethodCall, targetParameter, parametersParameter); | ||||
@@ -122,14 +134,17 @@ namespace Cap.Consistency.Internal | |||||
} | } | ||||
} | } | ||||
private static ConsumerMethodExecutor WrapVoidAction(VoidActionExecutor executor) { | |||||
return delegate (object target, object[] parameters) { | |||||
private static ConsumerMethodExecutor WrapVoidAction(VoidActionExecutor executor) | |||||
{ | |||||
return delegate (object target, object[] parameters) | |||||
{ | |||||
executor(target, parameters); | executor(target, parameters); | ||||
return null; | return null; | ||||
}; | }; | ||||
} | } | ||||
private static ConsumerMethodExecutorAsync GetExecutorAsync(Type taskInnerType, MethodInfo methodInfo, TypeInfo targetTypeInfo) { | |||||
private static ConsumerMethodExecutorAsync GetExecutorAsync(Type taskInnerType, MethodInfo methodInfo, TypeInfo targetTypeInfo) | |||||
{ | |||||
// Parameters to executor | // Parameters to executor | ||||
var targetParameter = Expression.Parameter(typeof(object), "target"); | var targetParameter = Expression.Parameter(typeof(object), "target"); | ||||
var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); | var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); | ||||
@@ -137,7 +152,8 @@ namespace Cap.Consistency.Internal | |||||
// Build parameter list | // Build parameter list | ||||
var parameters = new List<Expression>(); | var parameters = new List<Expression>(); | ||||
var paramInfos = methodInfo.GetParameters(); | var paramInfos = methodInfo.GetParameters(); | ||||
for (int i = 0; i < paramInfos.Length; i++) { | |||||
for (int i = 0; i < paramInfos.Length; i++) | |||||
{ | |||||
var paramInfo = paramInfos[i]; | var paramInfo = paramInfos[i]; | ||||
var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); | var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); | ||||
var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); | var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); | ||||
@@ -162,7 +178,8 @@ namespace Cap.Consistency.Internal | |||||
private static Expression GetCoerceMethodCallExpression( | private static Expression GetCoerceMethodCallExpression( | ||||
Type taskValueType, | Type taskValueType, | ||||
MethodCallExpression methodCall, | MethodCallExpression methodCall, | ||||
MethodInfo methodInfo) { | |||||
MethodInfo methodInfo) | |||||
{ | |||||
var castMethodCall = Expression.Convert(methodCall, typeof(object)); | var castMethodCall = Expression.Convert(methodCall, typeof(object)); | ||||
// for: public Task<T> Action() | // for: public Task<T> Action() | ||||
// constructs: return (Task<object>)Convert<T>((Task<T>)result) | // constructs: return (Task<object>)Convert<T>((Task<T>)result) | ||||
@@ -175,26 +192,32 @@ namespace Cap.Consistency.Internal | |||||
/// <summary> | /// <summary> | ||||
/// Cast Task of T to Task of object | /// Cast Task of T to Task of object | ||||
/// </summary> | /// </summary> | ||||
private static async Task<object> CastToObject<T>(Task<T> task) { | |||||
private static async Task<object> CastToObject<T>(Task<T> task) | |||||
{ | |||||
return (object)await task; | return (object)await task; | ||||
} | } | ||||
private static Type GetTaskInnerTypeOrNull(Type type) { | |||||
private static Type GetTaskInnerTypeOrNull(Type type) | |||||
{ | |||||
var genericType = ExtractGenericInterface(type, typeof(Task<>)); | var genericType = ExtractGenericInterface(type, typeof(Task<>)); | ||||
return genericType?.GenericTypeArguments[0]; | return genericType?.GenericTypeArguments[0]; | ||||
} | } | ||||
public static Type ExtractGenericInterface(Type queryType, Type interfaceType) { | |||||
if (queryType == null) { | |||||
public static Type ExtractGenericInterface(Type queryType, Type interfaceType) | |||||
{ | |||||
if (queryType == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(queryType)); | throw new ArgumentNullException(nameof(queryType)); | ||||
} | } | ||||
if (interfaceType == null) { | |||||
if (interfaceType == null) | |||||
{ | |||||
throw new ArgumentNullException(nameof(interfaceType)); | throw new ArgumentNullException(nameof(interfaceType)); | ||||
} | } | ||||
if (IsGenericInstantiation(queryType, interfaceType)) { | |||||
if (IsGenericInstantiation(queryType, interfaceType)) | |||||
{ | |||||
// queryType matches (i.e. is a closed generic type created from) the open generic type. | // queryType matches (i.e. is a closed generic type created from) the open generic type. | ||||
return queryType; | return queryType; | ||||
} | } | ||||
@@ -203,75 +226,91 @@ namespace Cap.Consistency.Internal | |||||
// - If multiple different generic instantiations exists, we want the most derived one. | // - If multiple different generic instantiations exists, we want the most derived one. | ||||
// - If that doesn't break the tie, then we sort alphabetically so that it's deterministic. | // - If that doesn't break the tie, then we sort alphabetically so that it's deterministic. | ||||
// | // | ||||
// We do this by looking at interfaces on the type, and recursing to the base type | |||||
// We do this by looking at interfaces on the type, and recursing to the base type | |||||
// if we don't find any matches. | // if we don't find any matches. | ||||
return GetGenericInstantiation(queryType, interfaceType); | return GetGenericInstantiation(queryType, interfaceType); | ||||
} | } | ||||
private static bool IsGenericInstantiation(Type candidate, Type interfaceType) { | |||||
private static bool IsGenericInstantiation(Type candidate, Type interfaceType) | |||||
{ | |||||
return | return | ||||
candidate.GetTypeInfo().IsGenericType && | candidate.GetTypeInfo().IsGenericType && | ||||
candidate.GetGenericTypeDefinition() == interfaceType; | candidate.GetGenericTypeDefinition() == interfaceType; | ||||
} | } | ||||
private static Type GetGenericInstantiation(Type queryType, Type interfaceType) { | |||||
private static Type GetGenericInstantiation(Type queryType, Type interfaceType) | |||||
{ | |||||
Type bestMatch = null; | Type bestMatch = null; | ||||
var interfaces = queryType.GetInterfaces(); | var interfaces = queryType.GetInterfaces(); | ||||
foreach (var @interface in interfaces) { | |||||
if (IsGenericInstantiation(@interface, interfaceType)) { | |||||
if (bestMatch == null) { | |||||
foreach (var @interface in interfaces) | |||||
{ | |||||
if (IsGenericInstantiation(@interface, interfaceType)) | |||||
{ | |||||
if (bestMatch == null) | |||||
{ | |||||
bestMatch = @interface; | bestMatch = @interface; | ||||
} | } | ||||
else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0) { | |||||
else if (StringComparer.Ordinal.Compare(@interface.FullName, bestMatch.FullName) < 0) | |||||
{ | |||||
bestMatch = @interface; | bestMatch = @interface; | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
// There are two matches at this level of the class hierarchy, but @interface is after | // There are two matches at this level of the class hierarchy, but @interface is after | ||||
// bestMatch in the sort order. | // bestMatch in the sort order. | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (bestMatch != null) { | |||||
if (bestMatch != null) | |||||
{ | |||||
return bestMatch; | return bestMatch; | ||||
} | } | ||||
// BaseType will be null for object and interfaces, which means we've reached 'bottom'. | // BaseType will be null for object and interfaces, which means we've reached 'bottom'. | ||||
var baseType = queryType?.GetTypeInfo().BaseType; | var baseType = queryType?.GetTypeInfo().BaseType; | ||||
if (baseType == null) { | |||||
if (baseType == null) | |||||
{ | |||||
return null; | return null; | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
return GetGenericInstantiation(baseType, interfaceType); | return GetGenericInstantiation(baseType, interfaceType); | ||||
} | } | ||||
} | } | ||||
private static Task<object> Convert<T>(object taskAsObject) { | |||||
private static Task<object> Convert<T>(object taskAsObject) | |||||
{ | |||||
var task = (Task<T>)taskAsObject; | var task = (Task<T>)taskAsObject; | ||||
return CastToObject<T>(task); | return CastToObject<T>(task); | ||||
} | } | ||||
private static object[] GetParameterDefaultValues(ParameterInfo[] parameters) { | |||||
private static object[] GetParameterDefaultValues(ParameterInfo[] parameters) | |||||
{ | |||||
var values = new object[parameters.Length]; | var values = new object[parameters.Length]; | ||||
for (var i = 0; i < parameters.Length; i++) { | |||||
for (var i = 0; i < parameters.Length; i++) | |||||
{ | |||||
var parameterInfo = parameters[i]; | var parameterInfo = parameters[i]; | ||||
object defaultValue; | object defaultValue; | ||||
if (parameterInfo.HasDefaultValue) { | |||||
if (parameterInfo.HasDefaultValue) | |||||
{ | |||||
defaultValue = parameterInfo.DefaultValue; | defaultValue = parameterInfo.DefaultValue; | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
var defaultValueAttribute = parameterInfo | var defaultValueAttribute = parameterInfo | ||||
.GetCustomAttribute<DefaultValueAttribute>(inherit: false); | .GetCustomAttribute<DefaultValueAttribute>(inherit: false); | ||||
if (defaultValueAttribute?.Value == null) { | |||||
if (defaultValueAttribute?.Value == null) | |||||
{ | |||||
defaultValue = parameterInfo.ParameterType.GetTypeInfo().IsValueType | defaultValue = parameterInfo.ParameterType.GetTypeInfo().IsValueType | ||||
? Activator.CreateInstance(parameterInfo.ParameterType) | ? Activator.CreateInstance(parameterInfo.ParameterType) | ||||
: null; | : null; | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
defaultValue = defaultValueAttribute.Value; | defaultValue = defaultValueAttribute.Value; | ||||
} | } | ||||
} | } | ||||
@@ -282,4 +321,4 @@ namespace Cap.Consistency.Internal | |||||
return values; | return values; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,28 +1,30 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using NCrontab; | using NCrontab; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class ComputedCronJob | public class ComputedCronJob | ||||
{ | { | ||||
private CronJobRegistry.Entry _entry; | private CronJobRegistry.Entry _entry; | ||||
public ComputedCronJob() { | |||||
public ComputedCronJob() | |||||
{ | |||||
} | } | ||||
public ComputedCronJob(CronJob job) { | |||||
public ComputedCronJob(CronJob job) | |||||
{ | |||||
Job = job; | Job = job; | ||||
Schedule = CrontabSchedule.Parse(job.Cron); | Schedule = CrontabSchedule.Parse(job.Cron); | ||||
if (job.TypeName != null) { | |||||
if (job.TypeName != null) | |||||
{ | |||||
JobType = Type.GetType(job.TypeName); | JobType = Type.GetType(job.TypeName); | ||||
} | } | ||||
} | } | ||||
public ComputedCronJob(CronJob job, CronJobRegistry.Entry entry) | public ComputedCronJob(CronJob job, CronJobRegistry.Entry entry) | ||||
: this(job) { | |||||
: this(job) | |||||
{ | |||||
_entry = entry; | _entry = entry; | ||||
} | } | ||||
@@ -40,14 +42,16 @@ namespace Cap.Consistency.Job | |||||
public RetryBehavior RetryBehavior => _entry.RetryBehavior; | public RetryBehavior RetryBehavior => _entry.RetryBehavior; | ||||
public void Update(DateTime baseTime) { | |||||
public void Update(DateTime baseTime) | |||||
{ | |||||
Job.LastRun = baseTime; | Job.LastRun = baseTime; | ||||
} | } | ||||
public void UpdateNext(DateTime now) { | |||||
public void UpdateNext(DateTime now) | |||||
{ | |||||
var next = Schedule.GetNextOccurrence(now); | var next = Schedule.GetNextOccurrence(now); | ||||
var previousNext = Schedule.GetNextOccurrence(Job.LastRun); | var previousNext = Schedule.GetNextOccurrence(Job.LastRun); | ||||
Next = next > previousNext ? now : next; | Next = next > previousNext ? now : next; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,22 +1,22 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class Cron | public class Cron | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Returns cron expression that fires every minute. | /// Returns cron expression that fires every minute. | ||||
/// </summary> | /// </summary> | ||||
public static string Minutely() { | |||||
public static string Minutely() | |||||
{ | |||||
return "* * * * *"; | return "* * * * *"; | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Returns cron expression that fires every hour at the first minute. | /// Returns cron expression that fires every hour at the first minute. | ||||
/// </summary> | /// </summary> | ||||
public static string Hourly() { | |||||
public static string Hourly() | |||||
{ | |||||
return Hourly(minute: 0); | return Hourly(minute: 0); | ||||
} | } | ||||
@@ -24,14 +24,16 @@ namespace Cap.Consistency.Job | |||||
/// Returns cron expression that fires every hour at the specified minute. | /// Returns cron expression that fires every hour at the specified minute. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | /// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | ||||
public static string Hourly(int minute) { | |||||
public static string Hourly(int minute) | |||||
{ | |||||
return string.Format("{0} * * * *", minute); | return string.Format("{0} * * * *", minute); | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Returns cron expression that fires every day at 00:00 UTC. | /// Returns cron expression that fires every day at 00:00 UTC. | ||||
/// </summary> | /// </summary> | ||||
public static string Daily() { | |||||
public static string Daily() | |||||
{ | |||||
return Daily(hour: 0); | return Daily(hour: 0); | ||||
} | } | ||||
@@ -40,7 +42,8 @@ namespace Cap.Consistency.Job | |||||
/// the specified hour in UTC. | /// the specified hour in UTC. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
public static string Daily(int hour) { | |||||
public static string Daily(int hour) | |||||
{ | |||||
return Daily(hour, minute: 0); | return Daily(hour, minute: 0); | ||||
} | } | ||||
@@ -50,14 +53,16 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | /// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | ||||
public static string Daily(int hour, int minute) { | |||||
public static string Daily(int hour, int minute) | |||||
{ | |||||
return string.Format("{0} {1} * * *", minute, hour); | return string.Format("{0} {1} * * *", minute, hour); | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Returns cron expression that fires every week at Monday, 00:00 UTC. | /// Returns cron expression that fires every week at Monday, 00:00 UTC. | ||||
/// </summary> | /// </summary> | ||||
public static string Weekly() { | |||||
public static string Weekly() | |||||
{ | |||||
return Weekly(DayOfWeek.Monday); | return Weekly(DayOfWeek.Monday); | ||||
} | } | ||||
@@ -66,7 +71,8 @@ namespace Cap.Consistency.Job | |||||
/// day of the week. | /// day of the week. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | /// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | ||||
public static string Weekly(DayOfWeek dayOfWeek) { | |||||
public static string Weekly(DayOfWeek dayOfWeek) | |||||
{ | |||||
return Weekly(dayOfWeek, hour: 0); | return Weekly(dayOfWeek, hour: 0); | ||||
} | } | ||||
@@ -76,7 +82,8 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
/// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | /// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
public static string Weekly(DayOfWeek dayOfWeek, int hour) { | |||||
public static string Weekly(DayOfWeek dayOfWeek, int hour) | |||||
{ | |||||
return Weekly(dayOfWeek, hour, minute: 0); | return Weekly(dayOfWeek, hour, minute: 0); | ||||
} | } | ||||
@@ -87,7 +94,8 @@ namespace Cap.Consistency.Job | |||||
/// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | /// <param name="dayOfWeek">The day of week in which the schedule will be activated.</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | /// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | ||||
public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute) { | |||||
public static string Weekly(DayOfWeek dayOfWeek, int hour, int minute) | |||||
{ | |||||
return string.Format("{0} {1} * * {2}", minute, hour, (int)dayOfWeek); | return string.Format("{0} {1} * * {2}", minute, hour, (int)dayOfWeek); | ||||
} | } | ||||
@@ -95,7 +103,8 @@ namespace Cap.Consistency.Job | |||||
/// Returns cron expression that fires every month at 00:00 UTC of the first | /// Returns cron expression that fires every month at 00:00 UTC of the first | ||||
/// day of month. | /// day of month. | ||||
/// </summary> | /// </summary> | ||||
public static string Monthly() { | |||||
public static string Monthly() | |||||
{ | |||||
return Monthly(day: 1); | return Monthly(day: 1); | ||||
} | } | ||||
@@ -104,7 +113,8 @@ namespace Cap.Consistency.Job | |||||
/// day of month. | /// day of month. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
public static string Monthly(int day) { | |||||
public static string Monthly(int day) | |||||
{ | |||||
return Monthly(day, hour: 0); | return Monthly(day, hour: 0); | ||||
} | } | ||||
@@ -114,7 +124,8 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
public static string Monthly(int day, int hour) { | |||||
public static string Monthly(int day, int hour) | |||||
{ | |||||
return Monthly(day, hour, minute: 0); | return Monthly(day, hour, minute: 0); | ||||
} | } | ||||
@@ -125,14 +136,16 @@ namespace Cap.Consistency.Job | |||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | /// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | ||||
public static string Monthly(int day, int hour, int minute) { | |||||
public static string Monthly(int day, int hour, int minute) | |||||
{ | |||||
return string.Format("{0} {1} {2} * *", minute, hour, day); | return string.Format("{0} {1} {2} * *", minute, hour, day); | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Returns cron expression that fires every year on Jan, 1st at 00:00 UTC. | /// Returns cron expression that fires every year on Jan, 1st at 00:00 UTC. | ||||
/// </summary> | /// </summary> | ||||
public static string Yearly() { | |||||
public static string Yearly() | |||||
{ | |||||
return Yearly(month: 1); | return Yearly(month: 1); | ||||
} | } | ||||
@@ -141,7 +154,8 @@ namespace Cap.Consistency.Job | |||||
/// of the specified month. | /// of the specified month. | ||||
/// </summary> | /// </summary> | ||||
/// <param name="month">The month in which the schedule will be activated (1-12).</param> | /// <param name="month">The month in which the schedule will be activated (1-12).</param> | ||||
public static string Yearly(int month) { | |||||
public static string Yearly(int month) | |||||
{ | |||||
return Yearly(month, day: 1); | return Yearly(month, day: 1); | ||||
} | } | ||||
@@ -151,7 +165,8 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
/// <param name="month">The month in which the schedule will be activated (1-12).</param> | /// <param name="month">The month in which the schedule will be activated (1-12).</param> | ||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
public static string Yearly(int month, int day) { | |||||
public static string Yearly(int month, int day) | |||||
{ | |||||
return Yearly(month, day, hour: 0); | return Yearly(month, day, hour: 0); | ||||
} | } | ||||
@@ -162,7 +177,8 @@ namespace Cap.Consistency.Job | |||||
/// <param name="month">The month in which the schedule will be activated (1-12).</param> | /// <param name="month">The month in which the schedule will be activated (1-12).</param> | ||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
public static string Yearly(int month, int day, int hour) { | |||||
public static string Yearly(int month, int day, int hour) | |||||
{ | |||||
return Yearly(month, day, hour, minute: 0); | return Yearly(month, day, hour, minute: 0); | ||||
} | } | ||||
@@ -174,8 +190,9 @@ namespace Cap.Consistency.Job | |||||
/// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | /// <param name="day">The day of month in which the schedule will be activated (1-31).</param> | ||||
/// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | /// <param name="hour">The hour in which the schedule will be activated (0-23).</param> | ||||
/// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | /// <param name="minute">The minute in which the schedule will be activated (0-59).</param> | ||||
public static string Yearly(int month, int day, int hour, int minute) { | |||||
public static string Yearly(int month, int day, int hour, int minute) | |||||
{ | |||||
return string.Format("{0} {1} {2} {3} *", minute, hour, day, month); | return string.Format("{0} {1} {2} {3} *", minute, hour, day, month); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,25 +1,26 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents a cron job to be executed at specified intervals of time. | /// Represents a cron job to be executed at specified intervals of time. | ||||
/// </summary> | /// </summary> | ||||
public class CronJob | public class CronJob | ||||
{ | { | ||||
public CronJob() { | |||||
public CronJob() | |||||
{ | |||||
Id = Guid.NewGuid().ToString(); | Id = Guid.NewGuid().ToString(); | ||||
} | } | ||||
public CronJob(string cron) | public CronJob(string cron) | ||||
: this() { | |||||
: this() | |||||
{ | |||||
Cron = cron; | Cron = cron; | ||||
} | } | ||||
public CronJob(string cron, DateTime lastRun) | public CronJob(string cron, DateTime lastRun) | ||||
: this(cron) { | |||||
: this(cron) | |||||
{ | |||||
LastRun = lastRun; | LastRun = lastRun; | ||||
} | } | ||||
@@ -33,4 +34,4 @@ namespace Cap.Consistency.Job | |||||
public DateTime LastRun { get; set; } | public DateTime LastRun { get; set; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,19 +1,17 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using Cap.Consistency.Infrastructure; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class DefaultCronJobRegistry : CronJobRegistry | public class DefaultCronJobRegistry : CronJobRegistry | ||||
{ | { | ||||
private readonly ConsistencyOptions _options; | private readonly ConsistencyOptions _options; | ||||
public DefaultCronJobRegistry(IOptions<ConsistencyOptions> options) { | |||||
public DefaultCronJobRegistry(IOptions<ConsistencyOptions> options) | |||||
{ | |||||
_options = options.Value; | _options = options.Value; | ||||
RegisterJob<CapJob>(nameof(DefaultCronJobRegistry), _options.CronExp, RetryBehavior.DefaultRetry); | RegisterJob<CapJob>(nameof(DefaultCronJobRegistry), _options.CronExp, RetryBehavior.DefaultRetry); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,21 +1,22 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Reflection; | using System.Reflection; | ||||
using System.Text; | |||||
using NCrontab; | using NCrontab; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public abstract class CronJobRegistry | public abstract class CronJobRegistry | ||||
{ | { | ||||
private List<Entry> _entries; | private List<Entry> _entries; | ||||
public CronJobRegistry() { | |||||
public CronJobRegistry() | |||||
{ | |||||
_entries = new List<Entry>(); | _entries = new List<Entry>(); | ||||
} | } | ||||
protected void RegisterJob<T>(string name, string cron, RetryBehavior retryBehavior = null) | protected void RegisterJob<T>(string name, string cron, RetryBehavior retryBehavior = null) | ||||
where T : IJob { | |||||
where T : IJob | |||||
{ | |||||
RegisterJob(name, typeof(T), cron, retryBehavior); | RegisterJob(name, typeof(T), cron, retryBehavior); | ||||
} | } | ||||
@@ -26,7 +27,8 @@ namespace Cap.Consistency.Job | |||||
/// <param name="jobType">The job's type.</param> | /// <param name="jobType">The job's type.</param> | ||||
/// <param name="cron">The cron expression to use.</param> | /// <param name="cron">The cron expression to use.</param> | ||||
/// <param name="retryBehavior">The <see cref="RetryBehavior"/> to use.</param> | /// <param name="retryBehavior">The <see cref="RetryBehavior"/> to use.</param> | ||||
protected void RegisterJob(string name, Type jobType, string cron, RetryBehavior retryBehavior = null) { | |||||
protected void RegisterJob(string name, Type jobType, string cron, RetryBehavior retryBehavior = null) | |||||
{ | |||||
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException(nameof(cron)); | if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException(nameof(cron)); | ||||
if (jobType == null) throw new ArgumentNullException(nameof(jobType)); | if (jobType == null) throw new ArgumentNullException(nameof(jobType)); | ||||
if (cron == null) throw new ArgumentNullException(nameof(cron)); | if (cron == null) throw new ArgumentNullException(nameof(cron)); | ||||
@@ -34,7 +36,8 @@ namespace Cap.Consistency.Job | |||||
CrontabSchedule.TryParse(cron); | CrontabSchedule.TryParse(cron); | ||||
if (!typeof(IJob).GetTypeInfo().IsAssignableFrom(jobType)) { | |||||
if (!typeof(IJob).GetTypeInfo().IsAssignableFrom(jobType)) | |||||
{ | |||||
throw new ArgumentException( | throw new ArgumentException( | ||||
"Cron jobs should extend IJob.", nameof(jobType)); | "Cron jobs should extend IJob.", nameof(jobType)); | ||||
} | } | ||||
@@ -46,7 +49,8 @@ namespace Cap.Consistency.Job | |||||
public class Entry | public class Entry | ||||
{ | { | ||||
public Entry(string name, Type jobType, string cron) { | |||||
public Entry(string name, Type jobType, string cron) | |||||
{ | |||||
Name = name; | Name = name; | ||||
JobType = jobType; | JobType = jobType; | ||||
Cron = cron; | Cron = cron; | ||||
@@ -61,4 +65,4 @@ namespace Cap.Consistency.Job | |||||
public RetryBehavior RetryBehavior { get; set; } | public RetryBehavior RetryBehavior { get; set; } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,18 +1,15 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class CapJob : IJob | public class CapJob : IJob | ||||
{ | { | ||||
public Task ExecuteAsync() { | |||||
public Task ExecuteAsync() | |||||
{ | |||||
Console.WriteLine("当前时间:" + DateTime.Now.ToString()); | Console.WriteLine("当前时间:" + DateTime.Now.ToString()); | ||||
return Task.CompletedTask; | return Task.CompletedTask; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,9 +1,6 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using System.Threading.Tasks; | |||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public interface IJob | public interface IJob | ||||
{ | { | ||||
@@ -12,4 +9,4 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
Task ExecuteAsync(); | Task ExecuteAsync(); | ||||
} | } | ||||
} | |||||
} |
@@ -3,13 +3,13 @@ using System.Collections.Generic; | |||||
using System.Linq; | using System.Linq; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Cap.Consistency.Infrastructure; | |||||
using Cap.Consistency.Job; | |||||
using DotNetCore.CAP.Infrastructure; | |||||
using DotNetCore.CAP.Job; | |||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public class JobProcessingServer : IProcessingServer, IDisposable | public class JobProcessingServer : IProcessingServer, IDisposable | ||||
{ | { | ||||
@@ -29,8 +29,8 @@ namespace Cap.Consistency | |||||
ILoggerFactory loggerFactory, | ILoggerFactory loggerFactory, | ||||
IServiceProvider provider, | IServiceProvider provider, | ||||
DefaultCronJobRegistry defaultJobRegistry, | DefaultCronJobRegistry defaultJobRegistry, | ||||
IOptions<ConsistencyOptions> options) { | |||||
IOptions<ConsistencyOptions> options) | |||||
{ | |||||
_logger = logger; | _logger = logger; | ||||
_loggerFactory = loggerFactory; | _loggerFactory = loggerFactory; | ||||
_provider = provider; | _provider = provider; | ||||
@@ -39,8 +39,8 @@ namespace Cap.Consistency | |||||
_cts = new CancellationTokenSource(); | _cts = new CancellationTokenSource(); | ||||
} | } | ||||
public void Start() { | |||||
public void Start() | |||||
{ | |||||
var processorCount = Environment.ProcessorCount; | var processorCount = Environment.ProcessorCount; | ||||
processorCount = 1; | processorCount = 1; | ||||
_processors = GetProcessors(processorCount); | _processors = GetProcessors(processorCount); | ||||
@@ -57,40 +57,50 @@ namespace Cap.Consistency | |||||
_compositeTask = Task.WhenAll(processorTasks); | _compositeTask = Task.WhenAll(processorTasks); | ||||
} | } | ||||
public void Dispose() { | |||||
if (_disposed) { | |||||
public void Dispose() | |||||
{ | |||||
if (_disposed) | |||||
{ | |||||
return; | return; | ||||
} | } | ||||
_disposed = true; | _disposed = true; | ||||
_logger.ServerShuttingDown(); | _logger.ServerShuttingDown(); | ||||
_cts.Cancel(); | _cts.Cancel(); | ||||
try { | |||||
try | |||||
{ | |||||
_compositeTask.Wait((int)TimeSpan.FromSeconds(60).TotalMilliseconds); | _compositeTask.Wait((int)TimeSpan.FromSeconds(60).TotalMilliseconds); | ||||
} | } | ||||
catch (AggregateException ex) { | |||||
catch (AggregateException ex) | |||||
{ | |||||
var innerEx = ex.InnerExceptions[0]; | var innerEx = ex.InnerExceptions[0]; | ||||
if (!(innerEx is OperationCanceledException)) { | |||||
if (!(innerEx is OperationCanceledException)) | |||||
{ | |||||
_logger.ExpectedOperationCanceledException(innerEx); | _logger.ExpectedOperationCanceledException(innerEx); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private IJobProcessor InfiniteRetry(IJobProcessor inner) { | |||||
private IJobProcessor InfiniteRetry(IJobProcessor inner) | |||||
{ | |||||
return new InfiniteRetryProcessor(inner, _loggerFactory); | return new InfiniteRetryProcessor(inner, _loggerFactory); | ||||
} | } | ||||
private IJobProcessor[] GetProcessors(int processorCount) { | |||||
private IJobProcessor[] GetProcessors(int processorCount) | |||||
{ | |||||
var returnedProcessors = new List<IJobProcessor>(); | var returnedProcessors = new List<IJobProcessor>(); | ||||
for (int i = 0; i < processorCount; i++) { | |||||
for (int i = 0; i < processorCount; i++) | |||||
{ | |||||
var processors = _provider.GetServices<IJobProcessor>(); | var processors = _provider.GetServices<IJobProcessor>(); | ||||
foreach (var processor in processors) { | |||||
if (processor is CronJobProcessor) { | |||||
foreach (var processor in processors) | |||||
{ | |||||
if (processor is CronJobProcessor) | |||||
{ | |||||
if (i == 0) // only add first cronJob | if (i == 0) // only add first cronJob | ||||
returnedProcessors.Add(processor); | returnedProcessors.Add(processor); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
returnedProcessors.Add(processor); | returnedProcessors.Add(processor); | ||||
} | } | ||||
} | } | ||||
@@ -99,4 +109,4 @@ namespace Cap.Consistency | |||||
return returnedProcessors.ToArray(); | return returnedProcessors.ToArray(); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -2,12 +2,11 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Text; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class CronJobProcessor : IJobProcessor | public class CronJobProcessor : IJobProcessor | ||||
{ | { | ||||
@@ -18,8 +17,8 @@ namespace Cap.Consistency.Job | |||||
public CronJobProcessor( | public CronJobProcessor( | ||||
DefaultCronJobRegistry jobRegistry, | DefaultCronJobRegistry jobRegistry, | ||||
ILogger<CronJobProcessor> logger, | ILogger<CronJobProcessor> logger, | ||||
IServiceProvider provider) { | |||||
IServiceProvider provider) | |||||
{ | |||||
_jobRegistry = jobRegistry; | _jobRegistry = jobRegistry; | ||||
_logger = logger; | _logger = logger; | ||||
_provider = provider; | _provider = provider; | ||||
@@ -27,17 +26,20 @@ namespace Cap.Consistency.Job | |||||
public override string ToString() => nameof(CronJobProcessor); | public override string ToString() => nameof(CronJobProcessor); | ||||
public Task ProcessAsync(ProcessingContext context) { | |||||
public Task ProcessAsync(ProcessingContext context) | |||||
{ | |||||
if (context == null) throw new ArgumentNullException(nameof(context)); | if (context == null) throw new ArgumentNullException(nameof(context)); | ||||
return ProcessCoreAsync(context); | return ProcessCoreAsync(context); | ||||
} | } | ||||
private async Task ProcessCoreAsync(ProcessingContext context) { | |||||
private async Task ProcessCoreAsync(ProcessingContext context) | |||||
{ | |||||
//var storage = context.Storage; | //var storage = context.Storage; | ||||
//var jobs = await GetJobsAsync(storage); | //var jobs = await GetJobsAsync(storage); | ||||
var jobs = GetJobs(); | var jobs = GetJobs(); | ||||
if (!jobs.Any()) { | |||||
if (!jobs.Any()) | |||||
{ | |||||
_logger.CronJobsNotFound(); | _logger.CronJobsNotFound(); | ||||
// This will cancel this processor. | // This will cancel this processor. | ||||
@@ -48,52 +50,61 @@ namespace Cap.Consistency.Job | |||||
context.ThrowIfStopping(); | context.ThrowIfStopping(); | ||||
var computedJobs = Compute(jobs, context.CronJobRegistry.Build()); | var computedJobs = Compute(jobs, context.CronJobRegistry.Build()); | ||||
if (context.IsStopping) { | |||||
if (context.IsStopping) | |||||
{ | |||||
return; | return; | ||||
} | } | ||||
await Task.WhenAll(computedJobs.Select(j => RunAsync(j, context))); | await Task.WhenAll(computedJobs.Select(j => RunAsync(j, context))); | ||||
} | } | ||||
private async Task RunAsync(ComputedCronJob computedJob, ProcessingContext context) { | |||||
private async Task RunAsync(ComputedCronJob computedJob, ProcessingContext context) | |||||
{ | |||||
//var storage = context.Storage; | //var storage = context.Storage; | ||||
var retryBehavior = computedJob.RetryBehavior; | var retryBehavior = computedJob.RetryBehavior; | ||||
while (!context.IsStopping) { | |||||
while (!context.IsStopping) | |||||
{ | |||||
var now = DateTime.UtcNow; | var now = DateTime.UtcNow; | ||||
var due = ComputeDue(computedJob, now); | var due = ComputeDue(computedJob, now); | ||||
var timeSpan = due - now; | var timeSpan = due - now; | ||||
if (timeSpan.TotalSeconds > 0) { | |||||
if (timeSpan.TotalSeconds > 0) | |||||
{ | |||||
await context.WaitAsync(timeSpan); | await context.WaitAsync(timeSpan); | ||||
} | } | ||||
context.ThrowIfStopping(); | context.ThrowIfStopping(); | ||||
using (var scopedContext = context.CreateScope()) { | |||||
using (var scopedContext = context.CreateScope()) | |||||
{ | |||||
var provider = scopedContext.Provider; | var provider = scopedContext.Provider; | ||||
var job = provider.GetService<IJob>(); | var job = provider.GetService<IJob>(); | ||||
var success = true; | var success = true; | ||||
try { | |||||
try | |||||
{ | |||||
var sw = Stopwatch.StartNew(); | var sw = Stopwatch.StartNew(); | ||||
await job.ExecuteAsync(); | await job.ExecuteAsync(); | ||||
sw.Stop(); | sw.Stop(); | ||||
computedJob.Retries = 0; | computedJob.Retries = 0; | ||||
_logger.CronJobExecuted(computedJob.Job.Name, sw.Elapsed.TotalSeconds); | _logger.CronJobExecuted(computedJob.Job.Name, sw.Elapsed.TotalSeconds); | ||||
} | } | ||||
catch (Exception ex) { | |||||
catch (Exception ex) | |||||
{ | |||||
success = false; | success = false; | ||||
if (computedJob.Retries == 0) { | |||||
if (computedJob.Retries == 0) | |||||
{ | |||||
computedJob.FirstTry = DateTime.UtcNow; | computedJob.FirstTry = DateTime.UtcNow; | ||||
} | } | ||||
computedJob.Retries++; | computedJob.Retries++; | ||||
_logger.CronJobFailed(computedJob.Job.Name, ex); | _logger.CronJobFailed(computedJob.Job.Name, ex); | ||||
} | } | ||||
if (success) { | |||||
if (success) | |||||
{ | |||||
//var connection = provider.GetRequiredService<IStorageConnection>(); | //var connection = provider.GetRequiredService<IStorageConnection>(); | ||||
//await connection.AttachCronJobAsync(computedJob.Job); | //await connection.AttachCronJobAsync(computedJob.Job); | ||||
@@ -105,24 +116,28 @@ namespace Cap.Consistency.Job | |||||
} | } | ||||
} | } | ||||
private DateTime ComputeDue(ComputedCronJob computedJob, DateTime now) { | |||||
private DateTime ComputeDue(ComputedCronJob computedJob, DateTime now) | |||||
{ | |||||
computedJob.UpdateNext(now); | computedJob.UpdateNext(now); | ||||
var retryBehavior = computedJob.RetryBehavior ?? RetryBehavior.DefaultRetry; | var retryBehavior = computedJob.RetryBehavior ?? RetryBehavior.DefaultRetry; | ||||
var retries = computedJob.Retries; | var retries = computedJob.Retries; | ||||
if (retries == 0) { | |||||
if (retries == 0) | |||||
{ | |||||
return computedJob.Next; | return computedJob.Next; | ||||
} | } | ||||
var realNext = computedJob.Schedule.GetNextOccurrence(now); | var realNext = computedJob.Schedule.GetNextOccurrence(now); | ||||
if (!retryBehavior.Retry) { | |||||
if (!retryBehavior.Retry) | |||||
{ | |||||
// No retry. If job failed before, we don't care, just schedule it next as usual. | // No retry. If job failed before, we don't care, just schedule it next as usual. | ||||
return realNext; | return realNext; | ||||
} | } | ||||
if (retries >= retryBehavior.RetryCount) { | |||||
if (retries >= retryBehavior.RetryCount) | |||||
{ | |||||
// Max retries. Just schedule it for the next occurance. | // Max retries. Just schedule it for the next occurance. | ||||
return realNext; | return realNext; | ||||
} | } | ||||
@@ -131,11 +146,14 @@ namespace Cap.Consistency.Job | |||||
return computedJob.FirstTry.AddSeconds(retryBehavior.RetryIn(retries)); | return computedJob.FirstTry.AddSeconds(retryBehavior.RetryIn(retries)); | ||||
} | } | ||||
private CronJob[] GetJobs() { | |||||
private CronJob[] GetJobs() | |||||
{ | |||||
var cronJobs = new List<CronJob>(); | var cronJobs = new List<CronJob>(); | ||||
var entries = _jobRegistry.Build() ?? new CronJobRegistry.Entry[0]; | var entries = _jobRegistry.Build() ?? new CronJobRegistry.Entry[0]; | ||||
foreach (var entry in entries) { | |||||
cronJobs.Add(new CronJob { | |||||
foreach (var entry in entries) | |||||
{ | |||||
cronJobs.Add(new CronJob | |||||
{ | |||||
Name = entry.Name, | Name = entry.Name, | ||||
TypeName = entry.JobType.AssemblyQualifiedName, | TypeName = entry.JobType.AssemblyQualifiedName, | ||||
Cron = entry.Cron, | Cron = entry.Cron, | ||||
@@ -148,9 +166,10 @@ namespace Cap.Consistency.Job | |||||
private ComputedCronJob[] Compute(IEnumerable<CronJob> jobs, CronJobRegistry.Entry[] entries) | private ComputedCronJob[] Compute(IEnumerable<CronJob> jobs, CronJobRegistry.Entry[] entries) | ||||
=> jobs.Select(j => CreateComputedCronJob(j, entries)).ToArray(); | => jobs.Select(j => CreateComputedCronJob(j, entries)).ToArray(); | ||||
private ComputedCronJob CreateComputedCronJob(CronJob job, CronJobRegistry.Entry[] entries) { | |||||
private ComputedCronJob CreateComputedCronJob(CronJob job, CronJobRegistry.Entry[] entries) | |||||
{ | |||||
var entry = entries.First(e => e.Name == job.Name); | var entry = entries.First(e => e.Name == job.Name); | ||||
return new ComputedCronJob(job, entry); | return new ComputedCronJob(job, entry); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,10 +1,8 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class InfiniteRetryProcessor : IJobProcessor | public class InfiniteRetryProcessor : IJobProcessor | ||||
{ | { | ||||
@@ -13,22 +11,28 @@ namespace Cap.Consistency.Job | |||||
public InfiniteRetryProcessor( | public InfiniteRetryProcessor( | ||||
IJobProcessor inner, | IJobProcessor inner, | ||||
ILoggerFactory loggerFactory) { | |||||
ILoggerFactory loggerFactory) | |||||
{ | |||||
_inner = inner; | _inner = inner; | ||||
_logger = loggerFactory.CreateLogger<InfiniteRetryProcessor>(); | _logger = loggerFactory.CreateLogger<InfiniteRetryProcessor>(); | ||||
} | } | ||||
public override string ToString() => _inner.ToString(); | public override string ToString() => _inner.ToString(); | ||||
public async Task ProcessAsync(ProcessingContext context) { | |||||
while (!context.IsStopping) { | |||||
try { | |||||
public async Task ProcessAsync(ProcessingContext context) | |||||
{ | |||||
while (!context.IsStopping) | |||||
{ | |||||
try | |||||
{ | |||||
await _inner.ProcessAsync(context); | await _inner.ProcessAsync(context); | ||||
} | } | ||||
catch (OperationCanceledException) { | |||||
catch (OperationCanceledException) | |||||
{ | |||||
return; | return; | ||||
} | } | ||||
catch (Exception ex) { | |||||
catch (Exception ex) | |||||
{ | |||||
_logger.LogWarning( | _logger.LogWarning( | ||||
1, | 1, | ||||
ex, | ex, | ||||
@@ -37,4 +41,4 @@ namespace Cap.Consistency.Job | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
using System.Threading.Tasks; | |||||
namespace DotNetCore.CAP.Job | |||||
{ | |||||
public interface IJobProcessor | |||||
{ | |||||
Task ProcessAsync(ProcessingContext context); | |||||
} | |||||
} |
@@ -1,31 +1,32 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class ProcessingContext : IDisposable | public class ProcessingContext : IDisposable | ||||
{ | { | ||||
private IServiceScope _scope; | private IServiceScope _scope; | ||||
private ProcessingContext(ProcessingContext other) { | |||||
private ProcessingContext(ProcessingContext other) | |||||
{ | |||||
Provider = other.Provider; | Provider = other.Provider; | ||||
//Storage = other.Storage; | //Storage = other.Storage; | ||||
CronJobRegistry = other.CronJobRegistry; | CronJobRegistry = other.CronJobRegistry; | ||||
CancellationToken = other.CancellationToken; | CancellationToken = other.CancellationToken; | ||||
} | } | ||||
public ProcessingContext() { | |||||
public ProcessingContext() | |||||
{ | |||||
} | } | ||||
public ProcessingContext( | public ProcessingContext( | ||||
IServiceProvider provider, | IServiceProvider provider, | ||||
//IStorage storage, | //IStorage storage, | ||||
CronJobRegistry cronJobRegistry, | CronJobRegistry cronJobRegistry, | ||||
CancellationToken cancellationToken) { | |||||
CancellationToken cancellationToken) | |||||
{ | |||||
Provider = provider; | Provider = provider; | ||||
//Storage = storage; | //Storage = storage; | ||||
CronJobRegistry = cronJobRegistry; | CronJobRegistry = cronJobRegistry; | ||||
@@ -44,23 +45,28 @@ namespace Cap.Consistency.Job | |||||
public void ThrowIfStopping() => CancellationToken.ThrowIfCancellationRequested(); | public void ThrowIfStopping() => CancellationToken.ThrowIfCancellationRequested(); | ||||
public ProcessingContext CreateScope() { | |||||
public ProcessingContext CreateScope() | |||||
{ | |||||
var serviceScope = Provider.CreateScope(); | var serviceScope = Provider.CreateScope(); | ||||
return new ProcessingContext(this) { | |||||
return new ProcessingContext(this) | |||||
{ | |||||
_scope = serviceScope, | _scope = serviceScope, | ||||
Provider = serviceScope.ServiceProvider | Provider = serviceScope.ServiceProvider | ||||
}; | }; | ||||
} | } | ||||
public Task WaitAsync(TimeSpan timeout) { | |||||
public Task WaitAsync(TimeSpan timeout) | |||||
{ | |||||
return Task.Delay(timeout, CancellationToken); | return Task.Delay(timeout, CancellationToken); | ||||
} | } | ||||
public void Dispose() { | |||||
if (_scope != null) { | |||||
public void Dispose() | |||||
{ | |||||
if (_scope != null) | |||||
{ | |||||
_scope.Dispose(); | _scope.Dispose(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,8 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Cap.Consistency.Job | |||||
namespace DotNetCore.CAP.Job | |||||
{ | { | ||||
public class RetryBehavior | public class RetryBehavior | ||||
{ | { | ||||
@@ -16,7 +14,8 @@ namespace Cap.Consistency.Job | |||||
private Func<int, int> _retryInThunk; | private Func<int, int> _retryInThunk; | ||||
static RetryBehavior() { | |||||
static RetryBehavior() | |||||
{ | |||||
DefaultRetryCount = 25; | DefaultRetryCount = 25; | ||||
DefaultRetryInThunk = retries => | DefaultRetryInThunk = retries => | ||||
(int)Math.Round(Math.Pow(retries - 1, 4) + 15 + (_random.Next(30) * (retries))); | (int)Math.Round(Math.Pow(retries - 1, 4) + 15 + (_random.Next(30) * (retries))); | ||||
@@ -26,7 +25,8 @@ namespace Cap.Consistency.Job | |||||
} | } | ||||
public RetryBehavior(bool retry) | public RetryBehavior(bool retry) | ||||
: this(retry, DefaultRetryCount, DefaultRetryInThunk) { | |||||
: this(retry, DefaultRetryCount, DefaultRetryInThunk) | |||||
{ | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
@@ -35,8 +35,10 @@ namespace Cap.Consistency.Job | |||||
/// <param name="retry">Whether to retry.</param> | /// <param name="retry">Whether to retry.</param> | ||||
/// <param name="retryCount">The maximum retry count.</param> | /// <param name="retryCount">The maximum retry count.</param> | ||||
/// <param name="retryInThunk">The retry in function to use.</param> | /// <param name="retryInThunk">The retry in function to use.</param> | ||||
public RetryBehavior(bool retry, int retryCount, Func<int, int> retryInThunk) { | |||||
if (retry) { | |||||
public RetryBehavior(bool retry, int retryCount, Func<int, int> retryInThunk) | |||||
{ | |||||
if (retry) | |||||
{ | |||||
if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); | if (retryCount < 0) throw new ArgumentOutOfRangeException(nameof(retryCount), "Can't be negative."); | ||||
} | } | ||||
@@ -62,8 +64,9 @@ namespace Cap.Consistency.Job | |||||
/// </summary> | /// </summary> | ||||
/// <param name="retries">The current retry count.</param> | /// <param name="retries">The current retry count.</param> | ||||
/// <returns>The seconds to delay.</returns> | /// <returns>The seconds to delay.</returns> | ||||
public int RetryIn(int retries) { | |||||
public int RetryIn(int retries) | |||||
{ | |||||
return _retryInThunk(retries); | return _retryInThunk(retries); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,10 +1,10 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using Cap.Consistency.Job; | |||||
using DotNetCore.CAP.Job; | |||||
using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
internal static class LoggerExtensions | internal static class LoggerExtensions | ||||
{ | { | ||||
@@ -17,7 +17,8 @@ namespace Cap.Consistency | |||||
private static Action<ILogger, string, double, Exception> _cronJobExecuted; | private static Action<ILogger, string, double, Exception> _cronJobExecuted; | ||||
private static Action<ILogger, string, Exception> _cronJobFailed; | private static Action<ILogger, string, Exception> _cronJobFailed; | ||||
static LoggerExtensions() { | |||||
static LoggerExtensions() | |||||
{ | |||||
_serverStarting = LoggerMessage.Define<int, int>( | _serverStarting = LoggerMessage.Define<int, int>( | ||||
LogLevel.Debug, | LogLevel.Debug, | ||||
1, | 1, | ||||
@@ -52,37 +53,41 @@ namespace Cap.Consistency | |||||
LogLevel.Warning, | LogLevel.Warning, | ||||
4, | 4, | ||||
"Cron job '{jobName}' failed to execute."); | "Cron job '{jobName}' failed to execute."); | ||||
} | } | ||||
public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount) { | |||||
public static void ServerStarting(this ILogger logger, int machineProcessorCount, int processorCount) | |||||
{ | |||||
_serverStarting(logger, machineProcessorCount, processorCount, null); | _serverStarting(logger, machineProcessorCount, processorCount, null); | ||||
} | } | ||||
public static void ServerShuttingDown(this ILogger logger) { | |||||
public static void ServerShuttingDown(this ILogger logger) | |||||
{ | |||||
_serverShuttingDown(logger, null); | _serverShuttingDown(logger, null); | ||||
} | } | ||||
public static void ExpectedOperationCanceledException(this ILogger logger, Exception ex) { | |||||
public static void ExpectedOperationCanceledException(this ILogger logger, Exception ex) | |||||
{ | |||||
_expectedOperationCanceledException(logger, ex.Message, ex); | _expectedOperationCanceledException(logger, ex.Message, ex); | ||||
} | } | ||||
public static void CronJobsNotFound(this ILogger logger) { | |||||
public static void CronJobsNotFound(this ILogger logger) | |||||
{ | |||||
_cronJobsNotFound(logger, null); | _cronJobsNotFound(logger, null); | ||||
} | } | ||||
public static void CronJobsScheduling(this ILogger logger, IEnumerable<CronJob> jobs) { | |||||
public static void CronJobsScheduling(this ILogger logger, IEnumerable<CronJob> jobs) | |||||
{ | |||||
_cronJobsScheduling(logger, jobs.Count(), null); | _cronJobsScheduling(logger, jobs.Count(), null); | ||||
} | } | ||||
public static void CronJobExecuted(this ILogger logger, string name, double seconds) { | |||||
public static void CronJobExecuted(this ILogger logger, string name, double seconds) | |||||
{ | |||||
_cronJobExecuted(logger, name, seconds, null); | _cronJobExecuted(logger, name, seconds, null); | ||||
} | } | ||||
public static void CronJobFailed(this ILogger logger, string name, Exception ex) { | |||||
public static void CronJobFailed(this ILogger logger, string name, Exception ex) | |||||
{ | |||||
_cronJobFailed(logger, name, ex); | _cronJobFailed(logger, name, ex); | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,7 +1,7 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// Represents the result of an consistent message operation. | /// Represents the result of an consistent message operation. | ||||
@@ -37,9 +37,11 @@ namespace Cap.Consistency | |||||
/// </summary> | /// </summary> | ||||
/// <param name="errors">An optional array of <see cref="OperateError"/>s which caused the operation to fail.</param> | /// <param name="errors">An optional array of <see cref="OperateError"/>s which caused the operation to fail.</param> | ||||
/// <returns>An <see cref="OperateResult"/> indicating a failed operation, with a list of <paramref name="errors"/> if applicable.</returns> | /// <returns>An <see cref="OperateResult"/> indicating a failed operation, with a list of <paramref name="errors"/> if applicable.</returns> | ||||
public static OperateResult Failed(params OperateError[] errors) { | |||||
public static OperateResult Failed(params OperateError[] errors) | |||||
{ | |||||
var result = new OperateResult { Succeeded = false }; | var result = new OperateResult { Succeeded = false }; | ||||
if (errors != null) { | |||||
if (errors != null) | |||||
{ | |||||
result._errors.AddRange(errors); | result._errors.AddRange(errors); | ||||
} | } | ||||
return result; | return result; | ||||
@@ -53,7 +55,8 @@ namespace Cap.Consistency | |||||
/// If the operation was successful the ToString() will return "Succeeded" otherwise it returned | /// If the operation was successful the ToString() will return "Succeeded" otherwise it returned | ||||
/// "Failed : " followed by a comma delimited list of error codes from its <see cref="Errors"/> collection, if any. | /// "Failed : " followed by a comma delimited list of error codes from its <see cref="Errors"/> collection, if any. | ||||
/// </remarks> | /// </remarks> | ||||
public override string ToString() { | |||||
public override string ToString() | |||||
{ | |||||
return Succeeded ? | return Succeeded ? | ||||
"Succeeded" : | "Succeeded" : | ||||
string.Format("{0} : {1}", "Failed", string.Join(",", Errors.Select(x => x.Code).ToList())); | string.Format("{0} : {1}", "Failed", string.Join(",", Errors.Select(x => x.Code).ToList())); |
@@ -1,22 +1,22 @@ | |||||
using System; | using System; | ||||
using System.Threading; | using System.Threading; | ||||
namespace Cap.Consistency | |||||
namespace DotNetCore.CAP | |||||
{ | { | ||||
public class TopicContext | public class TopicContext | ||||
{ | { | ||||
public TopicContext() { | |||||
public TopicContext() | |||||
{ | |||||
} | } | ||||
public TopicContext(IServiceProvider provider, CancellationToken cancellationToken) { | |||||
public TopicContext(IServiceProvider provider, CancellationToken cancellationToken) | |||||
{ | |||||
ServiceProvider = provider; | ServiceProvider = provider; | ||||
CancellationToken = cancellationToken; | CancellationToken = cancellationToken; | ||||
} | } | ||||
public IServiceProvider ServiceProvider { get; set; } | public IServiceProvider ServiceProvider { get; set; } | ||||
public CancellationToken CancellationToken { get; } | public CancellationToken CancellationToken { get; } | ||||
} | } | ||||
} | |||||
} |
@@ -1,18 +0,0 @@ | |||||
using System.Reflection; | |||||
using System.Runtime.InteropServices; | |||||
// General Information about an assembly is controlled through the following | |||||
// set of attributes. Change these attribute values to modify the information | |||||
// associated with an assembly. | |||||
[assembly: AssemblyConfiguration("")] | |||||
[assembly: AssemblyCompany("")] | |||||
[assembly: AssemblyProduct("Cap.Consistency.EntityFrameworkCore.Test")] | |||||
[assembly: AssemblyTrademark("")] | |||||
// Setting ComVisible to false makes the types in this assembly not visible | |||||
// to COM components. If you need to access a type in this assembly from | |||||
// COM, set the ComVisible attribute to true on that type. | |||||
[assembly: ComVisible(false)] | |||||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||||
[assembly: Guid("7442c942-1ddc-40e4-8f1b-654e721eaa45")] |
@@ -2,7 +2,7 @@ | |||||
using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
{ | { | ||||
public static class DbUtil | public static class DbUtil | ||||
{ | { |
@@ -1,13 +1,13 @@ | |||||
//using System.Threading.Tasks; | //using System.Threading.Tasks; | ||||
//using Cap.Consistency.Infrastructure; | |||||
//using Cap.Consistency.Store; | |||||
//using DotNetCore.CAP.Infrastructure; | |||||
//using DotNetCore.CAP.Store; | |||||
//using Microsoft.AspNetCore.Builder.Internal; | //using Microsoft.AspNetCore.Builder.Internal; | ||||
//using Microsoft.AspNetCore.Testing.xunit; | //using Microsoft.AspNetCore.Testing.xunit; | ||||
//using Microsoft.EntityFrameworkCore; | //using Microsoft.EntityFrameworkCore; | ||||
//using Microsoft.Extensions.DependencyInjection; | //using Microsoft.Extensions.DependencyInjection; | ||||
//using Xunit; | //using Xunit; | ||||
//namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
//namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
//{ | //{ | ||||
// public class DefaultPocoTest : IClassFixture<ScratchDatabaseFixture> | // public class DefaultPocoTest : IClassFixture<ScratchDatabaseFixture> | ||||
// { | // { |
@@ -3,8 +3,8 @@ | |||||
<PropertyGroup> | <PropertyGroup> | ||||
<TargetFramework>netcoreapp1.1</TargetFramework> | <TargetFramework>netcoreapp1.1</TargetFramework> | ||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> | <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||||
<AssemblyName>Cap.Consistency.EntityFrameworkCore.Test</AssemblyName> | |||||
<PackageId>Cap.Consistency.EntityFrameworkCore.Test</PackageId> | |||||
<AssemblyName>DotNetCore.CAP.EntityFrameworkCore.Test</AssemblyName> | |||||
<PackageId>DotNetCore.CAP.EntityFrameworkCore.Test</PackageId> | |||||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> | <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> | ||||
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net451+win8</PackageTargetFallback> | <PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net451+win8</PackageTargetFallback> | ||||
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion> | <RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion> | ||||
@@ -18,8 +18,8 @@ | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ProjectReference Include="..\..\src\Cap.Consistency\Cap.Consistency.csproj" /> | |||||
<ProjectReference Include="..\..\src\Cap.Consistency.EntityFrameworkCore\Cap.Consistency.EntityFrameworkCore.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.EntityFrameworkCore\DotNetCore.CAP.EntityFrameworkCore.csproj" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> |
@@ -1,16 +1,16 @@ | |||||
//using System; | //using System; | ||||
//using System.Linq; | //using System.Linq; | ||||
//using System.Threading.Tasks; | //using System.Threading.Tasks; | ||||
//using Cap.Consistency.Infrastructure; | |||||
//using Cap.Consistency.Store; | |||||
//using Cap.Consistency.Test; | |||||
//using DotNetCore.CAP.Infrastructure; | |||||
//using DotNetCore.CAP.Store; | |||||
//using DotNetCore.CAP.Test; | |||||
//using Microsoft.AspNetCore.Testing; | //using Microsoft.AspNetCore.Testing; | ||||
//using Microsoft.AspNetCore.Testing.xunit; | //using Microsoft.AspNetCore.Testing.xunit; | ||||
//using Microsoft.EntityFrameworkCore; | //using Microsoft.EntityFrameworkCore; | ||||
//using Microsoft.Extensions.DependencyInjection; | //using Microsoft.Extensions.DependencyInjection; | ||||
//using Xunit; | //using Xunit; | ||||
//namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
//namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
//{ | //{ | ||||
// public class MessageStoreTest : MessageManagerTestBase<ConsistencyMessage>, IClassFixture<ScratchDatabaseFixture> | // public class MessageStoreTest : MessageManagerTestBase<ConsistencyMessage>, IClassFixture<ScratchDatabaseFixture> | ||||
// { | // { |
@@ -1,12 +1,12 @@ | |||||
//using System; | //using System; | ||||
//using Cap.Consistency.Infrastructure; | |||||
//using Cap.Consistency.Store; | |||||
//using Cap.Consistency.Test; | |||||
//using DotNetCore.CAP.Infrastructure; | |||||
//using DotNetCore.CAP.Store; | |||||
//using DotNetCore.CAP.Test; | |||||
//using Microsoft.AspNetCore.Testing; | //using Microsoft.AspNetCore.Testing; | ||||
//using Microsoft.Extensions.DependencyInjection; | //using Microsoft.Extensions.DependencyInjection; | ||||
//using Xunit; | //using Xunit; | ||||
//namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
//namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
//{ | //{ | ||||
// public class MessageStoreWithGenericsTest : MessageManagerTestBase<MessageWithGenerics, string>, IClassFixture<ScratchDatabaseFixture> | // public class MessageStoreWithGenericsTest : MessageManagerTestBase<MessageWithGenerics, string>, IClassFixture<ScratchDatabaseFixture> | ||||
// { | // { |
@@ -6,7 +6,7 @@ using System.Runtime.InteropServices; | |||||
// associated with an assembly. | // associated with an assembly. | ||||
[assembly: AssemblyConfiguration("")] | [assembly: AssemblyConfiguration("")] | ||||
[assembly: AssemblyCompany("")] | [assembly: AssemblyCompany("")] | ||||
[assembly: AssemblyProduct("Cap.Consistency.EntityFrameworkCore")] | |||||
[assembly: AssemblyProduct("DotNetCore.CAP.EntityFrameworkCore.Test")] | |||||
[assembly: AssemblyTrademark("")] | [assembly: AssemblyTrademark("")] | ||||
// Setting ComVisible to false makes the types in this assembly not visible | // Setting ComVisible to false makes the types in this assembly not visible | ||||
@@ -15,4 +15,4 @@ using System.Runtime.InteropServices; | |||||
[assembly: ComVisible(false)] | [assembly: ComVisible(false)] | ||||
// The following GUID is for the ID of the typelib if this project is exposed to COM | // The following GUID is for the ID of the typelib if this project is exposed to COM | ||||
[assembly: Guid("96111249-c4c3-4dc9-a887-32d583723ab1")] | |||||
[assembly: Guid("7442c942-1ddc-40e4-8f1b-654e721eaa45")] |
@@ -1,7 +1,7 @@ | |||||
using System; | using System; | ||||
using Microsoft.EntityFrameworkCore.Internal; | using Microsoft.EntityFrameworkCore.Internal; | ||||
namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
{ | { | ||||
public class ScratchDatabaseFixture : IDisposable | public class ScratchDatabaseFixture : IDisposable | ||||
{ | { |
@@ -4,7 +4,7 @@ using System.Data.SqlClient; | |||||
using System.IO; | using System.IO; | ||||
using System.Threading; | using System.Threading; | ||||
namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
{ | { | ||||
public class SqlServerTestStore : IDisposable | public class SqlServerTestStore : IDisposable | ||||
{ | { |
@@ -1,7 +1,7 @@ | |||||
using System.IO; | using System.IO; | ||||
using Microsoft.Extensions.Configuration; | using Microsoft.Extensions.Configuration; | ||||
namespace Cap.Consistency.EntityFrameworkCore.Test | |||||
namespace DotNetCore.CAP.EntityFrameworkCore.Test | |||||
{ | { | ||||
public class TestEnvironment | public class TestEnvironment | ||||
{ | { |
@@ -1,12 +1,12 @@ | |||||
//using System; | //using System; | ||||
//using System.Threading; | //using System.Threading; | ||||
//using System.Threading.Tasks; | //using System.Threading.Tasks; | ||||
//using Cap.Consistency.Infrastructure; | |||||
//using Cap.Consistency.Store; | |||||
//using DotNetCore.CAP.Infrastructure; | |||||
//using DotNetCore.CAP.Store; | |||||
//using Microsoft.Extensions.DependencyInjection; | //using Microsoft.Extensions.DependencyInjection; | ||||
//using Xunit; | //using Xunit; | ||||
//namespace Cap.Consistency.Test | |||||
//namespace DotNetCore.CAP.Test | |||||
//{ | //{ | ||||
// public class ConsistencyBuilderTest | // public class ConsistencyBuilderTest | ||||
// { | // { |
@@ -1,15 +1,15 @@ | |||||
//using System; | //using System; | ||||
//using System.Threading; | //using System.Threading; | ||||
//using System.Threading.Tasks; | //using System.Threading.Tasks; | ||||
//using Cap.Consistency.Infrastructure; | |||||
//using Cap.Consistency.Store; | |||||
//using DotNetCore.CAP.Infrastructure; | |||||
//using DotNetCore.CAP.Store; | |||||
//using Microsoft.AspNetCore.Http; | //using Microsoft.AspNetCore.Http; | ||||
//using Microsoft.Extensions.DependencyInjection; | //using Microsoft.Extensions.DependencyInjection; | ||||
//using Microsoft.Extensions.Logging; | //using Microsoft.Extensions.Logging; | ||||
//using Moq; | //using Moq; | ||||
//using Xunit; | //using Xunit; | ||||
//namespace Cap.Consistency.Test | |||||
//namespace DotNetCore.CAP.Test | |||||
//{ | //{ | ||||
// public class ConsistencyMessageManagerTest | // public class ConsistencyMessageManagerTest | ||||
// { | // { |