public Task <IBinding> TryCreateAsync(BindingProviderContext context) { if (context == null) { throw new ArgumentNullException("context"); } ParameterInfo parameter = context.Parameter; TAttribute attributeSource = parameter.GetCustomAttribute <TAttribute>(inherit: false); if (attributeSource == null) { return(Task.FromResult <IBinding>(null)); } var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, _nameResolver); IBinding binding = new Binding(cloner, _builder, parameter); return(Task.FromResult(binding)); }
public Task <IBinding> TryCreateAsync(BindingProviderContext context) { if (context == null) { throw new ArgumentNullException("context"); } Type parametertype = context.Parameter.ParameterType; var attr = context.Parameter.GetCustomAttribute <TAttribute>(); var cloner = new AttributeCloner <TAttribute>(attr, context.BindingDataContract, _nameResolver); var attrNameResolved = cloner.GetNameResolvedAttribute(); // This may do validation and throw too. bool canBind = _predicate(attrNameResolved, parametertype); if (!canBind) { return(Task.FromResult <IBinding>(null)); } return(_inner.TryCreateAsync(context)); }
public async Task <IBinding> TryCreateAsync(BindingProviderContext context) { var attr = context.Parameter.GetCustomAttribute <TAttribute>(); if (attr == null) { return(null); } if (_validator != null) { // Expected this will throw on errors. Type parameterType = context.Parameter.ParameterType; var cloner = new AttributeCloner <TAttribute>(attr, context.BindingDataContract, _nameResolver); var attrNameResolved = cloner.GetNameResolvedAttribute(); _validator(attrNameResolved, parameterType); } foreach (IBindingProvider provider in _providers) { IBinding binding = await provider.TryCreateAsync(context); if (binding != null) { return(binding); } } // Nobody claimed it. string resourceName = typeof(TAttribute).Name; const string Suffix = "Attribute"; if (resourceName.EndsWith(Suffix)) { resourceName = resourceName.Substring(0, resourceName.Length - Suffix.Length); } throw new InvalidOperationException("Can't bind " + resourceName + " to type '" + context.Parameter.ParameterType + "'."); }
private static Func <TAttribute, IValueProvider> BuildIAsyncCollectorArgument <TAttribute, TMessage, TMessageSrc>( IConverterManager cm, Func <TAttribute, IAsyncCollector <TMessage> > buildFromAttribute, AttributeCloner <TAttribute> cloner) where TAttribute : Attribute { Func <TMessageSrc, TAttribute, TMessage> convert = cm.GetConverter <TMessageSrc, TMessage, TAttribute>(); if (convert == null) { ThrowMissingConversionError(typeof(TMessageSrc)); } Func <TAttribute, IValueProvider> argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); IAsyncCollector <TMessageSrc> obj = new TypedAsyncCollectorAdapter <TMessageSrc, TMessage, TAttribute>( raw, convert, attrResolved); var invokeString = cloner.GetInvokeString(attrResolved); return(new AsyncCollectorValueProvider <IAsyncCollector <TMessageSrc>, TMessage>(obj, raw, invokeString)); }; return(argumentBuilder); }
public async Task <IBinding> TryCreateAsync(BindingProviderContext context) { if (context == null) { throw new ArgumentNullException("context"); } var binding = await _inner.TryCreateAsync(context); if (binding != null) { // Only run the validator if the inner binding claims it. // Expected this will throw on errors. Type parameterType = context.Parameter.ParameterType; var attr = context.Parameter.GetCustomAttribute <TAttribute>(); var cloner = new AttributeCloner <TAttribute>(attr, context.BindingDataContract, _configuration, _nameResolver); var attrNameResolved = cloner.GetNameResolvedAttribute(); _validator(attrNameResolved, parameterType); } return(binding); }
public static TAttribute FromInvokeString(AttributeCloner <TAttribute> cloner, string invokeString) { if (invokeString == null) { throw new ArgumentNullException(nameof(invokeString)); } // Instantiating new attributes can be tricky since sometimes the arg is to the ctor and sometimes // its a property setter. AttributeCloner already solves this, so use it here to do the actual attribute instantiation. // This has an instantiation problem similar to what Attribute Cloner has if (invokeString[0] == '{') { var propertyValues = JsonConvert.DeserializeObject <IDictionary <string, string> >(invokeString); var attr = cloner.New(propertyValues); return(attr); } else { var attr = cloner.New(invokeString); return(attr); } }
private static Func <TAttribute, IValueProvider> BuildOutArgument <TAttribute, TMessage, TMessageSrc>( IConverterManager cm, Func <TAttribute, IAsyncCollector <TMessage> > buildFromAttribute, AttributeCloner <TAttribute> cloner) where TAttribute : Attribute { // Other Func <TMessageSrc, TAttribute, TMessage> convert = cm.GetConverter <TMessageSrc, TMessage, TAttribute>(); if (convert == null) { return(null); } Func <TAttribute, IValueProvider> argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); IAsyncCollector <TMessageSrc> obj = new TypedAsyncCollectorAdapter <TMessageSrc, TMessage, TAttribute>( raw, convert, attrResolved); string invokeString = cloner.GetInvokeString(attrResolved); return(new OutValueProvider <TMessageSrc>(obj, invokeString)); }; return(argumentBuilder); }
public BindingBase(AttributeCloner <TAttribute> cloner, ParameterDescriptor param) { Cloner = cloner; _param = param; }
// Helper to bind an IAsyncCollector<TMessage> to an 'out TUser' pattern, and invoke converter manager as needed. private static Func <TAttribute, IValueProvider> BindAsyncCollectorToOut <TAttribute, TMessage>( ParameterInfo parameter, IConverterManager converterManager, Func <TAttribute, IAsyncCollector <TMessage> > buildFromAttribute, AttributeCloner <TAttribute> cloner) where TAttribute : Attribute { Func <TAttribute, IValueProvider> argumentBuilder; Type elementType = parameter.ParameterType.GetElementType(); // How should "out byte[]" bind? // If there's an explicit "byte[] --> TMessage" converter, then that takes precedence. // Else, bind over an array of "byte --> TMessage" converters argumentBuilder = DynamicInvokeBuildOutArgument(elementType, converterManager, buildFromAttribute, cloner); if (argumentBuilder != null) { } else if (elementType.IsArray) { if (elementType == typeof(TMessage[])) { argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); var invokeString = cloner.GetInvokeString(attrResolved); return(new OutArrayValueProvider <TMessage>(raw, invokeString)); }; } else { // out TMessage[] var e2 = elementType.GetElementType(); argumentBuilder = DynamicBuildOutArrayArgument(e2, converterManager, buildFromAttribute, cloner); } } else { // Single enqueue // out TMessage if (elementType == typeof(TMessage)) { argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); var invokeString = cloner.GetInvokeString(attrResolved); return(new OutValueProvider <TMessage>(raw, invokeString)); }; } } // For out-param, give some rich errors. if (argumentBuilder == null) { if (typeof(IEnumerable).IsAssignableFrom(elementType)) { throw new InvalidOperationException( "Enumerable types are not supported. Use ICollector<T> or IAsyncCollector<T> instead."); } else if (typeof(object) == elementType) { throw new InvalidOperationException("Object element types are not supported."); } } return(argumentBuilder); }
// Helper to bind an IAsyncCollector<TMessage> raw object to an IAsyncCollector<TUser> or ICollecter<TUser> and invoke converter manager as needed. private static Func <TAttribute, IValueProvider> BindAsyncCollectorToInterface <TAttribute, TMessage>(IConverterManager converterManager, Func <TAttribute, IAsyncCollector <TMessage> > buildFromAttribute, AttributeCloner <TAttribute> cloner, Type parameterType, Func <TAttribute, IValueProvider> argumentBuilder) where TAttribute : Attribute { var genericType = parameterType.GetGenericTypeDefinition(); var elementType = parameterType.GetGenericArguments()[0]; if (genericType == typeof(IAsyncCollector <>)) { if (elementType == typeof(TMessage)) { // Bind to IAsyncCollector<TMessage>. This is the "purest" binding, no adaption needed. argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); var invokeString = cloner.GetInvokeString(attrResolved); return(new AsyncCollectorValueProvider <IAsyncCollector <TMessage>, TMessage>(raw, raw, invokeString)); }; } else { // Bind to IAsyncCollector<T> // Get a converter from T to TMessage argumentBuilder = DynamicInvokeBuildIAsyncCollectorArgument(elementType, converterManager, buildFromAttribute, cloner); } } else if (genericType == typeof(ICollector <>)) { if (elementType == typeof(TMessage)) { // Bind to ICollector<TMessage> This just needs an Sync/Async wrapper argumentBuilder = (attrResolved) => { IAsyncCollector <TMessage> raw = buildFromAttribute(attrResolved); var invokeString = cloner.GetInvokeString(attrResolved); ICollector <TMessage> obj = new SyncAsyncCollectorAdapter <TMessage>(raw); return(new AsyncCollectorValueProvider <ICollector <TMessage>, TMessage>(obj, raw, invokeString)); }; } else { // Bind to ICollector<T>. // This needs both a conversion from T to TMessage and an Sync/Async wrapper argumentBuilder = DynamicInvokeBuildICollectorArgument(elementType, converterManager, buildFromAttribute, cloner); } } return(argumentBuilder); }
internal static IBinding BindCollector <TAttribute, TMessage>( ParameterInfo parameter, INameResolver nameResolver, IConverterManager converterManager, IReadOnlyDictionary <string, Type> bindingDataContract, Func <TAttribute, IAsyncCollector <TMessage> > buildFromAttribute, Func <TAttribute, ParameterInfo, INameResolver, ParameterDescriptor> buildParamDescriptor = null, Func <TAttribute, ParameterInfo, INameResolver, Task <TAttribute> > hook = null) where TAttribute : Attribute { if (parameter == null) { throw new ArgumentNullException("parameter"); } Func <TAttribute, Task <TAttribute> > hookWrapper = null; if (hook != null) { hookWrapper = (attr) => hook(attr, parameter, nameResolver); } TAttribute attributeSource = parameter.GetCustomAttribute <TAttribute>(inherit: false); // ctor will do validation and throw. var cloner = new AttributeCloner <TAttribute>(attributeSource, bindingDataContract, nameResolver, hookWrapper); Type parameterType = parameter.ParameterType; Func <TAttribute, IValueProvider> argumentBuilder = null; // C# reflection trivia: If .IsOut = true, then IsGenericType = false. if (parameterType.IsGenericType) { argumentBuilder = BindAsyncCollectorToInterface(converterManager, buildFromAttribute, cloner, parameterType, argumentBuilder); } if (parameter.IsOut) { argumentBuilder = BindAsyncCollectorToOut(parameter, converterManager, buildFromAttribute, cloner); } if (argumentBuilder == null) { // Can't bind it. return(null); } ParameterDescriptor param; if (buildParamDescriptor != null) { param = buildParamDescriptor(attributeSource, parameter, nameResolver); } else { // If no parameter supplied, use a default. param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = "output" } }; } return(new AsyncCollectorBinding <TAttribute, TMessage>(param, argumentBuilder, cloner)); }
public static ExactBinding <TMessage> TryBuild( AsyncCollectorBindingProvider <TAttribute, TType> parent, Mode mode, BindingProviderContext context) { var patternMatcher = parent._patternMatcher; var parameter = context.Parameter; TAttribute attributeSource = parameter.GetCustomAttribute <TAttribute>(inherit: false); Func <TAttribute, Task <TAttribute> > hookWrapper = null; if (parent.PostResolveHook != null) { hookWrapper = (attrResolved) => parent.PostResolveHook(attrResolved, parameter, parent._nameResolver); } Func <object, object> buildFromAttribute; FuncConverter <TMessage, TAttribute, TType> converter = null; // Prefer the shortest route to creating the user type. // If TType matches the user type directly, then we should be able to directly invoke the builder in a single step. // TAttribute --> TUserType var checker = ConverterManager.GetTypeValidator <TType>(); if (checker.IsMatch(typeof(TMessage))) { buildFromAttribute = patternMatcher.TryGetConverterFunc( typeof(TAttribute), typeof(IAsyncCollector <TMessage>)); } else { var converterManager = parent._converterManager; // Try with a converter // Find a builder for : TAttribute --> TType // and then couple with a converter: TType --> TParameterType converter = converterManager.GetConverter <TMessage, TType, TAttribute>(); if (converter == null) { // Preserves legacy behavior. This means we can only have 1 async collector. // However, the collector's builder object can switch. throw NewMissingConversionError(typeof(TMessage)); } buildFromAttribute = patternMatcher.TryGetConverterFunc( typeof(TAttribute), typeof(IAsyncCollector <TType>)); } if (buildFromAttribute == null) { return(null); } ParameterDescriptor param; if (parent.BuildParameterDescriptor != null) { param = parent.BuildParameterDescriptor(attributeSource, parameter, parent._nameResolver); } else { param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = "output" } }; } var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, parent._nameResolver, hookWrapper); return(new ExactBinding <TMessage>(cloner, param, mode, buildFromAttribute, converter)); }
public static ExactBinding <TUserType> TryBuild( BindToInputBindingProvider <TAttribute, TType> parent, BindingProviderContext context) { var cm = parent._converterManager; var patternMatcher = parent._patternMatcher; var parameter = context.Parameter; TAttribute attributeSource = parameter.GetCustomAttribute <TAttribute>(inherit: false); Func <TAttribute, Task <TAttribute> > hookWrapper = null; if (parent.PostResolveHook != null) { hookWrapper = (attrResolved) => parent.PostResolveHook(attrResolved, parameter, parent._nameResolver); } var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, parent._nameResolver, hookWrapper); Func <object, object> buildFromAttribute; FuncConverter <TType, TAttribute, TUserType> converter = null; // Prefer the shortest route to creating the user type. // If TType matches the user type directly, then we should be able to directly invoke the builder in a single step. // TAttribute --> TUserType var checker = ConverterManager.GetTypeValidator <TType>(); if (checker.IsMatch(typeof(TUserType))) { buildFromAttribute = patternMatcher.TryGetConverterFunc(typeof(TAttribute), typeof(TUserType)); } else { // Try with a converter // Find a builder for : TAttribute --> TType // and then couple with a converter: TType --> TParameterType converter = cm.GetConverter <TType, TUserType, TAttribute>(); if (converter == null) { return(null); } buildFromAttribute = patternMatcher.TryGetConverterFunc(typeof(TAttribute), typeof(TType)); } if (buildFromAttribute == null) { return(null); } ParameterDescriptor param; if (parent.BuildParameterDescriptor != null) { param = parent.BuildParameterDescriptor(attributeSource, parameter, parent._nameResolver); } else { param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = "input" } }; } return(new ExactBinding <TUserType>(cloner, param, buildFromAttribute, converter)); }
public static ExactBinding <TMessage> TryBuild( AsyncCollectorBindingProvider <TAttribute, TType> parent, Mode mode, BindingProviderContext context) { var patternMatcher = parent._patternMatcher; var parameter = context.Parameter; var attributeSource = TypeUtility.GetResolvedAttribute <TAttribute>(parameter); FuncAsyncConverter buildFromAttribute; FuncAsyncConverter <TMessage, TType> converter = null; // Prefer the shortest route to creating the user type. // If TType matches the user type directly, then we should be able to directly invoke the builder in a single step. // TAttribute --> TUserType var checker = OpenType.FromType <TType>(); if (checker.IsMatch(typeof(TMessage))) { buildFromAttribute = patternMatcher.TryGetConverterFunc( typeof(TAttribute), typeof(IAsyncCollector <TMessage>)); } else { var converterManager = parent._converterManager; // Try with a converter // Find a builder for : TAttribute --> TType // and then couple with a converter: TType --> TParameterType converter = converterManager.GetConverter <TMessage, TType, TAttribute>(); if (converter == null) { // Preserves legacy behavior. This means we can only have 1 async collector. // However, the collector's builder object can switch. throw NewMissingConversionError(typeof(TMessage)); } buildFromAttribute = patternMatcher.TryGetConverterFunc( typeof(TAttribute), typeof(IAsyncCollector <TType>)); } if (buildFromAttribute == null) { context.BindingErrors.Add(String.Format(Constants.BindingAssemblyConflictMessage, typeof(TType).AssemblyQualifiedName, typeof(TMessage).AssemblyQualifiedName)); return(null); } ParameterDescriptor param; if (parent.BuildParameterDescriptor != null) { param = parent.BuildParameterDescriptor(attributeSource, parameter, parent._nameResolver); } else { param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = "output" } }; } var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, parent._nameResolver); return(new ExactBinding <TMessage>(cloner, param, mode, buildFromAttribute, converter)); }
public static IBinding TryBuild <TUserType>( BindToStreamBindingProvider <TAttribute> parent, BindingProviderContext context) { // Allowed Param types: // Stream // any T with a Stream --> T conversion // out T, with a Out<Stream,T> --> void conversion var parameter = context.Parameter; var parameterType = parameter.ParameterType; var attributeSource = TypeUtility.GetResolvedAttribute <TAttribute>(parameter); // Stream is either way; all other types are known. FileAccess?declaredAccess = GetFileAccessFromAttribute(attributeSource); Type argHelperType; bool isRead; IConverterManager cm = parent._converterManager; INameResolver nm = parent._nameResolver; IConfiguration config = parent._configuration; object converterParam = null; { if (parameter.IsOut) { var outConverter = cm.GetConverter <ApplyConversion <TUserType, Stream>, object, TAttribute>(); if (outConverter != null) { converterParam = outConverter; isRead = false; argHelperType = typeof(OutArgBaseValueProvider <>).MakeGenericType(typeof(TAttribute), typeof(TUserType)); } else { throw new InvalidOperationException($"No stream converter to handle {typeof(TUserType).FullName}."); } } else { var converter = cm.GetConverter <Stream, TUserType, TAttribute>(); if (converter != null) { converterParam = converter; if (parameterType == typeof(Stream)) { if (!declaredAccess.HasValue) { throw new InvalidOperationException("When binding to Stream, the attribute must specify a FileAccess direction."); } switch (declaredAccess.Value) { case FileAccess.Read: isRead = true; break; case FileAccess.Write: isRead = false; break; default: throw new NotImplementedException("ReadWrite access is not supported. Pick either Read or Write."); } } else { // For backwards compat, we recognize TextWriter as write; // anything else should explicitly set the FileAccess flag. if (typeof(TextWriter).IsAssignableFrom(typeof(TUserType))) { isRead = false; } else { isRead = true; } } argHelperType = typeof(ValueProvider <>).MakeGenericType(typeof(TAttribute), typeof(TUserType)); } else { // This rule can't bind. // Let another try. context.BindingErrors.Add(String.Format(Resource.BindingAssemblyConflictMessage, typeof(Stream).AssemblyQualifiedName, typeof(TUserType).AssemblyQualifiedName)); return(null); } } } VerifyAccessOrThrow(declaredAccess, isRead); if (!parent.IsSupportedByRule(isRead)) { return(null); } var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, config, nm); ParameterDescriptor param; if (parent.BuildParameterDescriptor != null) { param = parent.BuildParameterDescriptor(attributeSource, parameter, nm); } else { param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = isRead ? "Read Stream" : "Write Stream" } }; } var fileAccess = isRead ? FileAccess.Read : FileAccess.Write; IBinding binding = new StreamBinding(cloner, param, parent, argHelperType, parameterType, fileAccess, converterParam); return(binding); }
public static ExactBinding TryBuild( BindToInputBindingProvider <TAttribute, TType> parent, BindingProviderContext context) { var cm = parent._converterManager; var patternMatcher = parent._patternMatcher; var parameter = context.Parameter; var userType = parameter.ParameterType; var attributeSource = TypeUtility.GetResolvedAttribute <TAttribute>(parameter); var cloner = new AttributeCloner <TAttribute>(attributeSource, context.BindingDataContract, parent._configuration, parent._nameResolver); FuncAsyncConverter buildFromAttribute; FuncAsyncConverter converter = null; // Prefer the shortest route to creating the user type. // If TType matches the user type directly, then we should be able to directly invoke the builder in a single step. // TAttribute --> TUserType var checker = OpenType.FromType <TType>(); if (checker.IsMatch(userType)) { buildFromAttribute = patternMatcher.TryGetConverterFunc(typeof(TAttribute), userType); } else { // Try with a converter // Find a builder for : TAttribute --> TType // and then couple with a converter: TType --> TParameterType converter = cm.GetConverter <TAttribute>(typeof(TType), userType); if (converter == null) { var targetType = typeof(TType); context.BindingErrors.Add(String.Format(Resource.BindingAssemblyConflictMessage, targetType.AssemblyQualifiedName, userType.AssemblyQualifiedName)); return(null); } buildFromAttribute = patternMatcher.TryGetConverterFunc(typeof(TAttribute), typeof(TType)); } if (buildFromAttribute == null) { return(null); } ParameterDescriptor param; if (parent.BuildParameterDescriptor != null) { param = parent.BuildParameterDescriptor(attributeSource, parameter, parent._nameResolver); } else { param = new ParameterDescriptor { Name = parameter.Name, DisplayHints = new ParameterDisplayHints { Description = "input" } }; } return(new ExactBinding(cloner, param, buildFromAttribute, converter, userType)); }