diff --git a/CAP.sln b/CAP.sln index 1b77c31..a1b6644 100644 --- a/CAP.sln +++ b/CAP.sln @@ -71,8 +71,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetCore.CAP.NATS", "src\ 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}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyConsumerSelector", "samples\MyConsumerSelector\MyConsumerSelector.csproj", "{1AE86784-0B64-4A73-8D81-D913C6E33D7D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution 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}.Release|Any CPU.ActiveCfg = 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 GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -198,7 +192,6 @@ Global {B187DD15-092D-4B72-9807-50856607D237} = {3A6B6931-A123-477A-9469-8B468B5385AF} {8B2FD3EA-E72B-4A82-B182-B87EC0C15D07} = {9B2AE124-6636-4DE9-83A3-70360DABD0C4} {54F6C206-2A23-4971-AE5A-FC47EB772452} = {3A6B6931-A123-477A-9469-8B468B5385AF} - {1AE86784-0B64-4A73-8D81-D913C6E33D7D} = {3A6B6931-A123-477A-9469-8B468B5385AF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2E70565D-94CF-40B4-BFE1-AC18D5F736AB} diff --git a/samples/MyConsumerSelector/CustomSubscriber.cs b/samples/MyConsumerSelector/CustomSubscriber.cs deleted file mode 100644 index b29510e..0000000 --- a/samples/MyConsumerSelector/CustomSubscriber.cs +++ /dev/null @@ -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 message) - { - Console.WriteLine($"String: {System.Text.Json.JsonSerializer.Serialize(message)}"); - } - - [MessageSubscription("message.datetime")] - public void Date(Message 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 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 - { - public string Name { get; set; } - public T Body { get; set; } - } -} \ No newline at end of file diff --git a/samples/MyConsumerSelector/MyConsumerSelector.csproj b/samples/MyConsumerSelector/MyConsumerSelector.csproj deleted file mode 100644 index 32e0e64..0000000 --- a/samples/MyConsumerSelector/MyConsumerSelector.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - net5.0 - - - - - - - - - - - - - - diff --git a/samples/MyConsumerSelector/Program.cs b/samples/MyConsumerSelector/Program.cs deleted file mode 100644 index 7cb9a8f..0000000 --- a/samples/MyConsumerSelector/Program.cs +++ /dev/null @@ -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>(); - - container.AddTransient(); - container.AddTransient(); - - 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> - { - new(DotNetCore.CAP.Messages.Headers.MessageId, SnowflakeId.Default().NextId().ToString()), - new(DotNetCore.CAP.Messages.Headers.MessageName, e.RoutingKey) - }; - }); - }); - - var sp = container.BuildServiceProvider(); - sp.GetRequiredService().BootstrapAsync(default); - Console.ReadLine(); - } - } -} \ No newline at end of file diff --git a/samples/MyConsumerSelector/Selectors.cs b/samples/MyConsumerSelector/Selectors.cs deleted file mode 100644 index e24761d..0000000 --- a/samples/MyConsumerSelector/Selectors.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace MyConsumerSelector -{ - /// - /// Flags the implementer as a class that subscribes to messages. - /// - public interface IMessageSubscriber { } - - /// - /// Names the message being subscribed to. - /// - public class MessageSubscriptionAttribute : Attribute, INamedGroup - { - public MessageSubscriptionAttribute(string name) - { - Name = name; - } - - public string Name { get; } - - public string Group { get; set; } - } -} \ No newline at end of file diff --git a/test/DotNetCore.CAP.Test/CustomConsumerSubscribeTest.cs b/test/DotNetCore.CAP.Test/CustomConsumerSubscribeTest.cs index d7b2be9..142c13c 100644 --- a/test/DotNetCore.CAP.Test/CustomConsumerSubscribeTest.cs +++ b/test/DotNetCore.CAP.Test/CustomConsumerSubscribeTest.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Threading.Tasks; using DotNetCore.CAP.Internal; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Xunit; namespace DotNetCore.CAP.Test @@ -20,7 +16,8 @@ namespace DotNetCore.CAP.Test public CustomConsumerSubscribeTest() { var services = new ServiceCollection(); - services.AddSingleton(); + // Declare subscribe interface and attribute when configuring services. + services.AddSingleton>(); services.AddTransient(); services.AddLogging(); services.AddCap(x => @@ -37,7 +34,7 @@ namespace DotNetCore.CAP.Test var selector = _provider.GetRequiredService(); var candidates = selector.SelectCandidates(); - Assert.Equal(2, candidates.Count); + Assert.Equal(3, candidates.Count); } [Fact] @@ -53,87 +50,25 @@ namespace DotNetCore.CAP.Test Assert.StartsWith(TopicNamePrefix, bestCandidates.TopicName); Assert.Equal(typeof(Task), bestCandidates.MethodInfo.ReturnType); } - } - - public class MyConsumerServiceSelector : ConsumerServiceSelector - { - private readonly CapOptions _capOptions; - - public MyConsumerServiceSelector(IServiceProvider serviceProvider) - : base(serviceProvider) - { - _capOptions = serviceProvider.GetService>().Value; - } - - protected override IEnumerable FindConsumersFromInterfaceTypes(IServiceProvider provider) - { - var executorDescriptorList = new List(); - - using (var scoped = provider.CreateScope()) - { - var scopedProvider = scoped.ServiceProvider; - var consumerServices = scopedProvider.GetServices(); - 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 GetMyDescription(TypeInfo typeInfo) + + [Fact] + public void CanFindTopicWithParameters() { - foreach (var method in typeInfo.DeclaredMethods) - { - var topicAttr = method.GetCustomAttributes(true); - var topicAttributes = topicAttr as IList ?? 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(); + 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 class MySubscribeAttribute : Attribute + public class MySubscribeAttribute : Attribute, INamedGroup { public MySubscribeAttribute(string name) { @@ -150,19 +85,26 @@ namespace DotNetCore.CAP.Test [MySubscribe("Candidates.Foo")] public Task GetFoo() { - Console.WriteLine("GetFoo() method has been excuted."); + Console.WriteLine("GetFoo() method has been executed."); return Task.CompletedTask; } [MySubscribe("Candidates.Foo2")] 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() { - Console.WriteLine("DistracterMethod() method has been excuted."); + Console.WriteLine("DistracterMethod() method has been executed."); } } } \ No newline at end of file diff --git a/samples/MyConsumerSelector/GenericConsumerServiceSelector.cs b/test/DotNetCore.CAP.Test/GenericConsumerServiceSelector.cs similarity index 88% rename from samples/MyConsumerSelector/GenericConsumerServiceSelector.cs rename to test/DotNetCore.CAP.Test/GenericConsumerServiceSelector.cs index 4bf1f16..26f3fae 100644 --- a/samples/MyConsumerSelector/GenericConsumerServiceSelector.cs +++ b/test/DotNetCore.CAP.Test/GenericConsumerServiceSelector.cs @@ -2,13 +2,17 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using DotNetCore.CAP; using DotNetCore.CAP.Internal; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -namespace MyConsumerSelector +namespace DotNetCore.CAP.Test { + /// + /// Allows caller to supply subscribe interface and attribute when adding services. + /// + /// + /// public class GenericConsumerServiceSelector : ConsumerServiceSelector where TSubscriptionAttribute : Attribute, INamedGroup { @@ -20,6 +24,7 @@ namespace MyConsumerSelector _capOptions = serviceProvider.GetRequiredService>().Value; } + /// protected override IEnumerable FindConsumersFromInterfaceTypes(IServiceProvider provider) { var executorDescriptorList = new List(); @@ -31,8 +36,8 @@ namespace MyConsumerSelector var subscriberTypeInfo = typeof(TSubscriber).GetTypeInfo(); 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; }