Browse Source

add model binder

master
yangxiaodong 7 years ago
parent
commit
aa247b1979
7 changed files with 181 additions and 49 deletions
  1. +2
    -2
      src/Cap.Consistency/Internal/ConsumerExcutorSelector.cs
  2. +18
    -8
      src/Cap.Consistency/Internal/ConsumerInvoker.cs
  3. +5
    -1
      src/Cap.Consistency/Internal/ConsumerInvokerFactory.cs
  4. +48
    -0
      src/Cap.Consistency/Internal/DefaultModelBinder.cs
  5. +2
    -2
      src/Cap.Consistency/Internal/MethodMatcherCache.cs
  6. +106
    -0
      src/Cap.Consistency/Internal/ModelAttributes.cs
  7. +0
    -36
      src/Cap.Consistency/Internal/TopicInfo.cs

+ 2
- 2
src/Cap.Consistency/Internal/ConsumerExcutorSelector.cs View File

@@ -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;



+ 18
- 8
src/Cap.Consistency/Internal/ConsumerInvoker.cs View File

@@ -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 {


+ 5
- 1
src/Cap.Consistency/Internal/ConsumerInvokerFactory.cs View File

@@ -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;
}


+ 48
- 0
src/Cap.Consistency/Internal/DefaultModelBinder.cs View File

@@ -0,0 +1,48 @@
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();
}
}
}

+ 2
- 2
src/Cap.Consistency/Internal/MethodMatcherCache.cs View File

@@ -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;


+ 106
- 0
src/Cap.Consistency/Internal/ModelAttributes.cs View File

@@ -0,0 +1,106 @@
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);
}
}
}

+ 0
- 36
src/Cap.Consistency/Internal/TopicInfo.cs View File

@@ -1,36 +0,0 @@
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;
}

}
}

Loading…
Cancel
Save