diff --git a/.gitignore b/.gitignore index 116ed1edb..5b6ffb3cc 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,4 @@ NzbDrone.Web/_backboneApp/.idea/. *.test-cache *.sqo *.userprefs +*/test-results/* diff --git a/Autofac.Integration.Mvc/Autofac.Integration.Mvc.csproj b/Autofac.Integration.Mvc/Autofac.Integration.Mvc.csproj new file mode 100644 index 000000000..14ed3a232 --- /dev/null +++ b/Autofac.Integration.Mvc/Autofac.Integration.Mvc.csproj @@ -0,0 +1,133 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {DD874E64-C7EC-464D-925F-CF4A709EDEEF} + Library + Properties + Autofac.Integration.Mvc + Autofac.Integration.Mvc + v4.0 + 512 + ..\..\..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\..\..\Build\Full.ruleset + false + bin\Debug\Autofac.Integration.Mvc.xml + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\..\..\Build\Full.ruleset + true + bin\Release\Autofac.Integration.Mvc.xml + + + false + + + ..\..\..\Build\SharedKey.snk + + + + ..\packages\Autofac.3.0.1\lib\net40\Autofac.dll + + + False + ..\Libraries\MVC3\Microsoft.Web.Infrastructure.dll + + + + + + False + ..\Libraries\MVC3\System.Web.Helpers.dll + + + False + ..\Libraries\MVC3\System.Web.Mvc.dll + + + False + ..\Libraries\MVC3\System.Web.Razor.dll + + + False + ..\Libraries\MVC3\System.Web.WebPages.dll + + + False + ..\Libraries\MVC3\System.Web.WebPages.Deployment.dll + + + False + ..\Libraries\MVC3\System.Web.WebPages.Razor.dll + + + + + + + + + + True + True + RegistrationExtensionsResources.resx + + + + + + + + + + + True + True + RequestLifetimeScopeProviderResources.resx + + + + + + ResXFileCodeGenerator + RegistrationExtensionsResources.Designer.cs + + + ResXFileCodeGenerator + RequestLifetimeScopeProviderResources.Designer.cs + Designer + + + + + + + + + \ No newline at end of file diff --git a/Autofac.Integration.Mvc/AutofacDependencyResolver.cs b/Autofac.Integration.Mvc/AutofacDependencyResolver.cs new file mode 100644 index 000000000..e46a2c2a1 --- /dev/null +++ b/Autofac.Integration.Mvc/AutofacDependencyResolver.cs @@ -0,0 +1,164 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Web.Mvc; + +namespace Autofac.Integration.Mvc +{ + /// + /// Autofac implementation of the interface. + /// + public class AutofacDependencyResolver : IDependencyResolver + { + readonly ILifetimeScope _container; + readonly Action _configurationAction; + ILifetimeScopeProvider _lifetimeScopeProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The container that nested lifetime scopes will be create from. + public AutofacDependencyResolver(ILifetimeScope container) + { + if (container == null) throw new ArgumentNullException("container"); + _container = container; + } + + /// + /// Initializes a new instance of the class. + /// + /// The container that nested lifetime scopes will be create from. + /// Action on a + /// that adds component registations visible only in nested lifetime scopes. + public AutofacDependencyResolver(ILifetimeScope container, Action configurationAction) + : this(container) + { + if (configurationAction == null) throw new ArgumentNullException("configurationAction"); + _configurationAction = configurationAction; + } + + /// + /// Initializes a new instance of the class. + /// + /// The container that nested lifetime scopes will be create from. + /// A implementation for + /// creating new lifetime scopes. + public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider) : + this(container) + { + if (lifetimeScopeProvider == null) throw new ArgumentNullException("lifetimeScopeProvider"); + _lifetimeScopeProvider = lifetimeScopeProvider; + } + + /// + /// Initializes a new instance of the class. + /// + /// The container that nested lifetime scopes will be create from. + /// A implementation for + /// creating new lifetime scopes. + /// Action on a + /// that adds component registations visible only in nested lifetime scopes. + public AutofacDependencyResolver(ILifetimeScope container, ILifetimeScopeProvider lifetimeScopeProvider, Action configurationAction) + : this(container, lifetimeScopeProvider) + { + if (configurationAction == null) throw new ArgumentNullException("configurationAction"); + _configurationAction = configurationAction; + } + + /// + /// Gets the Autofac implementation of the dependency resolver. + /// + public static AutofacDependencyResolver Current + { + get + { + // Issue 351: We can't necessarily cast the current dependency resolver + // to AutofacDependencyResolver because diagnostic systems like Glimpse + // will wrap/proxy the resolver. Instead we need to register the resolver + // on the fly with the request lifetime scope and resolve it accordingly. + return DependencyResolver.Current.GetService(); + } + } + + /// + /// The lifetime containing components for processing the current HTTP request. + /// + public ILifetimeScope RequestLifetimeScope + { + get + { + // Issue 351: Register the AutofacDependencyResolver with + // the request lifetime scope so the current resolver can + // be retrieved without having to cast it directly to + // this specific type. + Action composite = builder => + { + if (this._configurationAction != null) + { + this._configurationAction(builder); + } + builder.RegisterInstance(this).As(); + }; + if (_lifetimeScopeProvider == null) + { + _lifetimeScopeProvider = new RequestLifetimeScopeProvider(_container); + } + return _lifetimeScopeProvider.GetLifetimeScope(composite); + } + } + + /// + /// Gets the application container that was provided to the constructor. + /// + public ILifetimeScope ApplicationContainer + { + get { return _container; } + } + + /// + /// Get a single instance of a service. + /// + /// Type of the service. + /// The single instance if resolved; otherwise, null. + public object GetService(Type serviceType) + { + return RequestLifetimeScope.ResolveOptional(serviceType); + } + + /// + /// Gets all available instances of a services. + /// + /// Type of the service. + /// The list of instances if any were resolved; otherwise, an empty list. + public IEnumerable GetServices(Type serviceType) + { + var enumerableServiceType = typeof(IEnumerable<>).MakeGenericType(serviceType); + var instance = RequestLifetimeScope.Resolve(enumerableServiceType); + return (IEnumerable)instance; + } + } +} diff --git a/Autofac.Integration.Mvc/AutofacFilterProvider.cs b/Autofac.Integration.Mvc/AutofacFilterProvider.cs new file mode 100644 index 000000000..c6ad44cae --- /dev/null +++ b/Autofac.Integration.Mvc/AutofacFilterProvider.cs @@ -0,0 +1,171 @@ +// This software is part of the Autofac IoC container +// Copyright © 2012 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web.Mvc; +using System.Web.Mvc.Async; +using Autofac.Features.Metadata; + +namespace Autofac.Integration.Mvc +{ + /// + /// Defines a filter provider for filter attributes that performs property injection. + /// + public class AutofacFilterProvider : FilterAttributeFilterProvider + { + class FilterContext + { + public ActionDescriptor ActionDescriptor { get; set; } + public ILifetimeScope LifetimeScope { get; set; } + public Type ControllerType { get; set; } + public List Filters { get; set; } + } + + internal static string ActionFilterMetadataKey = "AutofacMvcActionFilter"; + + internal static string AuthorizationFilterMetadataKey = "AutofacMvcAuthorizationFilter"; + + internal static string ExceptionFilterMetadataKey = "AutofacMvcExceptionFilter"; + + internal static string ResultFilterMetadataKey = "AutofacMvcResultFilter"; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The false constructor parameter passed to base here ensures that attribute instances are not cached. + /// + public AutofacFilterProvider() : base(false) + { + } + + /// + /// Aggregates the filters from all of the filter providers into one collection. + /// + /// The controller context. + /// The action descriptor. + /// + /// The collection filters from all of the filter providers with properties injected. + /// + /// + /// Thrown if is . + /// + public override IEnumerable GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) + { + if (controllerContext == null) + { + throw new ArgumentNullException("controllerContext"); + } + var filters = base.GetFilters(controllerContext, actionDescriptor).ToList(); + var lifetimeScope = AutofacDependencyResolver.Current.RequestLifetimeScope; + + if (lifetimeScope != null) + { + foreach (var filter in filters) + lifetimeScope.InjectProperties(filter.Instance); + + var controllerType = controllerContext.Controller.GetType(); + + var filterContext = new FilterContext + { + ActionDescriptor = actionDescriptor, + LifetimeScope = lifetimeScope, + ControllerType = controllerType, + Filters = filters + }; + + ResolveControllerScopedFilters(filterContext); + + ResolveActionScopedFilters(filterContext, d => d.MethodInfo); + ResolveActionScopedFilters(filterContext, d => d.AsyncMethodInfo); + } + + return filters.ToArray(); + } + + static void ResolveControllerScopedFilters(FilterContext filterContext) + { + ResolveControllerScopedFilter(filterContext, ActionFilterMetadataKey); + ResolveControllerScopedFilter(filterContext, AuthorizationFilterMetadataKey); + ResolveControllerScopedFilter(filterContext, ExceptionFilterMetadataKey); + ResolveControllerScopedFilter(filterContext, ResultFilterMetadataKey); + } + + static void ResolveControllerScopedFilter(FilterContext filterContext, string metadataKey) + where TFilter : class + { + var actionFilters = filterContext.LifetimeScope.Resolve>>>(); + + foreach (var actionFilter in actionFilters.Where(a => a.Metadata.ContainsKey(metadataKey) && a.Metadata[metadataKey] is FilterMetadata)) + { + var metadata = (FilterMetadata)actionFilter.Metadata[metadataKey]; + if (metadata.ControllerType != null + && metadata.ControllerType.IsAssignableFrom(filterContext.ControllerType) + && metadata.FilterScope == FilterScope.Controller + && metadata.MethodInfo == null) + { + var filter = new Filter(actionFilter.Value.Value, FilterScope.Controller, metadata.Order); + filterContext.Filters.Add(filter); + } + } + } + + static void ResolveActionScopedFilters(FilterContext filterContext, Func methodSelector) + where T : ActionDescriptor + { + var actionDescriptor = filterContext.ActionDescriptor as T; + if (actionDescriptor == null) return; + + var methodInfo = methodSelector(actionDescriptor); + + ResolveActionScopedFilter(filterContext, methodInfo, ActionFilterMetadataKey); + ResolveActionScopedFilter(filterContext, methodInfo, AuthorizationFilterMetadataKey); + ResolveActionScopedFilter(filterContext, methodInfo, ExceptionFilterMetadataKey); + ResolveActionScopedFilter(filterContext, methodInfo, ResultFilterMetadataKey); + } + + static void ResolveActionScopedFilter(FilterContext filterContext, MethodInfo methodInfo, string metadataKey) + where TFilter : class + { + var actionFilters = filterContext.LifetimeScope.Resolve>>>(); + + foreach (var actionFilter in actionFilters.Where(a => a.Metadata.ContainsKey(metadataKey) && a.Metadata[metadataKey] is FilterMetadata)) + { + var metadata = (FilterMetadata)actionFilter.Metadata[metadataKey]; + if (metadata.ControllerType != null + && metadata.ControllerType.IsAssignableFrom(filterContext.ControllerType) + && metadata.FilterScope == FilterScope.Action + && metadata.MethodInfo.GetBaseDefinition() == methodInfo.GetBaseDefinition()) + { + var filter = new Filter(actionFilter.Value.Value, FilterScope.Action, metadata.Order); + filterContext.Filters.Add(filter); + } + } + } + } +} diff --git a/Autofac.Integration.Mvc/AutofacModelBinderProvider.cs b/Autofac.Integration.Mvc/AutofacModelBinderProvider.cs new file mode 100644 index 000000000..3323ee4f4 --- /dev/null +++ b/Autofac.Integration.Mvc/AutofacModelBinderProvider.cs @@ -0,0 +1,59 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Autofac.Features.Metadata; + +namespace Autofac.Integration.Mvc +{ + /// + /// Autofac implementation of the interface. + /// + public class AutofacModelBinderProvider : IModelBinderProvider + { + /// + /// Metadata key for the supported model types. + /// + internal static readonly string MetadataKey = "SupportedModelTypes"; + + /// + /// Gets the model binder associated with the provided model type. + /// + /// Type of the model. + /// An instance if found; otherwise, null. + public IModelBinder GetBinder(Type modelType) + { + var modelBinders = DependencyResolver.Current.GetServices>>(); + + var modelBinder = modelBinders + .Where(binder => binder.Metadata.ContainsKey(MetadataKey)) + .FirstOrDefault(binder => ((List)binder.Metadata[MetadataKey]).Contains(modelType)); + return (modelBinder != null) ? modelBinder.Value.Value : null; + } + } +} \ No newline at end of file diff --git a/Autofac.Integration.Mvc/AutofacWebTypesModule.cs b/Autofac.Integration.Mvc/AutofacWebTypesModule.cs new file mode 100644 index 000000000..b362b0564 --- /dev/null +++ b/Autofac.Integration.Mvc/AutofacWebTypesModule.cs @@ -0,0 +1,178 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System.Web; +using System.Web.Hosting; +using System.Web.Mvc; +using System.Web.Routing; + +namespace Autofac.Integration.Mvc +{ + /// + /// Dependency injection module that registers abstractions for common + /// web application properties. + /// + /// + /// + /// This is primarily used during + /// application startup (in Global.asax) to register + /// mappings from commonly referenced contextual application properties + /// to their corresponding abstraction. + /// + /// + /// The following mappings are made: + /// + /// + /// + /// Common Construct + /// Abstraction + /// + /// + /// HttpContext.Current + /// + /// + /// + /// HttpContext.Current.Application + /// + /// + /// + /// HttpContext.Current.Request + /// + /// + /// + /// HttpContext.Current.Request.Browser + /// + /// + /// + /// HttpContext.Current.Request.Files + /// + /// + /// + /// HttpContext.Current.Request.RequestContext + /// + /// + /// + /// HttpContext.Current.Response + /// + /// + /// + /// HttpContext.Current.Response.Cache + /// + /// + /// + /// HttpContext.Current.Server + /// + /// + /// + /// HttpContext.Current.Session + /// + /// + /// + /// HostingEnvironment.VirtualPathProvider + /// + /// + /// + /// + /// In addition, the type is registered + /// for construction based on the current . + /// + /// + /// The lifetime for each of these items is one web request. + /// + /// + public class AutofacWebTypesModule : Module + { + /// + /// Registers web abstractions with dependency injection. + /// + /// + /// The in which registration + /// should take place. + /// + /// + /// + /// This method registers mappings between common current context-related + /// web constructs and their associated abstract counterparts. See + /// for the complete + /// list of mappings that get registered. + /// + /// + protected override void Load(ContainerBuilder builder) + { + builder.Register(c => new HttpContextWrapper(HttpContext.Current)) + .As() + .InstancePerHttpRequest(); + + // HttpContext properties + builder.Register(c => c.Resolve().Request) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().Response) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().Server) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().Session) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().Application) + .As() + .InstancePerHttpRequest(); + + // HttpRequest properties + builder.Register(c => c.Resolve().Browser) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().Files) + .As() + .InstancePerHttpRequest(); + + builder.Register(c => c.Resolve().RequestContext) + .As() + .InstancePerHttpRequest(); + + // HttpResponse properties + builder.Register(c => c.Resolve().Cache) + .As() + .InstancePerHttpRequest(); + + // HostingEnvironment properties + builder.Register(c => HostingEnvironment.VirtualPathProvider) + .As() + .InstancePerHttpRequest(); + + // MVC types + builder.Register(c => new UrlHelper(c.Resolve())) + .As() + .InstancePerHttpRequest(); + } + } +} diff --git a/Autofac.Integration.Mvc/ExtensibleActionInvoker.cs b/Autofac.Integration.Mvc/ExtensibleActionInvoker.cs new file mode 100644 index 000000000..179d72f3a --- /dev/null +++ b/Autofac.Integration.Mvc/ExtensibleActionInvoker.cs @@ -0,0 +1,87 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Web.Mvc; + +namespace Autofac.Integration.Mvc +{ + /// + /// Injects services from the container into the ASP.NET MVC invocation pipeline. + /// This is a Async Controller Action Invoker which can be used for both async and non-async scenarios + /// + /// + /// + /// Action methods can include parameters that will be resolved from the + /// container, along with regular parameters. + /// + /// + public class ExtensibleActionInvoker : System.Web.Mvc.Async.AsyncControllerActionInvoker + { + /// + /// Gets the parameter value. + /// + /// The controller context.The parameter descriptor. + /// + /// The parameter value. + /// + /// + /// Thrown if is . + /// + protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) + { + if (parameterDescriptor == null) + { + throw new ArgumentNullException("parameterDescriptor"); + } + + // Only resolve parameter values if injection is enabled. + var context = AutofacDependencyResolver.Current.RequestLifetimeScope; + var value = context.ResolveOptional(parameterDescriptor.ParameterType); + + if (value == null) + { + // Issue #368 + // If injection is enabled and the value can't be resolved, fall back to + // the default behavior. Or if injection isn't enabled, fall back. + // Unfortunately we can't do much to pre-determine if model binding will succeed + // because model binding "knows" about a lot of stuff like arrays, certain generic + // collection types, type converters, and other stuff that may or may not fail. + try + { + value = base.GetParameterValue(controllerContext, parameterDescriptor); + } + catch (MissingMethodException) + { + // Don't do anything - this means the default model binder couldn't + // activate a new instance or figure out some other way to model + // bind it. + } + } + + return value; + } + } +} \ No newline at end of file diff --git a/Autofac.Integration.Mvc/FilterMetadata.cs b/Autofac.Integration.Mvc/FilterMetadata.cs new file mode 100644 index 000000000..e7bda5bfb --- /dev/null +++ b/Autofac.Integration.Mvc/FilterMetadata.cs @@ -0,0 +1,62 @@ +// This software is part of the Autofac IoC container +// Copyright © 2012 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel; +using System.Reflection; +using System.Web.Mvc; + +namespace Autofac.Integration.Mvc +{ + /// + /// Metadata interface for filter registrations. + /// + internal class FilterMetadata + { + /// + /// Gets the type of the controller. + /// + [DefaultValue(null)] + public Type ControllerType { get; set; } + + /// + /// Gets the filter scope. + /// + [DefaultValue(FilterScope.First)] + public FilterScope FilterScope { get; set; } + + /// + /// Gets the method info. + /// + [DefaultValue(null)] + public MethodInfo MethodInfo { get; set; } + + /// + /// Gets the order in which the filter is applied. + /// + [DefaultValue(-1)] + public int Order { get; set; } + } +} \ No newline at end of file diff --git a/Autofac.Integration.Mvc/ILifetimeScopeProvider.cs b/Autofac.Integration.Mvc/ILifetimeScopeProvider.cs new file mode 100644 index 000000000..b55174d17 --- /dev/null +++ b/Autofac.Integration.Mvc/ILifetimeScopeProvider.cs @@ -0,0 +1,56 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Autofac.Integration.Mvc +{ + /// + /// Implementors are able to control the creation of nested lifetime scopes. + /// + public interface ILifetimeScopeProvider + { + /// + /// Gets a nested lifetime scope that services can be resolved from. + /// + /// + /// A configuration action that will execute during lifetime scope creation. + /// + /// A new or existing nested lifetime scope. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + ILifetimeScope GetLifetimeScope(Action configurationAction); + + /// + /// Ends the current lifetime scope. + /// + void EndLifetimeScope(); + + /// + /// Gets the global, application-wide container. + /// + ILifetimeScope ApplicationContainer { get; } + } +} \ No newline at end of file diff --git a/Autofac.Integration.Mvc/ModelBinderTypeAttribute.cs b/Autofac.Integration.Mvc/ModelBinderTypeAttribute.cs new file mode 100644 index 000000000..cd2537ae7 --- /dev/null +++ b/Autofac.Integration.Mvc/ModelBinderTypeAttribute.cs @@ -0,0 +1,64 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Autofac.Integration.Mvc +{ + /// + /// Indicates what types a model binder supports. + /// + [SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public sealed class ModelBinderTypeAttribute : Attribute + { + /// + /// Gets the target types. + /// + public IEnumerable TargetTypes { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The target types. + public ModelBinderTypeAttribute(params Type[] targetTypes) + { + if (targetTypes == null) throw new ArgumentNullException("targetTypes"); + TargetTypes = targetTypes; + } + + /// + /// Initializes a new instance of the class. + /// + /// The target type. + public ModelBinderTypeAttribute(Type targetType) + { + if (targetType == null) throw new ArgumentNullException("targetType"); + TargetTypes = new Type[] { targetType }; + } + } +} diff --git a/Autofac.Integration.Mvc/PreApplicationStartCode.cs b/Autofac.Integration.Mvc/PreApplicationStartCode.cs new file mode 100644 index 000000000..cc196b096 --- /dev/null +++ b/Autofac.Integration.Mvc/PreApplicationStartCode.cs @@ -0,0 +1,51 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System.ComponentModel; +using Microsoft.Web.Infrastructure.DynamicModuleHelper; + +namespace Autofac.Integration.Mvc +{ + /// + /// Container class for the ASP.NET application startup method. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class PreApplicationStartCode + { + private static bool _startWasCalled; + + /// + /// Performs ASP.NET application startup logic early in the pipeline. + /// + public static void Start() + { + // Guard against multiple calls. All Start calls are made on the same thread, so no lock needed here. + if (_startWasCalled) return; + + _startWasCalled = true; + DynamicModuleUtility.RegisterModule(typeof(RequestLifetimeHttpModule)); + } + } +} diff --git a/Autofac.Integration.Mvc/Properties/AssemblyInfo.cs b/Autofac.Integration.Mvc/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..4ba3cc441 --- /dev/null +++ b/Autofac.Integration.Mvc/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Web; +using Autofac.Integration.Mvc; + +[assembly: AssemblyTitle("Autofac.Integration.Mvc")] +[assembly: AssemblyDescription("Autofac ASP.NET MVC 4 Integration")] +[assembly: InternalsVisibleTo("Autofac.Tests.Integration.Mvc, PublicKey=00240000048000009400000006020000002400005253413100040000010001008728425885ef385e049261b18878327dfaaf0d666dea3bd2b0e4f18b33929ad4e5fbc9087e7eda3c1291d2de579206d9b4292456abffbe8be6c7060b36da0c33b883e3878eaf7c89fddf29e6e27d24588e81e86f3a22dd7b1a296b5f06fbfb500bbd7410faa7213ef4e2ce7622aefc03169b0324bcd30ccfe9ac8204e4960be6")] +[assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")] +[assembly: ComVisible(false)] diff --git a/Autofac.Integration.Mvc/RegistrationExtensions.cs b/Autofac.Integration.Mvc/RegistrationExtensions.cs new file mode 100644 index 000000000..f1f5c4cd1 --- /dev/null +++ b/Autofac.Integration.Mvc/RegistrationExtensions.cs @@ -0,0 +1,510 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Web; +using System.Web.Mvc; +using Autofac.Builder; +using Autofac.Core; +using Autofac.Features.Scanning; + +namespace Autofac.Integration.Mvc +{ + /// + /// Extends with methods to support ASP.NET MVC. + /// + public static class RegistrationExtensions + { + /// + /// Share one instance of the component within the context of a single + /// HTTP request. + /// + /// Registration limit type. + /// Registration style. + /// Activator data type. + /// The registration to configure. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + InstancePerHttpRequest( + this IRegistrationBuilder registration) + { + if (registration == null) throw new ArgumentNullException("registration"); + + return registration.InstancePerMatchingLifetimeScope(RequestLifetimeScopeProvider.HttpRequestTag); + } + + /// + /// Register types that implement IController in the provided assemblies. + /// + /// The container builder. + /// Assemblies to scan for controllers. + /// Registration builder allowing the controller components to be customised. + public static IRegistrationBuilder + RegisterControllers( + this ContainerBuilder builder, + params Assembly[] controllerAssemblies) + { + return builder.RegisterAssemblyTypes(controllerAssemblies) + .Where(t => typeof(IController).IsAssignableFrom(t) && + t.Name.EndsWith("Controller", StringComparison.Ordinal)); + } + + /// + /// Inject an IActionInvoker into the controller's ActionInvoker property. + /// + /// Limit type. + /// Activator data. + /// Registration style. + /// The registration builder. + /// A registration builder. + public static IRegistrationBuilder + InjectActionInvoker( + this IRegistrationBuilder registrationBuilder) + { + return registrationBuilder.InjectActionInvoker(new TypedService(typeof(IActionInvoker))); + } + + /// + /// Inject an IActionInvoker into the controller's ActionInvoker property. + /// + /// Limit type. + /// Activator data. + /// Registration style. + /// The registration builder. + /// Service used to resolve the action invoker. + /// A registration builder. + public static IRegistrationBuilder + InjectActionInvoker( + this IRegistrationBuilder registrationBuilder, + Service actionInvokerService) + { + if (registrationBuilder == null) throw new ArgumentNullException("registrationBuilder"); + if (actionInvokerService == null) throw new ArgumentNullException("actionInvokerService"); + + return registrationBuilder.OnActivating(e => + { + var controller = e.Instance as Controller; + if (controller != null) + controller.ActionInvoker = (IActionInvoker)e.Context.ResolveService(actionInvokerService); + }); + } + + /// + /// Registers the . + /// + /// The container builder. + public static void RegisterModelBinderProvider(this ContainerBuilder builder) + { + if (builder == null) throw new ArgumentNullException("builder"); + + builder.RegisterType() + .As() + .SingleInstance(); + } + + /// + /// Sets a provided registration to act as an + /// for the specified list of types. + /// + /// + /// The registration for the type or object instance that will act as + /// the model binder. + /// + /// + /// The list of model for which the + /// should be a model binder. + /// + /// + /// Registration limit type. + /// + /// + /// Activator data type. + /// + /// + /// Registration style. + /// + /// + /// An Autofac registration that can be modified as needed. + /// + /// + /// Thrown if or is . + /// + /// + /// Thrown if is empty or contains all + /// values. + /// + /// + /// + /// The declarative mechanism of registering model binders with Autofac + /// is through use of + /// and the . + /// This method is an imperative alternative. + /// + /// + /// The two mechanisms are mutually exclusive. If you register a model + /// binder using + /// and register the same model binder with this method, the results + /// are not automatically merged together - standard dependency + /// registration/resolution rules will be used to manage the conflict. + /// + /// + /// Any values provided in + /// will be removed prior to registration. + /// + /// + public static IRegistrationBuilder AsModelBinderForTypes(this IRegistrationBuilder registration, params Type[] types) + where TActivatorData : IConcreteActivatorData + where TRegistrationStyle : SingleRegistrationStyle + { + if (registration == null) + { + throw new ArgumentNullException("registration"); + } + if (types == null) + { + throw new ArgumentNullException("types"); + } + var typeList = types.Where(type => type != null).ToList(); + if (typeList.Count == 0) + { + throw new ArgumentException(RegistrationExtensionsResources.InvalidModelBinderType, "types"); + } + + return registration.As().WithMetadata(AutofacModelBinderProvider.MetadataKey, typeList); + } + + /// + /// Register types that implement in the provided assemblies + /// and have a . + /// + /// The container builder. + /// Assemblies to scan for model binders. + /// A registration builder. + /// + /// Thrown if or is . + /// + /// + /// + /// The declarative mechanism of registering model binders with Autofac + /// is through use of this method and the + /// . + /// If you would like more imperative control over registration for your + /// model binders, see the + /// method. + /// + /// + /// The two mechanisms are mutually exclusive. If you register a model + /// binder using + /// and register the same model binder with this method, the results + /// are not automatically merged together - standard dependency + /// registration/resolution rules will be used to manage the conflict. + /// + /// + /// This method only registers types that implement + /// and are marked with the . + /// The model binder must have the attribute because the + /// uses + /// the associated metadata - from the attribute(s) - to resolve the + /// binder based on model type. If there aren't any attributes, there + /// won't be any metadata, so the model binder will be technically + /// registered but will never actually be resolved. + /// + /// + /// If your model is not marked with the attribute, or if you don't want + /// to use attributes, use the + /// + /// extension instead. + /// + /// + public static IRegistrationBuilder + RegisterModelBinders(this ContainerBuilder builder, params Assembly[] modelBinderAssemblies) + { + if (builder == null) throw new ArgumentNullException("builder"); + if (modelBinderAssemblies == null) throw new ArgumentNullException("modelBinderAssemblies"); + + return builder.RegisterAssemblyTypes(modelBinderAssemblies) + .Where(type => typeof(IModelBinder).IsAssignableFrom(type) && type.GetCustomAttributes(typeof(ModelBinderTypeAttribute), true).Length > 0) + .As() + .InstancePerHttpRequest() + .WithMetadata(AutofacModelBinderProvider.MetadataKey, type => + (from ModelBinderTypeAttribute attribute in type.GetCustomAttributes(typeof(ModelBinderTypeAttribute), true) + from targetType in attribute.TargetTypes + select targetType).ToList()); + } + + /// + /// Registers the . + /// + /// The container builder. + public static void RegisterFilterProvider(this ContainerBuilder builder) + { + if (builder == null) throw new ArgumentNullException("builder"); + + foreach (var provider in FilterProviders.Providers.OfType().ToArray()) + FilterProviders.Providers.Remove(provider); + + builder.RegisterType() + .As() + .SingleInstance(); + } + + /// + /// Cache instances in the web session. This implies external ownership (disposal is not + /// available.) All dependencies must also have external ownership. + /// + /// + /// It is strongly recommended that components cached per-session do not take dependencies on + /// other services. + /// + /// Registration limit type. + /// Registration style. + /// Activator data type. + /// The registration to configure. + /// A registration builder allowing further configuration of the component. + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "It is the responsibility of the registry to dispose of registrations.")] + public static IRegistrationBuilder + CacheInSession( + this IRegistrationBuilder registration) + where TActivatorData : IConcreteActivatorData + where TSingleRegistrationStyle : SingleRegistrationStyle + { + if (registration == null) throw new ArgumentNullException("registration"); + + var services = registration.RegistrationData.Services.ToArray(); + registration.RegistrationData.ClearServices(); + + return registration + .ExternallyOwned() + .OnRegistered(e => e.ComponentRegistry.Register(RegistrationBuilder + .ForDelegate((c, p) => + { + var session = HttpContext.Current.Session; + object result; + lock (session.SyncRoot) + { + result = session[e.ComponentRegistration.Id.ToString()]; + if (result == null) + { + result = c.ResolveComponent(e.ComponentRegistration, p); + session[e.ComponentRegistration.Id.ToString()] = result; + } + } + return result; + }) + .As(services) + .InstancePerLifetimeScope() + .ExternallyOwned() + .CreateRegistration())); + } + + /// + /// Sets the provided registration to act as an for the specified controller action. + /// + /// The type of the controller. + /// The registration. + /// The action selector. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsActionFilterFor(this IRegistrationBuilder registration, + Expression> actionSelector, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ActionFilterMetadataKey, actionSelector, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller. + /// + /// The type of the controller. + /// The registration. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsActionFilterFor(this IRegistrationBuilder registration, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ActionFilterMetadataKey, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller action. + /// + /// The type of the controller. + /// The registration. + /// The action selector. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsAuthorizationFilterFor(this IRegistrationBuilder registration, + Expression> actionSelector, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.AuthorizationFilterMetadataKey, actionSelector, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller. + /// + /// The type of the controller. + /// The registration. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsAuthorizationFilterFor(this IRegistrationBuilder registration, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.AuthorizationFilterMetadataKey, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller action. + /// + /// The type of the controller. + /// The registration. + /// The action selector. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsExceptionFilterFor(this IRegistrationBuilder registration, + Expression> actionSelector, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ExceptionFilterMetadataKey, actionSelector, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller. + /// + /// The type of the controller. + /// The registration. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsExceptionFilterFor(this IRegistrationBuilder registration, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ExceptionFilterMetadataKey, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller action. + /// + /// The type of the controller. + /// The registration. + /// The action selector. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsResultFilterFor(this IRegistrationBuilder registration, + Expression> actionSelector, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ResultFilterMetadataKey, actionSelector, order); + } + + /// + /// Sets the provided registration to act as an for the specified controller. + /// + /// The type of the controller. + /// The registration. + /// The order in which the filter is applied. + /// A registration builder allowing further configuration of the component. + public static IRegistrationBuilder + AsResultFilterFor(this IRegistrationBuilder registration, int order = Filter.DefaultOrder) + where TController : IController + { + return AsFilterFor(registration, AutofacFilterProvider.ResultFilterMetadataKey, order); + } + + static IRegistrationBuilder + AsFilterFor(IRegistrationBuilder registration, string metadataKey, Expression> actionSelector, int order) + where TController : IController + { + if (registration == null) throw new ArgumentNullException("registration"); + if (actionSelector == null) throw new ArgumentNullException("actionSelector"); + + var limitType = registration.ActivatorData.Activator.LimitType; + + if (!limitType.IsAssignableTo()) + { + var message = string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MustBeAssignableToFilterType, + limitType.FullName, typeof(TFilter).FullName); + throw new ArgumentException(message, "registration"); + } + + var metadata = new FilterMetadata + { + ControllerType = typeof(TController), + FilterScope = FilterScope.Action, + MethodInfo = GetMethodInfo(actionSelector), + Order = order + }; + + return registration.As().WithMetadata(metadataKey, metadata); + } + + static IRegistrationBuilder + AsFilterFor(IRegistrationBuilder registration, string metadataKey, int order) + where TController : IController + { + if (registration == null) throw new ArgumentNullException("registration"); + + var limitType = registration.ActivatorData.Activator.LimitType; + + if (!limitType.IsAssignableTo()) + { + var message = string.Format(CultureInfo.CurrentCulture, RegistrationExtensionsResources.MustBeAssignableToFilterType, + limitType.FullName, typeof(TFilter).FullName); + throw new ArgumentException(message, "registration"); + } + + var metadata = new FilterMetadata + { + ControllerType = typeof(TController), + FilterScope = FilterScope.Controller, + MethodInfo = null, + Order = order + }; + + return registration.As().WithMetadata(metadataKey, metadata); + } + + static MethodInfo GetMethodInfo(LambdaExpression expression) + { + var outermostExpression = expression.Body as MethodCallExpression; + + if (outermostExpression == null) + throw new ArgumentException(RegistrationExtensionsResources.InvalidActionExpress); + + return outermostExpression.Method; + } + } +} diff --git a/Autofac.Integration.Mvc/RegistrationExtensionsResources.Designer.cs b/Autofac.Integration.Mvc/RegistrationExtensionsResources.Designer.cs new file mode 100644 index 000000000..175afcea9 --- /dev/null +++ b/Autofac.Integration.Mvc/RegistrationExtensionsResources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18010 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Autofac.Integration.Mvc { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class RegistrationExtensionsResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal RegistrationExtensionsResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Autofac.Integration.Mvc.RegistrationExtensionsResources", typeof(RegistrationExtensionsResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The action method Expression is invalid. It should consist only of a Method call to a controller action method.. + /// + internal static string InvalidActionExpress { + get { + return ResourceManager.GetString("InvalidActionExpress", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type list may not be empty or contain all null values.. + /// + internal static string InvalidModelBinderType { + get { + return ResourceManager.GetString("InvalidModelBinderType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The type '{0}' must be assignable to the filter type '{1}'.. + /// + internal static string MustBeAssignableToFilterType { + get { + return ResourceManager.GetString("MustBeAssignableToFilterType", resourceCulture); + } + } + } +} diff --git a/Autofac.Integration.Mvc/RegistrationExtensionsResources.resx b/Autofac.Integration.Mvc/RegistrationExtensionsResources.resx new file mode 100644 index 000000000..d1be6ef19 --- /dev/null +++ b/Autofac.Integration.Mvc/RegistrationExtensionsResources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The action method Expression is invalid. It should consist only of a Method call to a controller action method. + + + Type list may not be empty or contain all null values. + + + The type '{0}' must be assignable to the filter type '{1}'. + + \ No newline at end of file diff --git a/Autofac.Integration.Mvc/RequestLifetimeHttpModule.cs b/Autofac.Integration.Mvc/RequestLifetimeHttpModule.cs new file mode 100644 index 000000000..cf9441a92 --- /dev/null +++ b/Autofac.Integration.Mvc/RequestLifetimeHttpModule.cs @@ -0,0 +1,79 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Web; + +namespace Autofac.Integration.Mvc +{ + /// + /// An and implementation + /// that creates a nested lifetime scope for each HTTP request. + /// + internal class RequestLifetimeHttpModule : IHttpModule + { + /// + /// Gets the lifetime scope provider that should be notified when a HTTP request ends. + /// + internal static ILifetimeScopeProvider LifetimeScopeProvider { get; private set; } + + /// + /// Initializes a module and prepares it to handle requests. + /// + /// An that provides access to the + /// methods, properties, and events common to all application objects within an ASP.NET application + /// + /// Thrown if is . + /// + public void Init(HttpApplication context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + context.EndRequest += OnEndRequest; + } + + /// + /// Disposes of the resources (other than memory) used by the module that implements . + /// + public void Dispose() + { + } + + public static void SetLifetimeScopeProvider(ILifetimeScopeProvider lifetimeScopeProvider) + { + if (lifetimeScopeProvider == null) throw new ArgumentNullException("lifetimeScopeProvider"); + + LifetimeScopeProvider = lifetimeScopeProvider; + } + + static void OnEndRequest(object sender, EventArgs e) + { + if (LifetimeScopeProvider != null) + LifetimeScopeProvider.EndLifetimeScope(); + } + } +} \ No newline at end of file diff --git a/Autofac.Integration.Mvc/RequestLifetimeScopeProvider.cs b/Autofac.Integration.Mvc/RequestLifetimeScopeProvider.cs new file mode 100644 index 000000000..55eee6638 --- /dev/null +++ b/Autofac.Integration.Mvc/RequestLifetimeScopeProvider.cs @@ -0,0 +1,122 @@ +// This software is part of the Autofac IoC container +// Copyright (c) 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Web; + +namespace Autofac.Integration.Mvc +{ + /// + /// Creates and disposes HTTP request based lifetime scopes. + /// + /// + /// The provider is notified when a HTTP request ends by the . + /// + public class RequestLifetimeScopeProvider : ILifetimeScopeProvider + { + readonly ILifetimeScope _container; + + /// + /// Tag used to identify registrations that are scoped to the HTTP request level. + /// + internal static readonly object HttpRequestTag = "AutofacWebRequest"; + + /// + /// Initializes a new instance of the class. + /// + /// The parent container, usually the application container. + public RequestLifetimeScopeProvider(ILifetimeScope container) + { + if (container == null) throw new ArgumentNullException("container"); + _container = container; + RequestLifetimeHttpModule.SetLifetimeScopeProvider(this); + } + + /// + /// Gets the global, application-wide container. + /// + public ILifetimeScope ApplicationContainer + { + get { return _container; } + } + + static ILifetimeScope LifetimeScope + { + get { return (ILifetimeScope)HttpContext.Current.Items[typeof(ILifetimeScope)]; } + set { HttpContext.Current.Items[typeof(ILifetimeScope)] = value; } + } + + /// + /// Gets a nested lifetime scope that services can be resolved from. + /// + /// + /// A configuration action that will execute during lifetime scope creation. + /// + /// A new or existing nested lifetime scope. + public ILifetimeScope GetLifetimeScope(Action configurationAction) + { + if (HttpContext.Current == null) + { + throw new InvalidOperationException(RequestLifetimeScopeProviderResources.HttpContextNotAvailable); + } + + if (LifetimeScope == null) + { + if ((LifetimeScope = GetLifetimeScopeCore(configurationAction)) == null) + throw new InvalidOperationException( + string.Format(CultureInfo.CurrentCulture, RequestLifetimeScopeProviderResources.NullLifetimeScopeReturned, GetType().FullName)); + } + return LifetimeScope; + } + + /// + /// Ends the current HTTP request lifetime scope. + /// + public void EndLifetimeScope() + { + var lifetimeScope = LifetimeScope; + if (lifetimeScope != null) + lifetimeScope.Dispose(); + } + + /// + /// Gets a lifetime scope for the current HTTP request. This method can be overridden + /// to alter the way that the life time scope is constructed. + /// + /// + /// A configuration action that will execute during lifetime scope creation. + /// + /// A new lifetime scope for the current HTTP request. + [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected virtual ILifetimeScope GetLifetimeScopeCore(Action configurationAction) + { + return (configurationAction == null) + ? ApplicationContainer.BeginLifetimeScope(HttpRequestTag) + : ApplicationContainer.BeginLifetimeScope(HttpRequestTag, configurationAction); + } + } +} diff --git a/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.Designer.cs b/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.Designer.cs new file mode 100644 index 000000000..eb79ea366 --- /dev/null +++ b/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Autofac.Integration.Mvc { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class RequestLifetimeScopeProviderResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal RequestLifetimeScopeProviderResources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Autofac.Integration.Mvc.RequestLifetimeScopeProviderResources", typeof(RequestLifetimeScopeProviderResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The request lifetime scope cannot be created because the HttpContext is not available.. + /// + internal static string HttpContextNotAvailable { + get { + return ResourceManager.GetString("HttpContextNotAvailable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The 'GetLifetimeScopeCore' method implementation on '{0}' returned a null ILifetimeScope instance. When overridden this method must return a valid ILifetimeScope instance for the current HTTP request.. + /// + internal static string NullLifetimeScopeReturned { + get { + return ResourceManager.GetString("NullLifetimeScopeReturned", resourceCulture); + } + } + } +} diff --git a/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.resx b/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.resx new file mode 100644 index 000000000..22507abed --- /dev/null +++ b/Autofac.Integration.Mvc/RequestLifetimeScopeProviderResources.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The request lifetime scope cannot be created because the HttpContext is not available. + + + The 'GetLifetimeScopeCore' method implementation on '{0}' returned a null ILifetimeScope instance. When overridden this method must return a valid ILifetimeScope instance for the current HTTP request. + + \ No newline at end of file diff --git a/Autofac.Integration.Mvc/ViewRegistrationSource.cs b/Autofac.Integration.Mvc/ViewRegistrationSource.cs new file mode 100644 index 000000000..60f167df5 --- /dev/null +++ b/Autofac.Integration.Mvc/ViewRegistrationSource.cs @@ -0,0 +1,78 @@ +// This software is part of the Autofac IoC container +// Copyright © 2011 Autofac Contributors +// http://autofac.org +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Web.Mvc; +using Autofac.Builder; +using Autofac.Core; + +namespace Autofac.Integration.Mvc +{ + /// + /// A registration source for building view registrations. + /// + /// + /// Supports view registrations for , , + /// and derived types. + /// + public class ViewRegistrationSource : IRegistrationSource + { + /// + /// Retrieve registrations for an unregistered service, to be used + /// by the container. + /// + /// The service that was requested. + /// A function that will return existing registrations for a service. + /// Registrations providing the service. + public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) + { + var typedService = service as IServiceWithType; + + if (typedService != null && IsSupportedView(typedService.ServiceType)) + yield return RegistrationBuilder.ForType(typedService.ServiceType) + .PropertiesAutowired() + .InstancePerDependency() + .CreateRegistration(); + } + + /// + /// Gets whether the registrations provided by this source are 1:1 adapters on top + /// of other components (I.e. like Meta, Func or Owned.) + /// + public bool IsAdapterForIndividualComponents + { + get { return false; } + } + + static bool IsSupportedView(Type serviceType) + { + return serviceType.IsAssignableTo() + || serviceType.IsAssignableTo() + || serviceType.IsAssignableTo() + || serviceType.IsAssignableTo(); + } + } +} diff --git a/Autofac.Integration.Mvc/packages.config b/Autofac.Integration.Mvc/packages.config new file mode 100644 index 000000000..acd79060a --- /dev/null +++ b/Autofac.Integration.Mvc/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 5acb5e096..93102a6d2 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -67,12 +67,17 @@ prompt MinimumRecommendedRules.ruleset + + + - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.dll - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.Configuration.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.Configuration.dll ..\packages\AutoMapper.2.2.0\lib\net40\AutoMapper.dll @@ -80,14 +85,17 @@ ..\packages\FluentValidation.3.4.6.0\lib\Net40\FluentValidation.dll - - ..\packages\Nancy.0.15.3\lib\net40\Nancy.dll + + False + ..\packages\Nancy.0.16.1\lib\net40\Nancy.dll - - ..\packages\Nancy.Bootstrappers.Autofac.0.15.3\lib\net40\Nancy.Bootstrappers.Autofac.dll + + False + ..\NzbDrone.Services.Api\bin\Nancy.Bootstrappers.Autofac.dll - - ..\packages\Nancy.Hosting.Aspnet.0.15.3\lib\net40\Nancy.Hosting.Aspnet.dll + + False + ..\packages\Nancy.Hosting.Aspnet.0.16.1\lib\net40\Nancy.Hosting.Aspnet.dll False diff --git a/NzbDrone.Api/packages.config b/NzbDrone.Api/packages.config index 98eaade7f..bd2cd274e 100644 --- a/NzbDrone.Api/packages.config +++ b/NzbDrone.Api/packages.config @@ -1,11 +1,10 @@  - + - - - + + \ No newline at end of file diff --git a/NzbDrone.App.Test/NzbDrone.App.Test.csproj b/NzbDrone.App.Test/NzbDrone.App.Test.csproj index bad364db9..5b6addac9 100644 --- a/NzbDrone.App.Test/NzbDrone.App.Test.csproj +++ b/NzbDrone.App.Test/NzbDrone.App.Test.csproj @@ -71,11 +71,13 @@ MinimumRecommendedRules.ruleset - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.dll - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.Configuration.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.Configuration.dll ..\packages\NBuilder.3.0.1.1\lib\FizzWare.NBuilder.dll diff --git a/NzbDrone.App.Test/packages.config b/NzbDrone.App.Test/packages.config index d78223afa..bd67fdc24 100644 --- a/NzbDrone.App.Test/packages.config +++ b/NzbDrone.App.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Backbone/NzbDrone.Backbone.csproj b/NzbDrone.Backbone/NzbDrone.Backbone.csproj new file mode 100644 index 000000000..0d7cfdfc8 --- /dev/null +++ b/NzbDrone.Backbone/NzbDrone.Backbone.csproj @@ -0,0 +1,94 @@ + + + + + Debug + AnyCPU + + + 2.0 + {EE6B9BAC-2136-460A-87B0-709D2AC0A9AE} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + NzbDrone.Backbone + NzbDrone.Backbone + v4.0 + true + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 0 + / + http://localhost:55352/ + False + False + + + False + + + + + + \ No newline at end of file diff --git a/NzbDrone.Backbone/Properties/AssemblyInfo.cs b/NzbDrone.Backbone/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..2a8603592 --- /dev/null +++ b/NzbDrone.Backbone/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NzbDrone.Backbone")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NzbDrone.Backbone")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ed73d928-3270-4c84-8d9c-ee9e08dfcbd0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index 66e931811..998a19307 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -71,11 +71,13 @@ MinimumRecommendedRules.ruleset - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.dll - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.Configuration.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.Configuration.dll False diff --git a/NzbDrone.Common.Test/packages.config b/NzbDrone.Common.Test/packages.config index 1ed33377b..c0539435e 100644 --- a/NzbDrone.Common.Test/packages.config +++ b/NzbDrone.Common.Test/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Common/EnsureThat/Ensure.cs b/NzbDrone.Common/EnsureThat/Ensure.cs new file mode 100644 index 000000000..5ac44a25c --- /dev/null +++ b/NzbDrone.Common/EnsureThat/Ensure.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq.Expressions; + +namespace NzbDrone.Common.EnsureThat +{ + public static class Ensure + { + public static Param That(T value, string name = Param.DefaultName) + { + return new Param(name, value); + } + + public static Param That(Expression> expression) + { + var memberExpression = expression.GetRightMostMember(); + + return new Param( + memberExpression.ToPath(), + expression.Compile().Invoke()); + } + + public static TypeParam ThatTypeFor(T value, string name = Param.DefaultName) + { + return new TypeParam(name, value.GetType()); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureBoolExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureBoolExtensions.cs new file mode 100644 index 000000000..de18bb06c --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureBoolExtensions.cs @@ -0,0 +1,26 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureBoolExtensions + { + [DebuggerStepThrough] + public static Param IsTrue(this Param param) + { + if (!param.Value) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotTrue); + + return param; + } + + [DebuggerStepThrough] + public static Param IsFalse(this Param param) + { + if (param.Value) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotFalse); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureCollectionExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureCollectionExtensions.cs new file mode 100644 index 000000000..34565f8a0 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureCollectionExtensions.cs @@ -0,0 +1,67 @@ +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureCollectionExtensions + { + [DebuggerStepThrough] + public static Param HasItems(this Param param) where T : class, ICollection + { + if (param.Value == null || param.Value.Count < 1) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + + [DebuggerStepThrough] + public static Param> HasItems(this Param> param) + { + if (param.Value == null || param.Value.Count < 1) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + + [DebuggerStepThrough] + public static Param> HasItems(this Param> param) + { + if (param.Value == null || !param.Value.Any()) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + + [DebuggerStepThrough] + public static Param HasItems(this Param param) + { + if (param.Value == null || param.Value.Length < 1) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + + [DebuggerStepThrough] + public static Param> HasItems(this Param> param) + { + if (param.Value == null || param.Value.Count < 1) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + + + [DebuggerStepThrough] + public static Param> HasItems(this Param> param) + { + if (param.Value == null || param.Value.Count < 1) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyCollection); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureDateTimeExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureDateTimeExtensions.cs new file mode 100644 index 000000000..b4ce0024b --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureDateTimeExtensions.cs @@ -0,0 +1,75 @@ +using System; +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureDateTimeExtensions + { + + private static readonly DateTime _minTime = new DateTime(1960, 1, 1); + + [DebuggerStepThrough] + public static Param IsLt(this Param param, DateTime limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLte(this Param param, DateTime limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGt(this Param param, DateTime limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGte(this Param param, DateTime limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, DateTime min, DateTime max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsUtc(this Param param) + { + if (param.Value.Kind != DateTimeKind.Utc) + throw ExceptionFactory.CreateForParamValidation(param.Name, "Excepted time to be in UTC but was [{0}]".Inject(param.Value.Kind)); + return param; + } + + + [DebuggerStepThrough] + public static Param IsValid(this Param param) + { + return IsGt(param, _minTime); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureDecimalExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureDecimalExtensions.cs new file mode 100644 index 000000000..6725adfae --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureDecimalExtensions.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureDecimalExtensions + { + [DebuggerStepThrough] + public static Param IsLt(this Param param, decimal limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLte(this Param param, decimal limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGt(this Param param, decimal limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGte(this Param param, decimal limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, decimal min, decimal max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureDoubleExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureDoubleExtensions.cs new file mode 100644 index 000000000..9dd374c0f --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureDoubleExtensions.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureDoubleExtensions + { + [DebuggerStepThrough] + public static Param IsLt(this Param param, double limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLte(this Param param, double limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGt(this Param param, double limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGte(this Param param, double limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, double min, double max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureGuidExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureGuidExtensions.cs new file mode 100644 index 000000000..77a7be433 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureGuidExtensions.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureGuidExtensions + { + [DebuggerStepThrough] + public static Param IsNotEmpty(this Param param) + { + if (Guid.Empty.Equals(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsEmptyGuid); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureIntExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureIntExtensions.cs new file mode 100644 index 000000000..154921611 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureIntExtensions.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureIntExtensions + { + [DebuggerStepThrough] + public static Param IsLessThan(this Param param, int limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLessThanOrEqualTo(this Param param, int limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGreaterThan(this Param param, int limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGreaterOrEqualTo(this Param param, int limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, int min, int max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureLongExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureLongExtensions.cs new file mode 100644 index 000000000..d9d329b52 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureLongExtensions.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureLongExtensions + { + [DebuggerStepThrough] + public static Param IsLt(this Param param, long limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLte(this Param param, long limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGt(this Param param, long limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGte(this Param param, long limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, long min, long max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureNullableValueTypeExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureNullableValueTypeExtensions.cs new file mode 100644 index 000000000..54d5af7ad --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureNullableValueTypeExtensions.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureNullableValueTypeExtensions + { + [DebuggerStepThrough] + public static Param IsNotNull(this Param param) where T : struct + { + if (param.Value == null || !param.Value.HasValue) + throw ExceptionFactory.CreateForParamNullValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNull); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureObjectExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureObjectExtensions.cs new file mode 100644 index 000000000..a5fe6a23f --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureObjectExtensions.cs @@ -0,0 +1,17 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureObjectExtensions + { + [DebuggerStepThrough] + public static Param IsNotNull(this Param param) where T : class + { + if (param.Value == null) + throw ExceptionFactory.CreateForParamNullValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNull); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureShortExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureShortExtensions.cs new file mode 100644 index 000000000..e1189b1ed --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureShortExtensions.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureShortExtensions + { + [DebuggerStepThrough] + public static Param IsLt(this Param param, short limit) + { + if (param.Value >= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLte(this Param param, short limit) + { + if (!(param.Value <= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotLte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGt(this Param param, short limit) + { + if (param.Value <= limit) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGt.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsGte(this Param param, short limit) + { + if (!(param.Value >= limit)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotGte.Inject(param.Value, limit)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsInRange(this Param param, short min, short max) + { + if (param.Value < min) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLow.Inject(param.Value, min)); + + if (param.Value > max) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToHigh.Inject(param.Value, max)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs new file mode 100644 index 000000000..b81cc4a01 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs @@ -0,0 +1,98 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + [DebuggerStepThrough] + public static class EnsureStringExtensions + { + [DebuggerStepThrough] + public static Param IsNotNullOrWhiteSpace(this Param param) + { + if (string.IsNullOrWhiteSpace(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace); + + return param; + } + + [DebuggerStepThrough] + public static Param IsNotNullOrEmpty(this Param param) + { + if (string.IsNullOrEmpty(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrEmpty); + + return param; + } + + [DebuggerStepThrough] + public static Param HasLengthBetween(this Param param, int minLength, int maxLength) + { + if (string.IsNullOrEmpty(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrEmpty); + + var length = param.Value.Length; + + if (length < minLength) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToShort.Inject(minLength, maxLength, length)); + + if (length > maxLength) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotInRange_ToLong.Inject(minLength, maxLength, length)); + + return param; + } + + [DebuggerStepThrough] + public static Param IsLongerThan(this Param param, int minLength) + { + if (string.IsNullOrEmpty(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrEmpty); + + var length = param.Value.Length; + + if (length < minLength) + throw ExceptionFactory.CreateForParamValidation(param.Name, "The string is not long enough. Must be at least '{0}' but was '{1}' characters long.".Inject(minLength, length)); + + return param; + } + + [DebuggerStepThrough] + public static Param Matches(this Param param, string match) + { + return Matches(param, new Regex(match)); + } + + [DebuggerStepThrough] + public static Param Matches(this Param param, Regex match) + { + if (!match.IsMatch(param.Value)) + { + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_NoMatch.Inject(param.Value, match)); + } + return param; + } + + + [DebuggerStepThrough] + public static Param IsRelativePath(this Param param) + { + if (string.IsNullOrWhiteSpace(param.Value)) + throw ExceptionFactory.CreateForParamValidation(param.Name, ExceptionMessages.EnsureExtensions_IsNotNullOrWhiteSpace); + + if (!param.Value.EndsWith("\\")) + { + throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid relative path. relative paths must end with \\", param.Value)); + } + + if (param.Value.Length > 1 && param.Value.StartsWith("\\")) + { + throw ExceptionFactory.CreateForParamValidation(param.Name, string.Format("value [{0}] is not a valid relative path. relative paths can not start with \\", param.Value)); + } + + + return param; + } + } +} diff --git a/NzbDrone.Common/EnsureThat/EnsureTypeExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureTypeExtensions.cs new file mode 100644 index 000000000..c791d3fef --- /dev/null +++ b/NzbDrone.Common/EnsureThat/EnsureTypeExtensions.cs @@ -0,0 +1,86 @@ +using System; +using System.Diagnostics; +using NzbDrone.Common.EnsureThat.Resources; + +namespace NzbDrone.Common.EnsureThat +{ + public static class EnsureTypeExtensions + { + private static class Types + { + internal static readonly Type IntType = typeof (int); + + internal static readonly Type ShortType = typeof(short); + + internal static readonly Type DecimalType = typeof(decimal); + + internal static readonly Type DoubleType = typeof(double); + + internal static readonly Type FloatType = typeof(float); + + internal static readonly Type BoolType = typeof(bool); + + internal static readonly Type DateTimeType = typeof(DateTime); + + internal static readonly Type StringType = typeof(string); + } + + [DebuggerStepThrough] + public static TypeParam IsInt(this TypeParam param) + { + return IsOfType(param, Types.IntType); + } + + [DebuggerStepThrough] + public static TypeParam IsShort(this TypeParam param) + { + return IsOfType(param, Types.ShortType); + } + + [DebuggerStepThrough] + public static TypeParam IsDecimal(this TypeParam param) + { + return IsOfType(param, Types.DecimalType); + } + + [DebuggerStepThrough] + public static TypeParam IsDouble(this TypeParam param) + { + return IsOfType(param, Types.DoubleType); + } + + [DebuggerStepThrough] + public static TypeParam IsFloat(this TypeParam param) + { + return IsOfType(param, Types.FloatType); + } + + [DebuggerStepThrough] + public static TypeParam IsBool(this TypeParam param) + { + return IsOfType(param, Types.BoolType); + } + + [DebuggerStepThrough] + public static TypeParam IsDateTime(this TypeParam param) + { + return IsOfType(param, Types.DateTimeType); + } + + [DebuggerStepThrough] + public static TypeParam IsString(this TypeParam param) + { + return IsOfType(param, Types.StringType); + } + + [DebuggerStepThrough] + public static TypeParam IsOfType(this TypeParam param, Type type) + { + if (!param.Type.Equals(type)) + throw ExceptionFactory.CreateForParamValidation(param.Name, + ExceptionMessages.EnsureExtensions_IsNotOfType.Inject(param.Type.FullName)); + + return param; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/ExceptionFactory.cs b/NzbDrone.Common/EnsureThat/ExceptionFactory.cs new file mode 100644 index 000000000..42bbb1b5c --- /dev/null +++ b/NzbDrone.Common/EnsureThat/ExceptionFactory.cs @@ -0,0 +1,22 @@ +using System; +using NLog; + +namespace NzbDrone.Common.EnsureThat +{ + internal static class ExceptionFactory + { + private static readonly Logger Logger = LogManager.GetLogger("ArgumentValidator"); + + internal static ArgumentException CreateForParamValidation(string paramName, string message) + { + Logger.Warn(message); + return new ArgumentException(message, paramName); + } + + internal static ArgumentNullException CreateForParamNullValidation(string paramName, string message) + { + Logger.Warn(message); + return new ArgumentNullException(paramName, message); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/ExpressionExtensions.cs b/NzbDrone.Common/EnsureThat/ExpressionExtensions.cs new file mode 100644 index 000000000..f3e9473c4 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/ExpressionExtensions.cs @@ -0,0 +1,46 @@ +using System.Linq.Expressions; + +namespace NzbDrone.Common.EnsureThat +{ + internal static class ExpressionExtensions + { + internal static string ToPath(this MemberExpression e) + { + var path = ""; + var parent = e.Expression as MemberExpression; + + if (parent != null) + path = parent.ToPath() + "."; + + return path + e.Member.Name; + } + + internal static MemberExpression GetRightMostMember(this Expression e) + { + if (e is LambdaExpression) + return GetRightMostMember(((LambdaExpression)e).Body); + + if (e is MemberExpression) + return (MemberExpression)e; + + if (e is MethodCallExpression) + { + var callExpression = (MethodCallExpression)e; + + if (callExpression.Object is MethodCallExpression || callExpression.Object is MemberExpression) + return GetRightMostMember(callExpression.Object); + + var member = callExpression.Arguments.Count > 0 ? callExpression.Arguments[0] : callExpression.Object; + return GetRightMostMember(member); + } + + if (e is UnaryExpression) + { + var unaryExpression = (UnaryExpression)e; + return GetRightMostMember(unaryExpression.Operand); + } + + return null; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/Param.cs b/NzbDrone.Common/EnsureThat/Param.cs new file mode 100644 index 000000000..c1ff3c963 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/Param.cs @@ -0,0 +1,24 @@ +namespace NzbDrone.Common.EnsureThat +{ + public abstract class Param + { + public const string DefaultName = ""; + + public readonly string Name; + + protected Param(string name) + { + Name = name; + } + } + + public class Param : Param + { + public readonly T Value; + + internal Param(string name, T value) : base(name) + { + Value = value; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.Designer.cs b/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.Designer.cs new file mode 100644 index 000000000..cc5dd2b7d --- /dev/null +++ b/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.Designer.cs @@ -0,0 +1,225 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.17626 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NzbDrone.Common.EnsureThat.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ExceptionMessages { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ExceptionMessages() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NzbDrone.Common.EnsureThat.Resources.ExceptionMessages", typeof(ExceptionMessages).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Empty collection is not allowed.. + /// + internal static string EnsureExtensions_IsEmptyCollection { + get { + return ResourceManager.GetString("EnsureExtensions_IsEmptyCollection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty Guid is not allowed.. + /// + internal static string EnsureExtensions_IsEmptyGuid { + get { + return ResourceManager.GetString("EnsureExtensions_IsEmptyGuid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected an expression that evaluates to false.. + /// + internal static string EnsureExtensions_IsNotFalse { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotFalse", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is not greater than limit '{1}'.. + /// + internal static string EnsureExtensions_IsNotGt { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotGt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is not greater than or equal to limit '{1}'.. + /// + internal static string EnsureExtensions_IsNotGte { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotGte", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is > max '{1}'.. + /// + internal static string EnsureExtensions_IsNotInRange_ToHigh { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotInRange_ToHigh", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The string is too long. Must be between '{0}' and '{1}'. Must be between '{0}' and '{1}' but was '{2}' characters long.. + /// + internal static string EnsureExtensions_IsNotInRange_ToLong { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotInRange_ToLong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is < min '{1}'.. + /// + internal static string EnsureExtensions_IsNotInRange_ToLow { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotInRange_ToLow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The string is not long enough. Must be between '{0}' and '{1}' but was '{2}' characters long.. + /// + internal static string EnsureExtensions_IsNotInRange_ToShort { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotInRange_ToShort", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is not lower than limit '{1}'.. + /// + internal static string EnsureExtensions_IsNotLt { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotLt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' is not lower than or equal to limit '{1}'.. + /// + internal static string EnsureExtensions_IsNotLte { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotLte", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Value can not be null.. + /// + internal static string EnsureExtensions_IsNotNull { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The string can't be null or empty.. + /// + internal static string EnsureExtensions_IsNotNullOrEmpty { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotNullOrEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The string can't be left empty, null or consist of only whitespaces.. + /// + internal static string EnsureExtensions_IsNotNullOrWhiteSpace { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotNullOrWhiteSpace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The param is not of expected type: '{0}'.. + /// + internal static string EnsureExtensions_IsNotOfType { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotOfType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected an expression that evaluates to true.. + /// + internal static string EnsureExtensions_IsNotTrue { + get { + return ResourceManager.GetString("EnsureExtensions_IsNotTrue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to value '{0}' does not match '{1}'. + /// + internal static string EnsureExtensions_NoMatch { + get { + return ResourceManager.GetString("EnsureExtensions_NoMatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No MemberExpression found in expression: '{0}'.. + /// + internal static string ExpressionUtils_GetRightMostMember_NoMemberFound { + get { + return ResourceManager.GetString("ExpressionUtils_GetRightMostMember_NoMemberFound", resourceCulture); + } + } + } +} diff --git a/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.resx b/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.resx new file mode 100644 index 000000000..75de3f352 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/Resources/ExceptionMessages.resx @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + value '{0}' is not greater than or equal to limit '{1}'. + + + value '{0}' is not greater than limit '{1}'. + + + value '{0}' is > max '{1}'. + + + value '{0}' is < min '{1}'. + + + Empty collection is not allowed. + + + Empty Guid is not allowed. + + + Value can not be null. + + + No MemberExpression found in expression: '{0}'. + + + The string can't be left empty, null or consist of only whitespaces. + + + Expected an expression that evaluates to false. + + + Expected an expression that evaluates to true. + + + The param is not of expected type: '{0}'. + + + value '{0}' is not lower than limit '{1}'. + + + value '{0}' is not lower than or equal to limit '{1}'. + + + The string can't be null or empty. + + + The string is too long. Must be between '{0}' and '{1}'. Must be between '{0}' and '{1}' but was '{2}' characters long. + + + The string is not long enough. Must be between '{0}' and '{1}' but was '{2}' characters long. + + + value '{0}' does not match '{1}' + + \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/StringExtensions.cs b/NzbDrone.Common/EnsureThat/StringExtensions.cs new file mode 100644 index 000000000..b2e4e8560 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/StringExtensions.cs @@ -0,0 +1,15 @@ +namespace NzbDrone.Common.EnsureThat +{ + internal static class StringExtensions + { + internal static string Inject(this string format, params object[] formattingArgs) + { + return string.Format(format, formattingArgs); + } + + internal static string Inject(this string format, params string[] formattingArgs) + { + return string.Format(format, formattingArgs); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/TypeParam.cs b/NzbDrone.Common/EnsureThat/TypeParam.cs new file mode 100644 index 000000000..202dacf87 --- /dev/null +++ b/NzbDrone.Common/EnsureThat/TypeParam.cs @@ -0,0 +1,15 @@ +using System; + +namespace NzbDrone.Common.EnsureThat +{ + public class TypeParam : Param + { + public readonly Type Type; + + internal TypeParam(string name, Type type) + : base(name) + { + Type = type; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/EnvironmentProvider.cs b/NzbDrone.Common/EnvironmentProvider.cs index cb3585c85..4004de714 100644 --- a/NzbDrone.Common/EnvironmentProvider.cs +++ b/NzbDrone.Common/EnvironmentProvider.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Common { public const string NZBDRONE_PATH = "NZBDRONE_PATH"; public const string NZBDRONE_PID = "NZBDRONE_PID"; - public const string ROOT_MARKER = "NzbDrone.Web"; + public const string ROOT_MARKER = "IISExpress"; public static readonly char[] NewLineChars = Environment.NewLine.ToCharArray(); @@ -38,7 +38,10 @@ namespace NzbDrone.Common public static bool IsMono { - get { return Type.GetType("Mono.Runtime") != null; } + get + { + return Type.GetType("Mono.Runtime") != null; + } } public static bool IsDebug diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 7873b2980..a8e1e52d1 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -71,11 +71,13 @@ MinimumRecommendedRules.ruleset - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.dll - - ..\packages\Autofac.2.6.3.862\lib\NET40\Autofac.Configuration.dll + + False + ..\packages\Autofac.3.0.1\lib\net40\Autofac.Configuration.dll False @@ -99,6 +101,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -126,6 +148,7 @@ + @@ -139,6 +162,9 @@ True + + +