@@ -8,6 +8,7 @@ using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Azure.ServiceBus; | |||
using Microsoft.Azure.ServiceBus.Management; | |||
using Microsoft.Extensions.Logging; | |||
@@ -1,6 +1,7 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
@@ -3,6 +3,8 @@ | |||
using System.Reflection; | |||
using DotNetCore.CAP.Dashboard.Pages; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP.Dashboard | |||
@@ -3,6 +3,7 @@ | |||
using System; | |||
using DotNetCore.CAP; | |||
using DotNetCore.CAP.Internal; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP.Dashboard.NodeDiscovery | |||
@@ -1,6 +1,8 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Internal; | |||
namespace DotNetCore.CAP.Dashboard.NodeDiscovery | |||
{ | |||
internal class ConsulProcessingNodeServer : IProcessingServer | |||
@@ -5,6 +5,7 @@ | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Transport; | |||
namespace DotNetCore.CAP.InMemoryStorage | |||
{ | |||
@@ -8,6 +8,7 @@ using System.Text; | |||
using System.Threading; | |||
using Confluent.Kafka; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP.Kafka | |||
@@ -1,6 +1,7 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP.Kafka | |||
@@ -4,6 +4,7 @@ | |||
using System.Diagnostics; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using MongoDB.Driver; | |||
@@ -5,6 +5,7 @@ using System.Data; | |||
using System.Diagnostics; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Storage; | |||
using Microsoft.Extensions.DependencyInjection; | |||
@@ -5,6 +5,7 @@ using System.Data; | |||
using System.Diagnostics; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
using Microsoft.EntityFrameworkCore.Storage; | |||
using Microsoft.Extensions.DependencyInjection; | |||
@@ -6,6 +6,7 @@ using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Options; | |||
using RabbitMQ.Client; | |||
using RabbitMQ.Client.Events; | |||
@@ -1,6 +1,7 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP.RabbitMQ | |||
@@ -6,6 +6,7 @@ using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Data.SqlClient; | |||
namespace DotNetCore.CAP.SqlServer.Diagnostics | |||
@@ -6,6 +6,7 @@ using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Transport; | |||
namespace DotNetCore.CAP.SqlServer.Diagnostics | |||
{ | |||
@@ -9,6 +9,7 @@ using System.Threading.Tasks; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.SqlServer.Diagnostics; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Data.SqlClient; | |||
using Microsoft.EntityFrameworkCore; | |||
using Microsoft.EntityFrameworkCore.Infrastructure; | |||
@@ -1,21 +0,0 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System.Reflection; | |||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||
namespace DotNetCore.CAP.Abstractions | |||
{ | |||
/// <summary> | |||
/// Model binder factory. | |||
/// </summary> | |||
public interface IModelBinderFactory | |||
{ | |||
/// <summary> | |||
/// Create a model binder by parameter. | |||
/// </summary> | |||
/// <param name="parameter">The method parameter info</param> | |||
/// <returns>A model binder instance.</returns> | |||
IModelBinder CreateBinder(ParameterInfo parameter); | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System.Threading.Tasks; | |||
namespace DotNetCore.CAP.Abstractions.ModelBinding | |||
{ | |||
/// <summary> | |||
/// Defines an interface for model binders. | |||
/// </summary> | |||
public interface IModelBinder | |||
{ | |||
Task<ModelBindingResult> BindModelAsync(string content); | |||
} | |||
} |
@@ -1,104 +0,0 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Internal; | |||
namespace DotNetCore.CAP.Abstractions.ModelBinding | |||
{ | |||
/// <summary> | |||
/// Contains the result of model binding. | |||
/// </summary> | |||
public struct ModelBindingResult | |||
{ | |||
/// <summary> | |||
/// Creates a <see cref="ModelBindingResult" /> representing a failed model binding operation. | |||
/// </summary> | |||
/// <returns>A <see cref="ModelBindingResult" /> representing a failed model binding operation.</returns> | |||
public static ModelBindingResult Failed() | |||
{ | |||
return new ModelBindingResult(null, false); | |||
} | |||
/// <summary> | |||
/// Creates a <see cref="ModelBindingResult" /> representing a successful model binding operation. | |||
/// </summary> | |||
/// <param name="model">The model value. May be <c>null.</c></param> | |||
/// <returns>A <see cref="ModelBindingResult" /> representing a successful model bind.</returns> | |||
public static ModelBindingResult Success(object model) | |||
{ | |||
return new ModelBindingResult(model, true); | |||
} | |||
private ModelBindingResult(object model, bool isSuccess) | |||
{ | |||
Model = model; | |||
IsSuccess = isSuccess; | |||
} | |||
/// <summary> | |||
/// Gets the model associated with this context. | |||
/// </summary> | |||
public object Model { get; } | |||
public bool IsSuccess { get; } | |||
public override string ToString() | |||
{ | |||
if (IsSuccess) | |||
{ | |||
return $"Success '{Model}'"; | |||
} | |||
return "Failed"; | |||
} | |||
public override bool Equals(object obj) | |||
{ | |||
var other = obj as ModelBindingResult?; | |||
if (other == null) | |||
{ | |||
return false; | |||
} | |||
return Equals(other.Value); | |||
} | |||
public override int GetHashCode() | |||
{ | |||
var hashCodeCombiner = HashCodeCombiner.Start(); | |||
hashCodeCombiner.Add(IsSuccess); | |||
hashCodeCombiner.Add(Model); | |||
return hashCodeCombiner.CombinedHash; | |||
} | |||
public bool Equals(ModelBindingResult other) | |||
{ | |||
return | |||
IsSuccess == other.IsSuccess && | |||
Equals(Model, other.Model); | |||
} | |||
/// <summary> | |||
/// Compares <see cref="ModelBindingResult" /> objects for equality. | |||
/// </summary> | |||
/// <param name="x">A <see cref="ModelBindingResult" />.</param> | |||
/// <param name="y">A <see cref="ModelBindingResult" />.</param> | |||
/// <returns><c>true</c> if the objects are equal, otherwise <c>false</c>.</returns> | |||
public static bool operator ==(ModelBindingResult x, ModelBindingResult y) | |||
{ | |||
return x.Equals(y); | |||
} | |||
/// <summary> | |||
/// Compares <see cref="ModelBindingResult" /> objects for inequality. | |||
/// </summary> | |||
/// <param name="x">A <see cref="ModelBindingResult" />.</param> | |||
/// <param name="y">A <see cref="ModelBindingResult" />.</param> | |||
/// <returns><c>true</c> if the objects are not equal, otherwise <c>false</c>.</returns> | |||
public static bool operator !=(ModelBindingResult x, ModelBindingResult y) | |||
{ | |||
return !x.Equals(y); | |||
} | |||
} | |||
} |
@@ -4,7 +4,7 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.ObjectModel; | |||
using DotNetCore.CAP.Abstractions; | |||
using DotNetCore.CAP.Internal; | |||
// ReSharper disable once CheckNamespace | |||
namespace DotNetCore.CAP | |||
@@ -2,7 +2,6 @@ | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System; | |||
using DotNetCore.CAP.Abstractions; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP | |||
@@ -3,10 +3,10 @@ | |||
using System; | |||
using DotNetCore.CAP; | |||
using DotNetCore.CAP.Abstractions; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Processor; | |||
using DotNetCore.CAP.Serialization; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.DependencyInjection.Extensions; | |||
// ReSharper disable once CheckNamespace | |||
@@ -2,6 +2,7 @@ | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Transport; | |||
namespace DotNetCore.CAP | |||
{ | |||
@@ -4,9 +4,8 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Reflection; | |||
using DotNetCore.CAP.Abstractions; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// A descriptor of user definition method. |
@@ -2,7 +2,6 @@ | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System; | |||
using DotNetCore.CAP.Abstractions; | |||
using Microsoft.Extensions.Logging; | |||
namespace DotNetCore.CAP.Internal | |||
@@ -9,10 +9,10 @@ using DotNetCore.CAP.Persistence; | |||
using Microsoft.Extensions.Hosting; | |||
using Microsoft.Extensions.Logging; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// Default implement of <see cref="T:DotNetCore.CAP.IBootstrapper" />. | |||
/// Default implement of <see cref="T:DotNetCore.CAP.Internal.IBootstrapper" />. | |||
/// </summary> | |||
internal class DefaultBootstrapper : BackgroundService, IBootstrapper | |||
{ |
@@ -4,7 +4,7 @@ | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// Represents bootstrapping logic. For example, adding initial state to the storage or querying certain entities. |
@@ -7,14 +7,14 @@ using System.Diagnostics; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Diagnostics; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP.Abstractions | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
public class CapPublisher : ICapPublisher | |||
internal class CapPublisher : ICapPublisher | |||
{ | |||
private readonly IDispatcher _dispatcher; | |||
private readonly IDataStorage _storage; |
@@ -4,19 +4,17 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Text.Json; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Diagnostics; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Serialization; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
internal class ConsumerRegister : IConsumerRegister | |||
{ |
@@ -1,7 +1,7 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// Handler received message of subscribed. |
@@ -1,233 +1,231 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Text.RegularExpressions; | |||
using DotNetCore.CAP.Abstractions; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using System.Collections.Concurrent; | |||
using DotNetCore.CAP.Internal; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP | |||
{ | |||
/// <inheritdoc /> | |||
/// <summary> | |||
/// A default <see cref="T:DotNetCore.CAP.Abstractions.IConsumerServiceSelector" /> implementation. | |||
/// </summary> | |||
public class DefaultConsumerServiceSelector : IConsumerServiceSelector | |||
{ | |||
private readonly CapOptions _capOptions; | |||
private readonly IServiceProvider _serviceProvider; | |||
/// <summary> | |||
/// since this class be designed as a Singleton service,the following two list must be thread safe! | |||
/// </summary> | |||
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _asteriskList; | |||
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _poundList; | |||
/// <summary> | |||
/// Creates a new <see cref="DefaultConsumerServiceSelector" />. | |||
/// </summary> | |||
public DefaultConsumerServiceSelector(IServiceProvider serviceProvider) | |||
{ | |||
_serviceProvider = serviceProvider; | |||
_capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value; | |||
_asteriskList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>(); | |||
_poundList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>(); | |||
} | |||
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates() | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
executorDescriptorList.AddRange(FindConsumersFromInterfaceTypes(_serviceProvider)); | |||
executorDescriptorList.AddRange(FindConsumersFromControllerTypes()); | |||
return executorDescriptorList; | |||
} | |||
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var result = MatchUsingName(key, executeDescriptor); | |||
if (result != null) | |||
{ | |||
return result; | |||
} | |||
//[*] match with regex, i.e. foo.*.abc | |||
result = MatchAsteriskUsingRegex(key, executeDescriptor); | |||
if (result != null) | |||
{ | |||
return result; | |||
} | |||
//[#] match regex, i.e. foo.# | |||
result = MatchPoundUsingRegex(key, executeDescriptor); | |||
return result; | |||
} | |||
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes( | |||
IServiceProvider provider) | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
var capSubscribeTypeInfo = typeof(ICapSubscribe).GetTypeInfo(); | |||
foreach (var service in ServiceCollectionExtensions.ServiceCollection.Where(o => o.ImplementationType != null && o.ServiceType != null)) | |||
{ | |||
var typeInfo = service.ImplementationType.GetTypeInfo(); | |||
if (!capSubscribeTypeInfo.IsAssignableFrom(typeInfo)) | |||
{ | |||
continue; | |||
} | |||
var serviceTypeInfo = service.ServiceType.GetTypeInfo(); | |||
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo, serviceTypeInfo)); | |||
} | |||
return executorDescriptorList; | |||
} | |||
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromControllerTypes() | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
var types = Assembly.GetEntryAssembly().ExportedTypes; | |||
foreach (var type in types) | |||
{ | |||
var typeInfo = type.GetTypeInfo(); | |||
if (Helper.IsController(typeInfo)) | |||
{ | |||
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); | |||
} | |||
} | |||
return executorDescriptorList; | |||
} | |||
protected IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo serviceTypeInfo = null) | |||
{ | |||
foreach (var method in typeInfo.DeclaredMethods) | |||
{ | |||
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); | |||
var topicAttributes = topicAttr as IList<TopicAttribute> ?? topicAttr.ToList(); | |||
if (!topicAttributes.Any()) | |||
{ | |||
continue; | |||
} | |||
foreach (var attr in topicAttributes) | |||
{ | |||
if (attr.Group == null) | |||
{ | |||
attr.Group = _capOptions.DefaultGroup + "." + _capOptions.Version; | |||
} | |||
else | |||
{ | |||
attr.Group = attr.Group + "." + _capOptions.Version; | |||
} | |||
var parameters = method.GetParameters() | |||
.Select(parameter => new ParameterDescriptor | |||
{ | |||
Name = parameter.Name, | |||
ParameterType = parameter.ParameterType, | |||
IsFromCap = parameter.GetCustomAttributes(typeof(FromCapAttribute)).Any() | |||
}).ToList(); | |||
yield return InitDescriptor(attr, method, typeInfo, serviceTypeInfo, parameters); | |||
} | |||
} | |||
} | |||
private static ConsumerExecutorDescriptor InitDescriptor( | |||
TopicAttribute attr, | |||
MethodInfo methodInfo, | |||
TypeInfo implType, | |||
TypeInfo serviceTypeInfo, | |||
IList<ParameterDescriptor> parameters) | |||
{ | |||
var descriptor = new ConsumerExecutorDescriptor | |||
{ | |||
Attribute = attr, | |||
MethodInfo = methodInfo, | |||
ImplTypeInfo = implType, | |||
ServiceTypeInfo = serviceTypeInfo, | |||
Parameters = parameters | |||
}; | |||
return descriptor; | |||
} | |||
private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); | |||
} | |||
private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var group = executeDescriptor.First().Attribute.Group; | |||
if (!_asteriskList.TryGetValue(group, out var tmpList)) | |||
{ | |||
tmpList = executeDescriptor.Where(x => x.Attribute.Name.IndexOf('*') >= 0) | |||
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> | |||
{ | |||
Name = ("^" + x.Attribute.Name + "$").Replace("*", "[0-9_a-zA-Z]+").Replace(".", "\\."), | |||
Descriptor = x | |||
}).ToList(); | |||
_asteriskList.TryAdd(group, tmpList); | |||
} | |||
foreach (var red in tmpList) | |||
{ | |||
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) | |||
{ | |||
return red.Descriptor; | |||
} | |||
} | |||
return null; | |||
} | |||
private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var group = executeDescriptor.First().Attribute.Group; | |||
if (!_poundList.TryGetValue(group, out var tmpList)) | |||
{ | |||
tmpList = executeDescriptor | |||
.Where(x => x.Attribute.Name.IndexOf('#') >= 0) | |||
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> | |||
{ | |||
Name = ("^" + x.Attribute.Name.Replace(".", "\\.") + "$").Replace("#", "[0-9_a-zA-Z\\.]+"), | |||
Descriptor = x | |||
}).ToList(); | |||
_poundList.TryAdd(group, tmpList); | |||
} | |||
foreach (var red in tmpList) | |||
{ | |||
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) | |||
{ | |||
return red.Descriptor; | |||
} | |||
} | |||
return null; | |||
} | |||
private class RegexExecuteDescriptor<T> | |||
{ | |||
public string Name { get; set; } | |||
public T Descriptor { get; set; } | |||
} | |||
} | |||
} | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Text.RegularExpressions; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Options; | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <inheritdoc /> | |||
/// <summary> | |||
/// A default <see cref="T:DotNetCore.CAP.Abstractions.IConsumerServiceSelector" /> implementation. | |||
/// </summary> | |||
public class DefaultConsumerServiceSelector : IConsumerServiceSelector | |||
{ | |||
private readonly CapOptions _capOptions; | |||
private readonly IServiceProvider _serviceProvider; | |||
/// <summary> | |||
/// since this class be designed as a Singleton service,the following two list must be thread safe! | |||
/// </summary> | |||
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _asteriskList; | |||
private readonly ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>> _poundList; | |||
/// <summary> | |||
/// Creates a new <see cref="DefaultConsumerServiceSelector" />. | |||
/// </summary> | |||
public DefaultConsumerServiceSelector(IServiceProvider serviceProvider) | |||
{ | |||
_serviceProvider = serviceProvider; | |||
_capOptions = serviceProvider.GetService<IOptions<CapOptions>>().Value; | |||
_asteriskList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>(); | |||
_poundList = new ConcurrentDictionary<string, List<RegexExecuteDescriptor<ConsumerExecutorDescriptor>>>(); | |||
} | |||
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates() | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
executorDescriptorList.AddRange(FindConsumersFromInterfaceTypes(_serviceProvider)); | |||
executorDescriptorList.AddRange(FindConsumersFromControllerTypes()); | |||
return executorDescriptorList; | |||
} | |||
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var result = MatchUsingName(key, executeDescriptor); | |||
if (result != null) | |||
{ | |||
return result; | |||
} | |||
//[*] match with regex, i.e. foo.*.abc | |||
result = MatchAsteriskUsingRegex(key, executeDescriptor); | |||
if (result != null) | |||
{ | |||
return result; | |||
} | |||
//[#] match regex, i.e. foo.# | |||
result = MatchPoundUsingRegex(key, executeDescriptor); | |||
return result; | |||
} | |||
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromInterfaceTypes( | |||
IServiceProvider provider) | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
var capSubscribeTypeInfo = typeof(ICapSubscribe).GetTypeInfo(); | |||
foreach (var service in ServiceCollectionExtensions.ServiceCollection.Where(o => o.ImplementationType != null && o.ServiceType != null)) | |||
{ | |||
var typeInfo = service.ImplementationType.GetTypeInfo(); | |||
if (!capSubscribeTypeInfo.IsAssignableFrom(typeInfo)) | |||
{ | |||
continue; | |||
} | |||
var serviceTypeInfo = service.ServiceType.GetTypeInfo(); | |||
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo, serviceTypeInfo)); | |||
} | |||
return executorDescriptorList; | |||
} | |||
protected virtual IEnumerable<ConsumerExecutorDescriptor> FindConsumersFromControllerTypes() | |||
{ | |||
var executorDescriptorList = new List<ConsumerExecutorDescriptor>(); | |||
var types = Assembly.GetEntryAssembly().ExportedTypes; | |||
foreach (var type in types) | |||
{ | |||
var typeInfo = type.GetTypeInfo(); | |||
if (Helper.IsController(typeInfo)) | |||
{ | |||
executorDescriptorList.AddRange(GetTopicAttributesDescription(typeInfo)); | |||
} | |||
} | |||
return executorDescriptorList; | |||
} | |||
protected IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo, TypeInfo serviceTypeInfo = null) | |||
{ | |||
foreach (var method in typeInfo.DeclaredMethods) | |||
{ | |||
var topicAttr = method.GetCustomAttributes<TopicAttribute>(true); | |||
var topicAttributes = topicAttr as IList<TopicAttribute> ?? topicAttr.ToList(); | |||
if (!topicAttributes.Any()) | |||
{ | |||
continue; | |||
} | |||
foreach (var attr in topicAttributes) | |||
{ | |||
if (attr.Group == null) | |||
{ | |||
attr.Group = _capOptions.DefaultGroup + "." + _capOptions.Version; | |||
} | |||
else | |||
{ | |||
attr.Group = attr.Group + "." + _capOptions.Version; | |||
} | |||
var parameters = method.GetParameters() | |||
.Select(parameter => new ParameterDescriptor | |||
{ | |||
Name = parameter.Name, | |||
ParameterType = parameter.ParameterType, | |||
IsFromCap = parameter.GetCustomAttributes(typeof(FromCapAttribute)).Any() | |||
}).ToList(); | |||
yield return InitDescriptor(attr, method, typeInfo, serviceTypeInfo, parameters); | |||
} | |||
} | |||
} | |||
private static ConsumerExecutorDescriptor InitDescriptor( | |||
TopicAttribute attr, | |||
MethodInfo methodInfo, | |||
TypeInfo implType, | |||
TypeInfo serviceTypeInfo, | |||
IList<ParameterDescriptor> parameters) | |||
{ | |||
var descriptor = new ConsumerExecutorDescriptor | |||
{ | |||
Attribute = attr, | |||
MethodInfo = methodInfo, | |||
ImplTypeInfo = implType, | |||
ServiceTypeInfo = serviceTypeInfo, | |||
Parameters = parameters | |||
}; | |||
return descriptor; | |||
} | |||
private ConsumerExecutorDescriptor MatchUsingName(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key); | |||
} | |||
private ConsumerExecutorDescriptor MatchAsteriskUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var group = executeDescriptor.First().Attribute.Group; | |||
if (!_asteriskList.TryGetValue(group, out var tmpList)) | |||
{ | |||
tmpList = executeDescriptor.Where(x => x.Attribute.Name.IndexOf('*') >= 0) | |||
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> | |||
{ | |||
Name = ("^" + x.Attribute.Name + "$").Replace("*", "[0-9_a-zA-Z]+").Replace(".", "\\."), | |||
Descriptor = x | |||
}).ToList(); | |||
_asteriskList.TryAdd(group, tmpList); | |||
} | |||
foreach (var red in tmpList) | |||
{ | |||
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) | |||
{ | |||
return red.Descriptor; | |||
} | |||
} | |||
return null; | |||
} | |||
private ConsumerExecutorDescriptor MatchPoundUsingRegex(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) | |||
{ | |||
var group = executeDescriptor.First().Attribute.Group; | |||
if (!_poundList.TryGetValue(group, out var tmpList)) | |||
{ | |||
tmpList = executeDescriptor | |||
.Where(x => x.Attribute.Name.IndexOf('#') >= 0) | |||
.Select(x => new RegexExecuteDescriptor<ConsumerExecutorDescriptor> | |||
{ | |||
Name = ("^" + x.Attribute.Name.Replace(".", "\\.") + "$").Replace("#", "[0-9_a-zA-Z\\.]+"), | |||
Descriptor = x | |||
}).ToList(); | |||
_poundList.TryAdd(group, tmpList); | |||
} | |||
foreach (var red in tmpList) | |||
{ | |||
if (Regex.IsMatch(key, red.Name, RegexOptions.Singleline)) | |||
{ | |||
return red.Descriptor; | |||
} | |||
} | |||
return null; | |||
} | |||
private class RegexExecuteDescriptor<T> | |||
{ | |||
public string Name { get; set; } | |||
public T Descriptor { get; set; } | |||
} | |||
} | |||
} |
@@ -3,7 +3,7 @@ | |||
using System.Collections.Generic; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// Defines an interface for selecting an consumer service method to invoke for the current message. |
@@ -4,7 +4,7 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
namespace DotNetCore.CAP.Abstractions | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
public interface IMongoTransaction : IDisposable | |||
{ | |||
@@ -14,8 +14,8 @@ namespace DotNetCore.CAP.Abstractions | |||
/// <value></value> | |||
bool AutoCommit { get; set; } | |||
Task<IMongoTransaction> BegeinAsync(bool autoCommit = true); | |||
Task<IMongoTransaction> BeginAsync(bool autoCommit = true); | |||
IMongoTransaction Begein(bool autoCommit = true); | |||
IMongoTransaction Begin(bool autoCommit = true); | |||
} | |||
} |
@@ -3,7 +3,7 @@ | |||
using System; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <inheritdoc /> | |||
/// <summary> |
@@ -7,15 +7,14 @@ using System.Diagnostics; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Diagnostics; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Messages; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Processor; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
using Microsoft.Extensions.Options; | |||
using Microsoft.Extensions.DependencyInjection; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
internal class DefaultSubscriberExecutor : ISubscriberExecutor | |||
{ |
@@ -5,7 +5,7 @@ using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Persistence; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <summary> | |||
/// Consumer executor |
@@ -5,7 +5,7 @@ using System; | |||
using System.Diagnostics.CodeAnalysis; | |||
using Microsoft.Extensions.Logging; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
[SuppressMessage("ReSharper", "InconsistentNaming")] | |||
internal static class LoggerExtensions | |||
@@ -3,7 +3,7 @@ | |||
using System; | |||
namespace DotNetCore.CAP.Abstractions | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
/// <inheritdoc /> | |||
/// <summary> |
@@ -10,6 +10,7 @@ namespace DotNetCore.CAP.Monitoring | |||
public MessageType MessageType { get; set; } | |||
public string Group { get; set; } | |||
public string Name { get; set; } | |||
public string Content { get; set; } | |||
@@ -7,6 +7,7 @@ using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Persistence; | |||
using DotNetCore.CAP.Transport; | |||
using Microsoft.Extensions.Logging; | |||
namespace DotNetCore.CAP.Processor | |||
@@ -6,6 +6,7 @@ using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Internal; | |||
using Microsoft.Extensions.DependencyInjection; | |||
using Microsoft.Extensions.Logging; | |||
@@ -3,6 +3,7 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Internal; | |||
namespace DotNetCore.CAP.Processor | |||
{ | |||
@@ -6,7 +6,7 @@ using System.Collections.Generic; | |||
using System.Threading; | |||
using DotNetCore.CAP.Messages; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Transport | |||
{ | |||
/// <inheritdoc /> | |||
/// <summary> |
@@ -1,7 +1,7 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Transport | |||
{ | |||
/// <summary> | |||
/// Consumer client factory to create consumer client instance. |
@@ -1,9 +1,10 @@ | |||
// Copyright (c) .NET Core Community. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Persistence; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Transport | |||
{ | |||
public interface IDispatcher | |||
{ |
@@ -3,7 +3,7 @@ | |||
using System; | |||
namespace DotNetCore.CAP | |||
namespace DotNetCore.CAP.Transport | |||
{ | |||
public enum MqLogType | |||
{ |
@@ -1,46 +0,0 @@ | |||
using System.Linq; | |||
using System.Reflection; | |||
using DotNetCore.CAP.Abstractions; | |||
using DotNetCore.CAP.Internal; | |||
using Moq; | |||
using Xunit; | |||
namespace DotNetCore.CAP.Test | |||
{ | |||
public class ModelBinderFactoryTest | |||
{ | |||
private IModelBinderFactory _factory; | |||
public ModelBinderFactoryTest() | |||
{ | |||
var serializer = Mock.Of<IContentSerializer>(); | |||
_factory = new ModelBinderFactory(serializer); | |||
} | |||
[Theory] | |||
[InlineData(nameof(Sample.DateTimeParam))] | |||
[InlineData(nameof(Sample.StringParam))] | |||
[InlineData(nameof(Sample.IntegerParam))] | |||
[InlineData(nameof(Sample.GuidParam))] | |||
[InlineData(nameof(Sample.UriParam))] | |||
public void CreateSimpleTypeBinderTest(string methodName) | |||
{ | |||
var methodInfo = typeof(Sample).GetRuntimeMethods().Single(x => x.Name == methodName); | |||
var binder = _factory.CreateBinder(methodInfo.GetParameters()[0]); | |||
Assert.NotNull(binder); | |||
Assert.True(binder is SimpleTypeModelBinder); | |||
Assert.False(binder is ComplexTypeModelBinder); | |||
} | |||
[Theory] | |||
[InlineData(nameof(Sample.ComplexTypeParam))] | |||
public void CreateComplexTypeBinderTest(string methodName) | |||
{ | |||
var methodInfo = typeof(Sample).GetRuntimeMethods().Single(x => x.Name == methodName); | |||
var binder = _factory.CreateBinder(methodInfo.GetParameters()[0]); | |||
Assert.NotNull(binder); | |||
Assert.False(binder is SimpleTypeModelBinder); | |||
Assert.True(binder is ComplexTypeModelBinder); | |||
} | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Infrastructure; | |||
using DotNetCore.CAP.Internal; | |||
using Xunit; | |||
namespace DotNetCore.CAP.Test | |||