Commit aa247b19 authored by yangxiaodong's avatar yangxiaodong

add model binder

parent 209bdcff
......@@ -20,7 +20,7 @@ namespace Cap.Consistency.Internal
}
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) {
......@@ -50,7 +50,7 @@ namespace Cap.Consistency.Internal
) {
var descriptor = new ConsumerExecutorDescriptor();
descriptor.Topic = new TopicInfo(attr.Name);
descriptor.Attribute = attr;
descriptor.MethodInfo = methodInfo;
descriptor.ImplTypeInfo = implType;
......
......@@ -4,6 +4,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Cap.Consistency.Abstractions;
using Cap.Consistency.Abstractions.ModelBinding;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
......@@ -14,15 +15,18 @@ namespace Cap.Consistency.Internal
{
protected readonly ILogger _logger;
protected readonly IServiceProvider _serviceProvider;
private readonly IModelBinder _modelBinder;
private readonly ObjectMethodExecutor _executor;
protected readonly ConsumerContext _consumerContext;
public ConsumerInvoker(ILogger logger,
IServiceProvider serviceProvider,
IModelBinder modelBinder,
ConsumerContext consumerContext) {
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_serviceProvider = serviceProvider;
_modelBinder = modelBinder;
_consumerContext = consumerContext ?? throw new ArgumentNullException(nameof(consumerContext));
_executor = ObjectMethodExecutor.Create(_consumerContext.ConsumerDescriptor.MethodInfo,
_consumerContext.ConsumerDescriptor.ImplTypeInfo);
......@@ -33,20 +37,26 @@ namespace Cap.Consistency.Internal
try {
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 {
var obj = ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, _consumerContext.ConsumerDescriptor.ImplTypeInfo.AsType());
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;
}
finally {
......
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using Cap.Consistency.Abstractions;
using Cap.Consistency.Abstractions.ModelBinding;
using Cap.Consistency.Infrastructure;
using Microsoft.Extensions.Logging;
......@@ -11,12 +12,15 @@ namespace Cap.Consistency.Internal
{
private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
private readonly IModelBinder _modelBinder;
public ConsumerInvokerFactory(
ILoggerFactory loggerFactory,
IModelBinder modelBinder,
IServiceProvider serviceProvider) {
_logger = loggerFactory.CreateLogger<ConsumerInvokerFactory>();
_modelBinder = modelBinder;
_serviceProvider = serviceProvider;
}
......@@ -24,7 +28,7 @@ namespace Cap.Consistency.Internal
var context = new ConsumerInvokerContext(consumerContext);
context.Result = new ConsumerInvoker(_logger, _serviceProvider, consumerContext);
context.Result = new ConsumerInvoker(_logger, _serviceProvider, _modelBinder, consumerContext);
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
public ConcurrentDictionary<string, ConsumerExecutorDescriptor> GetCandidatesMethods(TopicRouteContext routeContext) {
if (Entries == null) {
if (Entries.Count == 0) {
var executorCollection = _selector.SelectCandidates(routeContext);
foreach (var item in executorCollection) {
Entries.GetOrAdd(item.Topic.Name, item);
Entries.GetOrAdd(item.Attribute.Name, item);
}
}
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