@@ -1,6 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using DotNetCore.CAP.Internal; | |||
namespace DotNetCore.CAP.Abstractions.ModelBinding | |||
{ | |||
@@ -51,6 +49,57 @@ namespace DotNetCore.CAP.Abstractions.ModelBinding | |||
{ | |||
return $"Failed"; | |||
} | |||
} | |||
} | |||
public override bool Equals(object obj) | |||
{ | |||
var other = obj as ModelBindingResult?; | |||
if (other == null) | |||
{ | |||
return false; | |||
} | |||
else | |||
{ | |||
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 && | |||
object.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); | |||
} | |||
} | |||
} |
@@ -2,7 +2,6 @@ | |||
using System.Diagnostics; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Abstractions; | |||
using DotNetCore.CAP.Infrastructure; | |||
using DotNetCore.CAP.Internal; | |||
using DotNetCore.CAP.Models; | |||
using DotNetCore.CAP.Processor; | |||
@@ -1,4 +1,5 @@ | |||
using System; | |||
using System.ComponentModel; | |||
using System.Reflection; | |||
using Newtonsoft.Json; | |||
@@ -68,5 +69,10 @@ namespace DotNetCore.CAP.Infrastructure | |||
return !typeInfo.ContainsGenericParameters | |||
&& typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase); | |||
} | |||
public static bool IsComplexType(Type type) | |||
{ | |||
return !TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string)); | |||
} | |||
} | |||
} |
@@ -35,23 +35,29 @@ namespace DotNetCore.CAP.Internal | |||
_logger.LogDebug("Executing consumer Topic: {0}", _consumerContext.ConsumerDescriptor.MethodInfo.Name); | |||
var obj = ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, | |||
_consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType()); | |||
_consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType()); | |||
var value = _consumerContext.DeliverMessage.Content; | |||
if (_executor.MethodParameters.Length > 0) | |||
{ | |||
var firstParameter = _executor.MethodParameters[0]; | |||
var binder = _modelBinderFactory.CreateBinder(firstParameter); | |||
var result = await binder.BindModelAsync(value); | |||
if (result.IsSuccess) | |||
try | |||
{ | |||
_executor.Execute(obj, result.Model); | |||
var binder = _modelBinderFactory.CreateBinder(firstParameter); | |||
var result = await binder.BindModelAsync(value); | |||
if (result.IsSuccess) | |||
{ | |||
_executor.Execute(obj, result.Model); | |||
} | |||
else | |||
{ | |||
_logger.LogWarning($"Parameters:{firstParameter.Name} bind failed!"); | |||
} | |||
} | |||
else | |||
catch (FormatException ex) | |||
{ | |||
_logger.LogWarning($"Parameters:{firstParameter.Name} bind failed!"); | |||
} | |||
_logger.ModelBinderFormattingException(_executor.MethodInfo?.Name, firstParameter.Name, value, ex); | |||
} | |||
} | |||
else | |||
{ | |||
@@ -2,6 +2,7 @@ | |||
using System.ComponentModel; | |||
using System.Globalization; | |||
using System.Reflection; | |||
using System.Runtime.ExceptionServices; | |||
using System.Threading.Tasks; | |||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||
@@ -24,41 +25,55 @@ namespace DotNetCore.CAP.Internal | |||
{ | |||
throw new ArgumentNullException(nameof(content)); | |||
} | |||
var parameterType = _parameterInfo.ParameterType; | |||
object model; | |||
if (parameterType == typeof(string)) | |||
try | |||
{ | |||
if (string.IsNullOrWhiteSpace(content)) | |||
object model; | |||
if (parameterType == typeof(string)) | |||
{ | |||
if (string.IsNullOrWhiteSpace(content)) | |||
{ | |||
model = null; | |||
} | |||
else | |||
{ | |||
model = content; | |||
} | |||
} | |||
else if (string.IsNullOrWhiteSpace(content)) | |||
{ | |||
// Other than the StringConverter, converters Trim() the value then throw if the result is empty. | |||
model = null; | |||
} | |||
else | |||
{ | |||
model = content; | |||
model = _typeConverter.ConvertFrom( | |||
context: null, | |||
culture: CultureInfo.CurrentCulture, | |||
value: content); | |||
} | |||
if (model == null && !IsReferenceOrNullableType(parameterType)) | |||
{ | |||
return Task.FromResult(ModelBindingResult.Failed()); | |||
} | |||
else | |||
{ | |||
return Task.FromResult(ModelBindingResult.Success(model)); | |||
} | |||
} | |||
else if (string.IsNullOrWhiteSpace(content)) | |||
{ | |||
// Other than the StringConverter, converters Trim() the value then throw if the result is empty. | |||
model = null; | |||
} | |||
else | |||
{ | |||
model = _typeConverter.ConvertFrom( | |||
context: null, | |||
culture: CultureInfo.CurrentCulture, | |||
value: content); | |||
} | |||
if (model == null && !IsReferenceOrNullableType(parameterType)) | |||
{ | |||
return Task.FromResult(ModelBindingResult.Failed()); | |||
} | |||
else | |||
catch (Exception exception) | |||
{ | |||
return Task.FromResult(ModelBindingResult.Success(model)); | |||
var isFormatException = exception is FormatException; | |||
if (!isFormatException && exception.InnerException != null) | |||
{ | |||
// TypeConverter throws System.Exception wrapping the FormatException, | |||
// so we capture the inner exception. | |||
exception = ExceptionDispatchInfo.Capture(exception.InnerException).SourceException; | |||
} | |||
throw exception; | |||
} | |||
} | |||
@@ -1,9 +1,9 @@ | |||
using System; | |||
using System.Collections.Concurrent; | |||
using System.ComponentModel; | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using DotNetCore.CAP.Abstractions.ModelBinding; | |||
using DotNetCore.CAP.Infrastructure; | |||
namespace DotNetCore.CAP.Internal | |||
{ | |||
@@ -40,9 +40,7 @@ namespace DotNetCore.CAP.Internal | |||
{ | |||
return binder; | |||
} | |||
var type = parameterInfo.ParameterType; | |||
var isComplexType = !TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string)); | |||
if (!isComplexType) | |||
if (!Helper.IsComplexType(parameterInfo.ParameterType)) | |||
{ | |||
binder = new SimpleTypeModelBinder(parameterInfo); | |||
} | |||
@@ -14,6 +14,7 @@ namespace DotNetCore.CAP | |||
private static readonly Action<ILogger, string, string, Exception> _enqueuingReceivdeMessage; | |||
private static readonly Action<ILogger, string, Exception> _executingConsumerMethod; | |||
private static readonly Action<ILogger, string, Exception> _receivedMessageRetryExecuting; | |||
private static readonly Action<ILogger, string, string, string, Exception> _modelBinderFormattingException; | |||
private static Action<ILogger, Exception> _jobFailed; | |||
private static Action<ILogger, Exception> _jobFailedWillRetry; | |||
@@ -46,12 +47,12 @@ namespace DotNetCore.CAP | |||
_enqueuingSentMessage = LoggerMessage.Define<string, string>( | |||
LogLevel.Debug, | |||
2, | |||
"Enqueuing a topic to the sent message store. NameKey: {NameKey}. Content: {Content}"); | |||
"Enqueuing a topic to the sent message store. NameKey: '{NameKey}' Content: '{Content}'."); | |||
_enqueuingReceivdeMessage = LoggerMessage.Define<string, string>( | |||
LogLevel.Debug, | |||
2, | |||
"Enqueuing a topic to the received message store. NameKey: {NameKey}. Content: {Content}"); | |||
"Enqueuing a topic to the received message store. NameKey: '{NameKey}. Content: '{Content}'."); | |||
_executingConsumerMethod = LoggerMessage.Define<string>( | |||
LogLevel.Error, | |||
@@ -63,6 +64,12 @@ namespace DotNetCore.CAP | |||
5, | |||
"Received message topic method '{topicName}' failed to execute."); | |||
_modelBinderFormattingException = LoggerMessage.Define<string, string, string>( | |||
LogLevel.Error, | |||
5, | |||
"When call subscribe method, a parameter format conversion exception occurs. MethodName:'{MethodName}' ParameterName:'{ParameterName}' Content:'{Content}'." | |||
); | |||
_jobRetrying = LoggerMessage.Define<int>( | |||
LogLevel.Debug, | |||
3, | |||
@@ -154,5 +161,10 @@ namespace DotNetCore.CAP | |||
{ | |||
_exceptionOccuredWhileExecutingJob(logger, jobId, ex); | |||
} | |||
public static void ModelBinderFormattingException(this ILogger logger, string methodName, string parameterName, string content, Exception ex) | |||
{ | |||
_modelBinderFormattingException(logger, methodName, parameterName, content, ex); | |||
} | |||
} | |||
} |