Commit aa247b19 authored by yangxiaodong's avatar yangxiaodong

add model binder

parent 209bdcff
...@@ -20,7 +20,7 @@ namespace Cap.Consistency.Internal ...@@ -20,7 +20,7 @@ namespace Cap.Consistency.Internal
} }
public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) { public ConsumerExecutorDescriptor SelectBestCandidate(string key, IReadOnlyList<ConsumerExecutorDescriptor> executeDescriptor) {
return executeDescriptor.FirstOrDefault(x => x.Topic.Name == key); return executeDescriptor.FirstOrDefault(x => x.Attribute.Name == key);
} }
public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates(TopicRouteContext context) { public IReadOnlyList<ConsumerExecutorDescriptor> SelectCandidates(TopicRouteContext context) {
...@@ -50,7 +50,7 @@ namespace Cap.Consistency.Internal ...@@ -50,7 +50,7 @@ namespace Cap.Consistency.Internal
) { ) {
var descriptor = new ConsumerExecutorDescriptor(); var descriptor = new ConsumerExecutorDescriptor();
descriptor.Topic = new TopicInfo(attr.Name); descriptor.Attribute = attr;
descriptor.MethodInfo = methodInfo; descriptor.MethodInfo = methodInfo;
descriptor.ImplTypeInfo = implType; descriptor.ImplTypeInfo = implType;
......
...@@ -4,6 +4,7 @@ using System.Reflection; ...@@ -4,6 +4,7 @@ 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;
using Cap.Consistency.Abstractions.ModelBinding;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
...@@ -14,15 +15,18 @@ namespace Cap.Consistency.Internal ...@@ -14,15 +15,18 @@ namespace Cap.Consistency.Internal
{ {
protected readonly ILogger _logger; protected readonly ILogger _logger;
protected readonly IServiceProvider _serviceProvider; protected readonly IServiceProvider _serviceProvider;
private readonly IModelBinder _modelBinder;
private readonly ObjectMethodExecutor _executor; private readonly ObjectMethodExecutor _executor;
protected readonly ConsumerContext _consumerContext; protected readonly ConsumerContext _consumerContext;
public ConsumerInvoker(ILogger logger, public ConsumerInvoker(ILogger logger,
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
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;
_consumerContext = consumerContext ?? throw new ArgumentNullException(nameof(consumerContext)); _consumerContext = consumerContext ?? throw new ArgumentNullException(nameof(consumerContext));
_executor = ObjectMethodExecutor.Create(_consumerContext.ConsumerDescriptor.MethodInfo, _executor = ObjectMethodExecutor.Create(_consumerContext.ConsumerDescriptor.MethodInfo,
_consumerContext.ConsumerDescriptor.ImplTypeInfo); _consumerContext.ConsumerDescriptor.ImplTypeInfo);
...@@ -33,20 +37,26 @@ namespace Cap.Consistency.Internal ...@@ -33,20 +37,26 @@ namespace Cap.Consistency.Internal
try { try {
using (_logger.BeginScope("consumer invoker begin")) { using (_logger.BeginScope("consumer invoker begin")) {
_logger.LogDebug("Executing consumer Topic: {0}", _consumerContext.ConsumerDescriptor.Topic); _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);
var firstParameter = _executor.MethodParameters[0];
object firstParameterObj = null;
if (firstParameter != null) {
firstParameterObj = JsonConvert.DeserializeObject(bodyString, firstParameter.ParameterType);
}
_executor.Execute(obj, firstParameterObj);
if (_executor.MethodParameters.Length > 0) {
var firstParameter = _executor.MethodParameters[0];
var bindingContext = ModelBindingContext.CreateBindingContext(bodyString,
firstParameter.Name, firstParameter.ParameterType);
_modelBinder.BindModelAsync(bindingContext);
_executor.Execute(obj, bindingContext.Result);
}
else {
_executor.Execute(obj);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
finally { finally {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using Cap.Consistency.Abstractions; using Cap.Consistency.Abstractions;
using Cap.Consistency.Abstractions.ModelBinding;
using Cap.Consistency.Infrastructure; using Cap.Consistency.Infrastructure;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
...@@ -11,12 +12,15 @@ namespace Cap.Consistency.Internal ...@@ -11,12 +12,15 @@ namespace Cap.Consistency.Internal
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly IModelBinder _modelBinder;
public ConsumerInvokerFactory( public ConsumerInvokerFactory(
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IModelBinder modelBinder,
IServiceProvider serviceProvider) { IServiceProvider serviceProvider) {
_logger = loggerFactory.CreateLogger<ConsumerInvokerFactory>(); _logger = loggerFactory.CreateLogger<ConsumerInvokerFactory>();
_modelBinder = modelBinder;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
...@@ -24,7 +28,7 @@ namespace Cap.Consistency.Internal ...@@ -24,7 +28,7 @@ namespace Cap.Consistency.Internal
var context = new ConsumerInvokerContext(consumerContext); var context = new ConsumerInvokerContext(consumerContext);
context.Result = new ConsumerInvoker(_logger, _serviceProvider, consumerContext); context.Result = new ConsumerInvoker(_logger, _serviceProvider, _modelBinder, consumerContext);
return context.Result; return context.Result;
} }
......
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Cap.Consistency.Abstractions.ModelBinding;
using Newtonsoft.Json;
namespace Cap.Consistency.Internal
{
public class DefaultModelBinder : IModelBinder
{
private Func<object> _modelCreator;
public Task BindModelAsync(ModelBindingContext bindingContext) {
if (bindingContext.Model == null) {
bindingContext.Model = CreateModel(bindingContext);
}
bindingContext.Result = JsonConvert.DeserializeObject(bindingContext.Values, bindingContext.ModelType);
return Task.CompletedTask;
}
protected virtual object CreateModel(ModelBindingContext bindingContext) {
if (bindingContext == null) {
throw new ArgumentNullException(nameof(bindingContext));
}
if (_modelCreator == null) {
var modelTypeInfo = bindingContext.ModelType.GetTypeInfo();
if (modelTypeInfo.IsAbstract || modelTypeInfo.GetConstructor(Type.EmptyTypes) == null) {
throw new InvalidOperationException();
}
_modelCreator = Expression
.Lambda<Func<object>>(Expression.New(bindingContext.ModelType))
.Compile();
}
return _modelCreator();
}
}
}
...@@ -18,13 +18,13 @@ namespace Cap.Consistency.Internal ...@@ -18,13 +18,13 @@ namespace Cap.Consistency.Internal
public ConcurrentDictionary<string, ConsumerExecutorDescriptor> GetCandidatesMethods(TopicRouteContext routeContext) { public ConcurrentDictionary<string, ConsumerExecutorDescriptor> GetCandidatesMethods(TopicRouteContext routeContext) {
if (Entries == null) { 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.Topic.Name, item); Entries.GetOrAdd(item.Attribute.Name, item);
} }
} }
return Entries; return Entries;
......
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Cap.Consistency.Internal
{
/// <summary>
/// Provides access to the combined list of attributes associated a <see cref="Type"/> or property.
/// </summary>
public class ModelAttributes
{
/// <summary>
/// Creates a new <see cref="ModelAttributes"/> for a <see cref="Type"/>.
/// </summary>
/// <param name="typeAttributes">The set of attributes for the <see cref="Type"/>.</param>
public ModelAttributes(IEnumerable<object> typeAttributes) {
if (typeAttributes == null) {
throw new ArgumentNullException(nameof(typeAttributes));
}
Attributes = typeAttributes.ToArray();
TypeAttributes = Attributes;
}
/// <summary>
/// Creates a new <see cref="ModelAttributes"/> for a property.
/// </summary>
/// <param name="propertyAttributes">The set of attributes for the property.</param>
/// <param name="typeAttributes">
/// The set of attributes for the property's <see cref="Type"/>. See <see cref="PropertyInfo.PropertyType"/>.
/// </param>
public ModelAttributes(IEnumerable<object> propertyAttributes, IEnumerable<object> typeAttributes) {
if (propertyAttributes == null) {
throw new ArgumentNullException(nameof(propertyAttributes));
}
if (typeAttributes == null) {
throw new ArgumentNullException(nameof(typeAttributes));
}
PropertyAttributes = propertyAttributes.ToArray();
TypeAttributes = typeAttributes.ToArray();
Attributes = PropertyAttributes.Concat(TypeAttributes).ToArray();
}
/// <summary>
/// Gets the set of all attributes. If this instance represents the attributes for a property, the attributes
/// on the property definition are before those on the property's <see cref="Type"/>.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the set of attributes on the property, or <c>null</c> if this instance represents the attributes
/// for a <see cref="Type"/>.
/// </summary>
public IReadOnlyList<object> PropertyAttributes { get; }
/// <summary>
/// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property,
/// then <see cref="TypeAttributes"/> contains attributes retrieved from
/// <see cref="PropertyInfo.PropertyType"/>.
/// </summary>
public IReadOnlyList<object> TypeAttributes { get; }
/// <summary>
/// Gets the attributes for the given <paramref name="property"/>.
/// </summary>
/// <param name="type">The <see cref="Type"/> in which caller found <paramref name="property"/>.
/// </param>
/// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved.
/// </param>
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns>
public static ModelAttributes GetAttributesForProperty(Type type, PropertyInfo property) {
if (type == null) {
throw new ArgumentNullException(nameof(type));
}
if (property == null) {
throw new ArgumentNullException(nameof(property));
}
var propertyAttributes = property.GetCustomAttributes();
var typeAttributes = property.PropertyType.GetTypeInfo().GetCustomAttributes();
return new ModelAttributes(propertyAttributes, typeAttributes);
}
/// <summary>
/// Gets the attributes for the given <paramref name="type"/>.
/// </summary>
/// <param name="type">The <see cref="Type"/> for which attributes need to be resolved.
/// </param>
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="Type"/>.</returns>
public static ModelAttributes GetAttributesForType(Type type) {
if (type == null) {
throw new ArgumentNullException(nameof(type));
}
var attributes = type.GetTypeInfo().GetCustomAttributes();
return new ModelAttributes(attributes);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Cap.Consistency
{
public class TopicInfo
{
public TopicInfo(string topicName) : this(topicName, 0) {}
public TopicInfo(string topicName, int partition) : this(topicName, partition, 0) {}
public TopicInfo(string topicName, int partition, long offset) {
Name = topicName;
Offset = offset;
Partition = partition;
}
public string Name { get; }
public int Partition { get; }
public long Offset { get; }
public bool IsPartition { get { return Partition == 0; } }
public bool IsOffset { get { return Offset == 0; } }
public override string ToString() {
return Name;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment