Removes sample project. Co-authored-by: Dave Smith <dave.smith@ventivtech.com>master
@@ -71,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\ | |||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.Postgres.DashboardAuth", "samples\Sample.RabbitMQ.Postgres.DashboardAuth\Sample.RabbitMQ.Postgres.DashboardAuth.csproj", "{54F6C206-2A23-4971-AE5A-FC47EB772452}" | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.RabbitMQ.Postgres.DashboardAuth", "samples\Sample.RabbitMQ.Postgres.DashboardAuth\Sample.RabbitMQ.Postgres.DashboardAuth.csproj", "{54F6C206-2A23-4971-AE5A-FC47EB772452}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyConsumerSelector", "samples\MyConsumerSelector\MyConsumerSelector.csproj", "{1AE86784-0B64-4A73-8D81-D913C6E33D7D}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -167,10 +165,6 @@ Global | |||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Debug|Any CPU.Build.0 = Debug|Any CPU | {54F6C206-2A23-4971-AE5A-FC47EB772452}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.ActiveCfg = Release|Any CPU | {54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
{54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.Build.0 = Release|Any CPU | {54F6C206-2A23-4971-AE5A-FC47EB772452}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
{1AE86784-0B64-4A73-8D81-D913C6E33D7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{1AE86784-0B64-4A73-8D81-D913C6E33D7D}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{1AE86784-0B64-4A73-8D81-D913C6E33D7D}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{1AE86784-0B64-4A73-8D81-D913C6E33D7D}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
@@ -198,7 +192,6 @@ Global | |||||
{B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} | {B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} | ||||
{8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} | ||||
{54F6C206-2A23-4971-AE5A-FC47EB772452} = {3A6B6931-A123-477A-9469-8B468B5385AF} | {54F6C206-2A23-4971-AE5A-FC47EB772452} = {3A6B6931-A123-477A-9469-8B468B5385AF} | ||||
{1AE86784-0B64-4A73-8D81-D913C6E33D7D} = {3A6B6931-A123-477A-9469-8B468B5385AF} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} | SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} | ||||
@@ -1,47 +0,0 @@ | |||||
using System; | |||||
using DotNetCore.CAP; | |||||
namespace MyConsumerSelector | |||||
{ | |||||
public class CustomSubscriber : IMessageSubscriber, ICapSubscribe | |||||
{ | |||||
[MessageSubscription("string")] | |||||
public void String(string message) | |||||
{ | |||||
Console.WriteLine($"String: {message}"); | |||||
} | |||||
[MessageSubscription("message.string")] | |||||
public void String(Message<string> message) | |||||
{ | |||||
Console.WriteLine($"String: {System.Text.Json.JsonSerializer.Serialize(message)}"); | |||||
} | |||||
[MessageSubscription("message.datetime")] | |||||
public void Date(Message<DateTime> message, [FromCap] CapHeader header) | |||||
{ | |||||
Console.WriteLine($"Date: {System.Text.Json.JsonSerializer.Serialize(message)}"); | |||||
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(header)); | |||||
} | |||||
[MessageSubscription("message.bytes")] | |||||
public void Bytes(Message<byte[]> message, [FromCap] CapHeader header) | |||||
{ | |||||
Console.WriteLine($"Bytes: {System.Text.Json.JsonSerializer.Serialize(message)}"); | |||||
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(header)); | |||||
} | |||||
[CapSubscribe("cap")] | |||||
public void Cap(string message, [FromCap] CapHeader header) | |||||
{ | |||||
Console.WriteLine($"Cap {message}"); | |||||
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(header)); | |||||
} | |||||
} | |||||
public class Message<T> | |||||
{ | |||||
public string Name { get; set; } | |||||
public T Body { get; set; } | |||||
} | |||||
} |
@@ -1,19 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<OutputType>Exe</OutputType> | |||||
<TargetFramework>net5.0</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.InMemoryStorage\DotNetCore.CAP.InMemoryStorage.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP.RabbitMQ\DotNetCore.CAP.RabbitMQ.csproj" /> | |||||
<ProjectReference Include="..\..\src\DotNetCore.CAP\DotNetCore.CAP.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /> | |||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="5.0.0" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,47 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Internal; | |||||
using Microsoft.Extensions.DependencyInjection; | |||||
using Microsoft.Extensions.Logging; | |||||
namespace MyConsumerSelector | |||||
{ | |||||
public class Program | |||||
{ | |||||
private static bool _useCustomSelector = true; | |||||
public static void Main(string[] args) | |||||
{ | |||||
var container = new ServiceCollection(); | |||||
container.AddLogging(x => x.AddConsole()); | |||||
if (_useCustomSelector) | |||||
container.AddSingleton<IConsumerServiceSelector, GenericConsumerServiceSelector<IMessageSubscriber, MessageSubscriptionAttribute>>(); | |||||
container.AddTransient<IMessageSubscriber, CustomSubscriber>(); | |||||
container.AddTransient<ICapSubscribe, CustomSubscriber>(); | |||||
container.AddCap(x => | |||||
{ | |||||
x.UseInMemoryStorage(); | |||||
x.UseRabbitMQ(z => | |||||
{ | |||||
z.ExchangeName = "MyConsumerSelector.Generic"; | |||||
z.HostName = "localhost"; | |||||
z.UserName = "guest"; | |||||
z.Password = "guest"; | |||||
z.CustomHeaders = e => new List<KeyValuePair<string, string>> | |||||
{ | |||||
new(DotNetCore.CAP.Messages.Headers.MessageId, SnowflakeId.Default().NextId().ToString()), | |||||
new(DotNetCore.CAP.Messages.Headers.MessageName, e.RoutingKey) | |||||
}; | |||||
}); | |||||
}); | |||||
var sp = container.BuildServiceProvider(); | |||||
sp.GetRequiredService<IBootstrapper>().BootstrapAsync(default); | |||||
Console.ReadLine(); | |||||
} | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
namespace MyConsumerSelector | |||||
{ | |||||
/// <summary> | |||||
/// Flags the implementer as a class that subscribes to messages. | |||||
/// </summary> | |||||
public interface IMessageSubscriber { } | |||||
/// <summary> | |||||
/// Names the message being subscribed to. | |||||
/// </summary> | |||||
public class MessageSubscriptionAttribute : Attribute, INamedGroup | |||||
{ | |||||
public MessageSubscriptionAttribute(string name) | |||||
{ | |||||
Name = name; | |||||
} | |||||
public string Name { get; } | |||||
public string Group { get; set; } | |||||
} | |||||
} |
@@ -1,11 +1,7 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using DotNetCore.CAP.Internal; | using DotNetCore.CAP.Internal; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Options; | |||||
using Xunit; | using Xunit; | ||||
namespace DotNetCore.CAP.Test | namespace DotNetCore.CAP.Test | ||||
@@ -20,7 +16,8 @@ namespace DotNetCore.CAP.Test | |||||
public CustomConsumerSubscribeTest() | public CustomConsumerSubscribeTest() | ||||
{ | { | ||||
var services = new ServiceCollection(); | var services = new ServiceCollection(); | ||||
services.AddSingleton<IConsumerServiceSelector, MyConsumerServiceSelector>(); | |||||
// Declare subscribe interface and attribute when configuring services. | |||||
services.AddSingleton<IConsumerServiceSelector, GenericConsumerServiceSelector<IMySubscribe, MySubscribeAttribute>>(); | |||||
services.AddTransient<IMySubscribe, CustomInterfaceTypesClass>(); | services.AddTransient<IMySubscribe, CustomInterfaceTypesClass>(); | ||||
services.AddLogging(); | services.AddLogging(); | ||||
services.AddCap(x => | services.AddCap(x => | ||||
@@ -37,7 +34,7 @@ namespace DotNetCore.CAP.Test | |||||
var selector = _provider.GetRequiredService<IConsumerServiceSelector>(); | var selector = _provider.GetRequiredService<IConsumerServiceSelector>(); | ||||
var candidates = selector.SelectCandidates(); | var candidates = selector.SelectCandidates(); | ||||
Assert.Equal(2, candidates.Count); | |||||
Assert.Equal(3, candidates.Count); | |||||
} | } | ||||
[Fact] | [Fact] | ||||
@@ -53,87 +50,25 @@ namespace DotNetCore.CAP.Test | |||||
Assert.StartsWith(TopicNamePrefix, bestCandidates.TopicName); | Assert.StartsWith(TopicNamePrefix, bestCandidates.TopicName); | ||||
Assert.Equal(typeof(Task), bestCandidates.MethodInfo.ReturnType); | Assert.Equal(typeof(Task), bestCandidates.MethodInfo.ReturnType); | ||||
} | } | ||||
} | |||||
public class MyConsumerServiceSelector : ConsumerServiceSelector | |||||
{ | |||||
private readonly CapOptions _capOptions; | |||||
public MyConsumerServiceSelector(IServiceProvider serviceProvider) | |||||
: base(serviceProvider) | |||||
{ | |||||
_capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value; | |||||
} | |||||
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) | |||||
{ | |||||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||||
using (var scoped = provider.CreateScope()) | |||||
{ | |||||
var scopedProvider = scoped.ServiceProvider; | |||||
var consumerServices = scopedProvider.GetServices<IMySubscribe>(); | |||||
foreach (var service in consumerServices) | |||||
{ | |||||
var typeInfo = service.GetType().GetTypeInfo(); | |||||
if (!typeof(IMySubscribe).GetTypeInfo().IsAssignableFrom(typeInfo)) | |||||
{ | |||||
continue; | |||||
} | |||||
executorDescriptorList.AddRange(GetMyDescription(typeInfo)); | |||||
} | |||||
return executorDescriptorList; | |||||
} | |||||
} | |||||
private IEnumerable<ConsumerExecutorDescriptor> GetMyDescription(TypeInfo typeInfo) | |||||
[Fact] | |||||
public void CanFindTopicWithParameters() | |||||
{ | { | ||||
foreach (var method in typeInfo.DeclaredMethods) | |||||
{ | |||||
var topicAttr = method.GetCustomAttributes<MySubscribeAttribute>(true); | |||||
var topicAttributes = topicAttr as IList<MySubscribeAttribute> ?? topicAttr.ToList(); | |||||
if (!topicAttributes.Any()) | |||||
{ | |||||
continue; | |||||
} | |||||
foreach (var attr in topicAttributes) | |||||
{ | |||||
if (attr.Group == null) | |||||
{ | |||||
attr.Group = _capOptions.DefaultGroupName + "." + _capOptions.Version; | |||||
} | |||||
else | |||||
{ | |||||
attr.Group = attr.Group + "." + _capOptions.Version; | |||||
} | |||||
if (!string.IsNullOrEmpty(_capOptions.GroupNamePrefix)) | |||||
{ | |||||
attr.Group = $"{_capOptions.GroupNamePrefix}.{attr.Group}"; | |||||
} | |||||
var selector = _provider.GetRequiredService<IConsumerServiceSelector>(); | |||||
var candidates = selector.SelectCandidates(); | |||||
var bestCandidates = selector.SelectBestCandidate($"{TopicNamePrefix}.Candidates.Foo3", candidates); | |||||
yield return new ConsumerExecutorDescriptor | |||||
{ | |||||
Attribute = new CapSubscribeAttribute(attr.Name) | |||||
{ | |||||
Group = attr.Group | |||||
}, | |||||
MethodInfo = method, | |||||
ImplTypeInfo = typeInfo, | |||||
TopicNamePrefix = _capOptions.TopicNamePrefix | |||||
}; | |||||
} | |||||
} | |||||
Assert.NotNull(bestCandidates); | |||||
Assert.NotNull(bestCandidates.Parameters); | |||||
Assert.StartsWith(GroupNamePrefix, bestCandidates.Attribute.Group); | |||||
Assert.StartsWith(TopicNamePrefix, bestCandidates.TopicName); | |||||
Assert.Equal(typeof(Task), bestCandidates.MethodInfo.ReturnType); | |||||
} | } | ||||
} | } | ||||
public interface IMySubscribe { } | public interface IMySubscribe { } | ||||
public class MySubscribeAttribute : Attribute | |||||
public class MySubscribeAttribute : Attribute, INamedGroup | |||||
{ | { | ||||
public MySubscribeAttribute(string name) | public MySubscribeAttribute(string name) | ||||
{ | { | ||||
@@ -150,19 +85,26 @@ namespace DotNetCore.CAP.Test | |||||
[MySubscribe("Candidates.Foo")] | [MySubscribe("Candidates.Foo")] | ||||
public Task GetFoo() | public Task GetFoo() | ||||
{ | { | ||||
Console.WriteLine("GetFoo() method has been excuted."); | |||||
Console.WriteLine("GetFoo() method has been executed."); | |||||
return Task.CompletedTask; | return Task.CompletedTask; | ||||
} | } | ||||
[MySubscribe("Candidates.Foo2")] | [MySubscribe("Candidates.Foo2")] | ||||
public void GetFoo2() | public void GetFoo2() | ||||
{ | { | ||||
Console.WriteLine("GetFoo2() method has been excuted."); | |||||
Console.WriteLine("GetFoo2() method has been executed."); | |||||
} | |||||
[MySubscribe("Candidates.Foo3")] | |||||
public Task GetFoo3(string message) | |||||
{ | |||||
Console.WriteLine($"GetFoo3() received message {message}."); | |||||
return Task.CompletedTask; | |||||
} | } | ||||
public void DistracterMethod() | public void DistracterMethod() | ||||
{ | { | ||||
Console.WriteLine("DistracterMethod() method has been excuted."); | |||||
Console.WriteLine("DistracterMethod() method has been executed."); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -2,13 +2,17 @@ using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Reflection; | using System.Reflection; | ||||
using DotNetCore.CAP; | |||||
using DotNetCore.CAP.Internal; | using DotNetCore.CAP.Internal; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
namespace MyConsumerSelector | |||||
namespace DotNetCore.CAP.Test | |||||
{ | { | ||||
/// <summary> | |||||
/// Allows caller to supply subscribe interface and attribute when adding services. | |||||
/// </summary> | |||||
/// <typeparam name="TSubscriber"></typeparam> | |||||
/// <typeparam name="TSubscriptionAttribute"></typeparam> | |||||
public class GenericConsumerServiceSelector<TSubscriber, TSubscriptionAttribute> : ConsumerServiceSelector | public class GenericConsumerServiceSelector<TSubscriber, TSubscriptionAttribute> : ConsumerServiceSelector | ||||
where TSubscriptionAttribute : Attribute, INamedGroup | where TSubscriptionAttribute : Attribute, INamedGroup | ||||
{ | { | ||||
@@ -20,6 +24,7 @@ namespace MyConsumerSelector | |||||
_capOptions = serviceProvider.GetRequiredService<IOptions<CapOptions>>().Value; | _capOptions = serviceProvider.GetRequiredService<IOptions<CapOptions>>().Value; | ||||
} | } | ||||
/// <inheritdoc cref="ConsumerServiceSelector"/> | |||||
protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) | protected override IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes(IServiceProvider provider) | ||||
{ | { | ||||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | ||||
@@ -31,8 +36,8 @@ namespace MyConsumerSelector | |||||
var subscriberTypeInfo = typeof(TSubscriber).GetTypeInfo(); | var subscriberTypeInfo = typeof(TSubscriber).GetTypeInfo(); | ||||
foreach (var service in subscribers) | foreach (var service in subscribers) | ||||
{ | { | ||||
var serviceTypeInfo = service.GetType().GetTypeInfo(); | |||||
if (!subscriberTypeInfo.IsAssignableFrom(serviceTypeInfo)) | |||||
var serviceTypeInfo = service?.GetType().GetTypeInfo(); | |||||
if (serviceTypeInfo == null || !subscriberTypeInfo.IsAssignableFrom(serviceTypeInfo)) | |||||
{ | { | ||||
continue; | continue; | ||||
} | } |