我正在使用基于REST的Web服务,使用NancyFx作为底层框架。但是,我的任务要求我使用Spring.Net进行依赖注入。我对C#(我在这个任务之前大部分都在研究Java代码)或Spring本身都没有太多的经验,而且我还没有找到很多关于使用Spring制作自定义引导程序的信息。 IoC容器,也没有像Ninject或Unity那样的预配置引导程序。
是否有一种很好的方法可以让Nancy和Spring玩得很好,或者我最好还是回到微软的MVC框架来完成我的工作?
提前多多感谢。
答案 0 :(得分:1)
Strip45,
不是很复杂,但是很健康。 Spring.Net是一个声明性配置容器,TinyIoCContainer是一个Register / Resolver容器。也许初看起来你看不出概念差异的问题,但一般来说,寄存器/解析器容器会自动填充,大多数情况下会发现注册它们的类型。 要更改NancyFx IoC容器,您可以从:
NancyBootstrapperWithRequestContainerBase<TContainer>
实现其抽象和虚拟方法很简单,但您需要配置超过60个对象定义。这非常复杂,因为如果在NancyFx的新版本中他们创建了一个新的可选依赖项,您将不会收到通知。
我现在正努力允许两个容器一起工作,只在Spring.Net上托管NancyModules。任何特定的NancyFx基础结构依赖关系仍然可以动态发现并像过去一样在容器上注册。
一个建议:如果你使用相同的策略,不向TinyIoCContainer发送弹簧代理,它会在初始化时崩溃。
答案 1 :(得分:1)
所以我通过结合Christian Horsdal和Luiz Carlos Faria的建议找到了解决方案。我现在已经成功地注入了一个工作注射&#34; Hello world&#34;模块通过南希。我最终做的是制作一个包含DualContainer
和IApplicationContext
的{{1}}课程,并将其实施为TinyIoCContainer
。我在大多数操作中使用了TinyIoCContainer,只有在对象XML中有模块定义时才调用Spring容器。
我实现它的方式确实假设模块在其类名下注册,所以这是需要考虑的事情。
DualContainer类:
NancyBootstrapperWithRequestContainerBase
Spring对象定义(configSections用于数据库配置,不用于此示例):
using Nancy.TinyIoc;
using Spring.Context;
namespace FORREST.WebService.General.Bootstrap
{
public class DualContainer
{
public TinyIoCContainer TinyIoCContainer { get; set; }
public IApplicationContext ApplicationContext { get; set; }
public DualContainer GetChildContainer()
{
return new DualContainer
{
TinyIoCContainer = TinyIoCContainer.GetChildContainer(),
ApplicationContext = this.ApplicationContext
};
}
}
}
自定义引导程序(最不可能是最干净的解决方案,但它对我有用):
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd">
<object name="appConfigPropertyHolder" type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
<property name="configSections">
<value>appSettings</value>
</property>
</object>
<object id="HelloWorldSpringRestModule" type="FORREST.WebService.RESTApi.Modules.HelloWorldSpringRestModule">
<property name="Message" value="Hello World!"/>
</object>
</objects>
最后,实际的NancyModule:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Nancy.Bootstrapper;
using Nancy.TinyIoc;
using Nancy;
using Nancy.Diagnostics;
using Spring.Context;
using Spring.Context.Support;
namespace FORREST.WebService.General.Bootstrap
{
/// <summary>
/// Class enabling the use of Spring injections in modules.
/// </summary>
public abstract class HybridNancyBootstrapperBase : NancyBootstrapperWithRequestContainerBase<DualContainer>
{
/// <summary>
/// Default assemblies that are ignored for autoregister
/// </summary>
public static IEnumerable<Func<Assembly, bool>> DefaultAutoRegisterIgnoredAssemblies = new Func<Assembly, bool>[]
{
asm => asm.FullName.StartsWith("Microsoft.", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("System.", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("System,", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("mscorlib,", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("IronPython", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("IronRuby", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("xunit", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("Nancy.Testing", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("MonoDevelop.NUnit", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("SMDiagnostics", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("CppCodeProvider", StringComparison.InvariantCulture),
asm => asm.FullName.StartsWith("WebDev.WebHost40", StringComparison.InvariantCulture),
};
/// <summary>
/// Gets the assemblies to ignore when autoregistering the application container
/// Return true from the delegate to ignore that particular assembly, returning true
/// does not mean the assembly *will* be included, a false from another delegate will
/// take precedence.
/// </summary>
protected virtual IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
{
get { return DefaultAutoRegisterIgnoredAssemblies; }
}
/// <summary>
/// Configures the container using AutoRegister followed by registration
/// of default INancyModuleCatalog and IRouteResolver.
/// </summary>
/// <param name="container">Container instance</param>
protected override void ConfigureApplicationContainer(DualContainer container)
{
AutoRegister(container, this.AutoRegisterIgnoredAssemblies);
}
/// <summary>
/// Resolve INancyEngine
/// </summary>
/// <returns>INancyEngine implementation</returns>
protected override sealed INancyEngine GetEngineInternal()
{
return this.ApplicationContainer.TinyIoCContainer.Resolve<INancyEngine>();
}
/// <summary>
/// Create a default, unconfigured, container
/// </summary>
/// <returns>Container instance</returns>
protected override DualContainer GetApplicationContainer()
{
return new DualContainer
{
ApplicationContext = ContextRegistry.GetContext(),
TinyIoCContainer = new TinyIoCContainer()
};
}
/// <summary>
/// Register the bootstrapper's implemented types into the container.
/// This is necessary so a user can pass in a populated container but not have
/// to take the responsibility of registering things like INancyModuleCatalog manually.
/// </summary>
/// <param name="applicationContainer">Application container to register into</param>
protected override sealed void RegisterBootstrapperTypes(DualContainer applicationContainer)
{
applicationContainer.TinyIoCContainer.Register<INancyModuleCatalog>(this);
}
/// <summary>
/// Register the default implementations of internally used types into the container as singletons
/// </summary>
/// <param name="container">Container to register into</param>
/// <param name="typeRegistrations">Type registrations to register</param>
protected override sealed void RegisterTypes(DualContainer container, IEnumerable<TypeRegistration> typeRegistrations)
{
foreach (var typeRegistration in typeRegistrations)
{
switch (typeRegistration.Lifetime)
{
case Lifetime.Transient:
container.TinyIoCContainer.Register(typeRegistration.RegistrationType
, typeRegistration.ImplementationType).AsMultiInstance();
break;
case Lifetime.Singleton:
container.TinyIoCContainer.Register(typeRegistration.RegistrationType
, typeRegistration.ImplementationType).AsSingleton();
break;
case Lifetime.PerRequest:
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
default:
throw new ArgumentOutOfRangeException();
}
}
}
/// <summary>
/// Register the various collections into the container as singletons to later be resolved
/// by IEnumerable{Type} constructor dependencies.
/// </summary>
/// <param name="container">Container to register into</param>
/// <param name="collectionTypeRegistrations">Collection type registrations to register</param>
protected override sealed void RegisterCollectionTypes(DualContainer container, IEnumerable<CollectionTypeRegistration> collectionTypeRegistrations)
{
foreach (var collectionTypeRegistration in collectionTypeRegistrations)
{
switch (collectionTypeRegistration.Lifetime)
{
case Lifetime.Transient:
container.TinyIoCContainer.RegisterMultiple(collectionTypeRegistration.RegistrationType
, collectionTypeRegistration.ImplementationTypes).AsMultiInstance();
break;
case Lifetime.Singleton:
container.TinyIoCContainer.RegisterMultiple(collectionTypeRegistration.RegistrationType
, collectionTypeRegistration.ImplementationTypes).AsSingleton();
break;
case Lifetime.PerRequest:
throw new InvalidOperationException("Unable to directly register a per request lifetime.");
default:
throw new ArgumentOutOfRangeException();
}
}
}
/// <summary>
/// Register the given module types into the container
/// </summary>
/// <param name="container">Container to register into</param>
/// <param name="moduleRegistrationTypes">NancyModule types</param>
protected override sealed void RegisterRequestContainerModules(DualContainer container, IEnumerable<ModuleRegistration> moduleRegistrationTypes)
{
foreach (var moduleRegistrationType in moduleRegistrationTypes)
{
container.TinyIoCContainer.Register(
typeof(INancyModule),
moduleRegistrationType.ModuleType,
moduleRegistrationType.ModuleType.FullName).
AsSingleton();
(container.ApplicationContext as IConfigurableApplicationContext).ObjectFactory.
RegisterResolvableDependency(moduleRegistrationType.ModuleType,
container.TinyIoCContainer.Resolve(moduleRegistrationType.ModuleType));
}
}
/// <summary>
/// Register the given instances into the container
/// </summary>
/// <param name="container">Container to register into</param>
/// <param name="instanceRegistrations">Instance registration types</param>
protected override void RegisterInstances(DualContainer container, IEnumerable<InstanceRegistration> instanceRegistrations)
{
foreach (var instanceRegistration in instanceRegistrations)
{
container.TinyIoCContainer.Register(
instanceRegistration.RegistrationType,
instanceRegistration.Implementation);
//Cast zodat het programmatisch kan worden gedaan
(container.ApplicationContext as IConfigurableApplicationContext).ObjectFactory.RegisterResolvableDependency(
instanceRegistration.RegistrationType,
instanceRegistration.Implementation);
}
}
/// <summary>
/// Creates a per request child/nested container
/// </summary>
/// <returns>Request container instance</returns>
protected override sealed DualContainer CreateRequestContainer()
{
return this.ApplicationContainer.GetChildContainer();
}
/// <summary>
/// Gets the diagnostics for initialisation
/// </summary>
/// <returns>IDiagnostics implementation</returns>
protected override IDiagnostics GetDiagnostics()
{
return this.ApplicationContainer.TinyIoCContainer.Resolve<IDiagnostics>();
}
/// <summary>
/// Gets all registered startup tasks
/// </summary>
/// <returns>An <see cref="IEnumerable{T}"/> instance containing <see cref="IApplicationStartup"/> instances. </returns>
protected override IEnumerable<IApplicationStartup> GetApplicationStartupTasks()
{
return this.ApplicationContainer.TinyIoCContainer.ResolveAll<IApplicationStartup>(false);
}
/// <summary>
/// Gets all registered request startup tasks
/// </summary>
/// <returns>An <see cref="IEnumerable{T}"/> instance containing <see cref="IRequestStartup"/> instances.</returns>
protected override IEnumerable<IRequestStartup> RegisterAndGetRequestStartupTasks(DualContainer container, Type[] requestStartupTypes)
{
container.TinyIoCContainer.RegisterMultiple(typeof(IRequestStartup), requestStartupTypes);
return container.TinyIoCContainer.ResolveAll<IRequestStartup>(false);
}
/// <summary>
/// Gets all registered application registration tasks
/// </summary>
/// <returns>An <see cref="IEnumerable{T}"/> instance containing <see cref="IRegistrations"/> instances.</returns>
protected override IEnumerable<IRegistrations> GetRegistrationTasks()
{
return this.ApplicationContainer.TinyIoCContainer.ResolveAll<IRegistrations>(false);
}
/// <summary>
/// Retrieve all module instances from the container
/// </summary>
/// <param name="container">Container to use</param>
/// <returns>Collection of NancyModule instances</returns>
protected override sealed IEnumerable<INancyModule> GetAllModules(DualContainer container)
{
var nancyModules = container.TinyIoCContainer.ResolveAll<INancyModule>(false);
return nancyModules;
}
/// <summary>
/// Retreive a specific module instance from the container
/// </summary>
/// <param name="container">Container to use</param>
/// <param name="moduleType">Type of the module</param>
/// <returns>NancyModule instance</returns>
protected override sealed INancyModule GetModule(DualContainer container, Type moduleType)
{
INancyModule module;
try
{
module = (INancyModule) container.ApplicationContext.GetObject(moduleType.Name, moduleType);
}
//Niet geregistreerd in Spring, gebruik TinyIoCContainer om op te halen
catch (Spring.Objects.Factory.NoSuchObjectDefinitionException)
{
System.Diagnostics.Debug.WriteLine("Laad " + moduleType.Name + " uit TinyIoC in plaats van Spring");
container.TinyIoCContainer.Register(typeof(INancyModule), moduleType);
module = container.TinyIoCContainer.Resolve<INancyModule>();
}
return module;
}
/// <summary>
/// Executes auto registation with the given container.
/// </summary>
/// <param name="container">Container instance</param>
private static void AutoRegister(DualContainer container, IEnumerable<Func<Assembly, bool>> ignoredAssemblies)
{
var assembly = typeof(NancyEngine).Assembly;
container.TinyIoCContainer.AutoRegister(AppDomain.CurrentDomain.GetAssemblies()
.Where(a => !ignoredAssemblies.Any(ia => ia(a)))
, DuplicateImplementationActions.RegisterMultiple, t => t.Assembly != assembly);
}
}
}
感谢帮助人员!
答案 2 :(得分:0)
现在最好的办法是阅读bit of documentation,然后从Nancy Github organization找到的其他容器特定引导程序中汲取灵感 - 例如Ninject one。
要验证实现,您可以对新的引导程序运行the tests NancyBootstrapperBase
。
希望将来我们会有更好的规范来满足bootstrappers的要求。