I am trying to create an ASP.NET MVC application, using Spring.NET to inject dependencies. The application has three tiers: Controller, Service, and Data.
I have defined the objects in the file "~\Resources\objects.xml".
My first object, UserAccountController, requires the injecion of two Service-tier classes: UserAccountService and DepartmentService. So, the definition in objects.xml looks like this:
<object id="UserAccountController" type="App.Controllers.UserAccountController, App">
<constructor-arg index="0" ref="DepartmentService" />
<constructor-arg index="1" ref="UserAccountService" />
</object>
<object id="UserAccountService" type="App.Service.UserAccountService, App">
<property name="UserAccountDao" ref="UserAccountDao" />
</object>
<object id="UserAccountDao" type="App.Data.UserAccountDao, App" />
<object id="DepartmentService" type="App.Service.DepartmentService, App">
<property name="DepartmentDao" ref="DepartmentDao" />
</object>
<object id="DepartmentDao" type="App.Data.DepartmentDao" />
Webconfig contains this:
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="~/Resources/objects.xml" />
</context>
</spring>
I would prefer to use Property injection rather than constructor, but currently neither method is working.
Well, it turned out to be that ASP.NET MVC and Spring.NET just don't get along...
However, the MvcContrib package (actually, the Extras package) seems to have solved the issue. The package had a Spring Controller factory implementation that worked, and everything was happy.
(Kind of reminds me of trying to make Struts 1.X and Spring work on the Java side...)
in your bootstrapclass you have to load the spring container
ContextRegistry.getContext();
by the way you need to specify the assembly name for DepartmentDao
<object id="DepartmentDao" type="App.Data.DepartmentDao, App" />
Further information: I also have classes for SpringApplicationController and SpringControllerFactory:
SpringApplicationController.cs:
public static class SpringApplicationContext
{
private static IApplicationContext Context { get; set; }
/// <summary>
/// Returns a boolean value if the current application context contains an named object.
/// </summary>
/// <param name="objectName">Accepts the name of the object to check.</param>
public static bool Contains(string objectName)
{
SpringApplicationContext.EnsureContext();
return SpringApplicationContext.Context.ContainsObject(objectName);
}
/// <summary>
/// Return a instance of an object in the context by the specified name.
/// </summary>
/// <param name="objectName">Accepts a string object name.</param>
public static object Resolve(string objectName)
{
SpringApplicationContext.EnsureContext();
return SpringApplicationContext.Context.GetObject(objectName);
}
/// <summary>
/// Return a instance of an object in the context by the specified name and type.
/// </summary>
/// <typeparam name="T">Accepts the type of the object to resolve.</typeparam>
/// <param name="objectName">Accepts a string object name.</param>
public static T Resolve<T>(string objectName)
{
return (T)SpringApplicationContext.Resolve(objectName);
}
private static void EnsureContext()
{
if (SpringApplicationContext.Context == null)
{
SpringApplicationContext.Context = ContextRegistry.GetContext();
}
}
}
SpringControllerFactory.cs:
public class SpringControllerFactory : DefaultControllerFactory
{
public IController CreateController(RequestContext context, Type controllerType)
{
IResource input = new FileSystemResource(context.HttpContext.Request.MapPath("Resource\\objects.xml"));
IObjectFactory factory = new XmlObjectFactory(input);
return (IController) factory.GetObject(controllerType.Name);
}
public IController CreateController(RequestContext context, string controllerName)
{
IController controller = null;
string controllerClassName = string.Format("{0}Controller", controllerName);
if (SpringApplicationContext.Contains(controllerClassName))
{
controller = SpringApplicationContext.Resolve<IController>(controllerClassName);
this.RequestContext = context;
}
else
{
controller = base.CreateController(context, controllerName);
}
return controller;
}
public override void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
}
I reference this in my Global.asax as follows:
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(typeof(App.Util.SpringControllerFactory));
RegisterRoutes(RouteTable.Routes);
}
Related
I've tried Dependency injection, but that always gives me a HttpContextAccessor.Current or ActionContext as null because I'm not in a request state (I think). So how can I get this context to just take a view, transform it to a html string (with Model if necessary) and throw it back in JS ? I even tried to call directly the Controller action, but it always gives HttpContext as null... I'm using Asp.NET Core 3.
Please, if someone has been going through, help me :-)
Thanks,
Edit:
I have an asp.net core based on Electron.net for a desktop application. I use a lot of IPC communication to retrieve data from backend c# using Electron.IpcMain.On. I register an action as listener in c# in a class. The main problem is that this class is really outside a normal HttpRequest or a Controller. Here is some sample code:
IpcBase Class
public abstract class IpcBase: IBaseIpcCommunicationClass
{
/// <summary>
/// Find a way to get rid of this and take more than the first window
/// </summary>
protected static BrowserWindow _mainWindow = Electron.WindowManager.BrowserWindows.First();
/// <summary>
/// Send an ipcEvent with a parameter class
/// </summary>
/// <param name="parameters">Parameters to fill</param>
public void RegisterIpcEvent<T>(IpcRegisterModel<T> registerModel) => Electron.IpcMain.On(registerModel.key, o => registerModel.action((T)
((JObject)o).ToObject(typeof(T))));
/// <summary>
/// Send a reply inside a registerevent
/// </summary>
/// <typeparam name="T">Type of model</typeparam>
/// <param name="model">model</param>
public void SendReply<T>(IpcSendParamsModel<T> model)
{
if (!string.IsNullOrEmpty(model.replyTo))
{
Electron.IpcMain.Send(_mainWindow, model.replyTo, model);
}
}
...
}
IpcUI (to get the controller view, just like an ajax call on a controller that retrieve the view in String (I already have that, but not with Ipc)
public class IpcUI: IpcBase
{
public IpcUI(IRazorViewToStringService razorViewRenderService)
{
Console.WriteLine("IpcUI::Constructor");
RegisterIpcEvent(new IpcRegisterModel<IpcSendParamsModel<AjaxPartialModel>>("renderPartial", async (param) =>
{
var param = new IpcSendParamsModel<AjaxPartialModel>("RenderPartial")
{
key = "renderPartial",
model = new AjaxPartialModel()
{
DataModel = "{items: [{\r\n MaterialIcon: \"\",\r\n Title: \"Games\",\r\n Selectable: true,\r\n Active: true,\r\n Key: \"GAMES\",\r\n BadgeCaption: \"new\",\r\n BadgeValue: \"123\",\r\n BadgeColor: \"red darken-1\",\r\n BadgePartialLink: \"\",\r\n BadgeContainerLink: \"\",\r\n BadgeModelLink: \"\",\r\n PartialLink: \"Home/Index\",\r\n ContainerLink: \"#body-content\",\r\n ModelLink: \"\"\r\n }] }".JsonDeserialize<MenuModelHeader>(),
PartialName = "PartialViews/_TopMenu"
}
};
try
{
param.results =
await razorViewRenderService.CreateAndResolveInstanceFromGeneric().RenderViewToStringAsync($"~/Views/{param.model.PartialName}.cshtml",
param.model.DataModel);
}
catch (Exception e)
{
IpcClasses.ExceptionManager.SendException(this, e, $"IpcUI params: {param.model.JsonSerialize()}");
}
}));
}
}
Razor Service (Mostly taken from here Generate string from view)
Added in startup:
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IRazorViewToStringService, RazorRazorViewToStringService>();
When I create an instance of IpcUI, DI gives me the service, but without any HttpContext or ActionContext... Sorry for the lack of information from my last edit :-). Hope it is a bit more specific.
Oh ! I forgot something, IpcUI is created at runtime not with a new (because that don't work) but with a custom extension function that retrieves the IServiceProvider for DI:
In startup
ExtensionsUtils.ServiceProvider = app.ApplicationServices;
In ExtensionsUtils
/// <summary>
/// This is called in configure services to get the collection of services in an extension static class
/// </summary>
public static IServiceProvider ServiceProvider { get; set; }
/// <summary>
/// Create a reference from type T with DI
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="o"></param>
/// <returns></returns>
public static T CreateAndResolveInstanceFromGeneric<T>(this T o)
{
return (T)ActivatorUtilities.CreateInstance<T>(ServiceProvider);
}
Edit 2:
I have tried to access IRazorViewToStringService from a real controller constructor and it's null again... What Am I doing wrong ???
private readonly IRazorViewToStringService _razorViewRenderService;
public BaseController(IRazorViewToStringService razorViewRenderService)
{
_razorViewRenderService = razorViewRenderService;
}
...
/// <summary>
/// Return a success http code and a View rendered as string
/// </summary>
/// <param name="ViewName">Name of MVC View (or PartialView)</param>
/// <param name="Model">Model to pass if any</param>
/// <returns>JSON: { result: "type", description: "Html Code" }</returns>
public async Task<ActionResult> CheckAndReturnView(string ViewName, object Model = null)
{
return Ok(await _razorViewRenderService.RenderViewToStringAsync(ViewName, Model));
}
I'm using Web API 2 with Ninject and i'm getting the following error when i've got multiple parallel HTTP calls.
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
I didn't get this error before, all the code that retrieves the data from the database is async so i'm thinking its a Ninject scope issue.
NinjectWebCommon
kernel.BindHttpFilter<MyAuthenticateFilter>(FilterScope.Action);
// Database context
kernel.Bind<IUnitOfWorkAsyncFactory>().To<UnitOfWorkAsyncFactory>().InRequestScope();
kernel.Bind<IUnitOfWorkAsync>().To<UnitOfWork>().InRequestScope();
kernel.Bind<IDataFactory>().To<DataFactory>().InRequestScope();
All the Application and domain handlers are also async and make use of the same DataFactory and UnitOfWorkAsyncFactory.
Seems like there's a threading issue with the IAuthenticationFilter.
The factory pattern used for the DbContext.
public class DataFactory : Disposable, IDataFactory
{
private MyContext DbContext { get; set; }
public IDataContextAsync GetDataContext()
{
return DbContext ?? (DbContext = new MyContext());
}
public void AfterDispose()
{
DbContext = null;
}
public DataFactory()
{
}
public DataFactory(MyContext context)
{
DbContext = context;
}
}
I remember some time ago having issues with filter injection in WebApi and scopes being lost.
As far as I remember using this implementation of IDependencyResolver (can not remember where I got it) made the issue go away.
using System.Web.Http.Dependencies;
using global::Ninject.Syntax;
public class DependencyResolverWebApiNinject : DependencyScopeWebApiNinject, IDependencyResolver
{
public DependencyResolverWebApiNinject(IResolutionRoot resolutionRoot)
: base(resolutionRoot)
{
}
public virtual IDependencyScope BeginScope()
{
return this;
}
}
Where DependencyScopeWebApiNinject is :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Dependencies;
using global::Ninject;
using global::Ninject.Infrastructure.Disposal;
using global::Ninject.Parameters;
using global::Ninject.Syntax;
public class DependencyScopeWebApiNinject : DisposableObject, IDependencyScope
{
/// <summary>
/// Initializes a new instance of the <see cref="DependencyScopeWebApiNinject"/> class.
/// </summary>
/// <param name="resolutionRoot">The resolution root.</param>
public DependencyScopeWebApiNinject(IResolutionRoot resolutionRoot)
{
this.ResolutionRoot = resolutionRoot;
}
/// <summary>
/// Gets the resolution root.
/// </summary>
/// <value>The resolution root.</value>
protected IResolutionRoot ResolutionRoot
{
get;
private set;
}
/// <summary>
/// Gets the service of the specified type.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <returns>The service instance or <see langword="null"/> if none is configured.</returns>
public object GetService(Type serviceType)
{
var request = this.ResolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
return this.ResolutionRoot.Resolve(request).SingleOrDefault();
}
/// <summary>
/// Gets the services of the specifies type.
/// </summary>
/// <param name="serviceType">The type of the service.</param>
/// <returns>All service instances or an empty enumerable if none is configured.</returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.ResolutionRoot.GetAll(serviceType).ToList();
}
}
hope this will help
I'm working on a REST-based webservice using NancyFx as the underlying framework. However, my assignment requires me to use Spring.Net for dependency injection. I'm not too experienced in C# (I have mostly been working on Java code prior to this assignment) or Spring itself quite yet, and I haven't been able to find a lot of information on making a custom bootstrapper using Spring as its IoC container, nor are there preconfigured bootstrappers like there are for Ninject or Unity.
Is there a good way to make Nancy and Spring play nice, or would I be better off going back to Microsoft's MVC framework for my assignment?
Thanks a lot in advance.
Strip45,
isn't very complicated, but is toilful. Spring.Net is a declarative configuration container and TinyIoCContainer is a Register/Resolver container. Maybe on first look you can't see problems for difference of concepts, but generally, register/resolver containers will be filled automatically, on most cases discovering types to register them.
To change NancyFx IoC Container you may derive from:
NancyBootstrapperWithRequestContainerBase<TContainer>
Implementing its abstract and virtual methods, it's easy, but you will need configure more then 60 object definitions. It's very complicated because if on new releases of NancyFx they create a new optional dependency, you will not be notified about it.
I'm now working to permit work with both containers, side by side, only hosting NancyModules on Spring.Net. Any specific of NancyFx infrastructure dependency still be discovered dynamically and registered on container like in the past.
One advice: if you use same strategy of me, does not send spring proxies to TinyIoCContainer, it crashes on initialization.
So I found a solution by combining the suggestions of Christian Horsdal and Luiz Carlos Faria. I've now managed to get a working injected "Hello world" module through Nancy. What I ended up doing was make a DualContainer class with both an IApplicationContext and a TinyIoCContainer included, and implementing them into a NancyBootstrapperWithRequestContainerBase. I used the TinyIoCContainer for the majority of the operations, with the Spring container only being called if there is a definition of the module in the object XML.
The way I implemented it does assume the module is registered under its class name however, so this is something to take into account.
The DualContainer class:
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
};
}
}
}
Spring object definition (configSections is used for the database config, not used for this example):
<?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>
The custom bootstrapper (not the cleanest solution most likely, but it worked for me):
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);
}
}
}
And finally, the actual NancyModule:
using FORREST.WebService.General;
using FORREST.WebService.General.Modules;
using Nancy;
using Newtonsoft.Json;
namespace FORREST.WebService.RESTApi.Modules
{
public class HelloWorldSpringRestModule : NancyModule
{
public string Message { get; set; }
public string Route_Base
{
get { return Configuratie.Api_Root + "/Hello"; }
}
public HelloWorldSpringRestModule()
{
Get[Route_Base] = HelloSpring;
}
protected internal Response HelloSpring(dynamic parameters)
{
var _response = (Response)(JsonConvert.SerializeObject(Message));
return _response;
}
}
}
Thanks for the help guys!
For now your best bet is to read this bit of documentation and then draw inspiration from one of the other container specific bootstrappers found at the Nancy Github organization- e.g. the Ninject one.
To verify the implementation you could run the tests NancyBootstrapperBase against your new bootstrapper.
Hopefully in the future we will have better specs for the requirements for bootstrappers.
I suspect this only applies to asp.net pages, but according to this:
http://msdn.microsoft.com/en-us/library/2y3fs9xs.aspx
I can define properties in web.config like so:
<profile>
<properties>
<add name="PostalCode" />
</properties>
</profile>
Then go ahead and use them like this:
Profile.PostalCode = txtPostalCode.Text;
But this does not compile for me inside a Controller:
public ActionResult Index()
{
Profile.PostalCode = "codeofpost";
return View();
}
Profile is of type ProfileBase and not dynamic, so I've no idea how this would work, but the document says otherwise.
Profile class is generated only in ASP.NET Website project NOT in ASP.NET Web aplication.
In Web Application project you need to use
ProfielBase.GetPropertyValue(PropertyName);
References: http://weblogs.asp.net/anasghanem/archive/2008/04/12/the-differences-in-profile-between-web-application-projects-wap-and-website.aspx
As I've been told it's not possible, I've decided to use a dynamic to do this for myself. I guess it's just syntactic sugar in the end.
Descending from this enables Profile.PostalCode = "codeofpost";
/// <summary>
/// Provides a dynamic Profile.
/// </summary>
public abstract class ControllerBase : Controller
{
private readonly Lazy<DynamicProfile> _dProfile;
protected ControllerBase()
{
_dProfile = new Lazy<DynamicProfile>(() => new DynamicProfile(base.Profile));
}
private sealed class DynamicProfile : DynamicObject
{
private readonly ProfileBase _profile;
public DynamicProfile(ProfileBase profile)
{
_profile = profile;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _profile.GetPropertyValue(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_profile.SetPropertyValue(binder.Name, value);
_profile.Save();
return true;
}
}
/// <summary>
/// New dynamic profile, can now access the properties as though they are on the Profile,
/// e.g. Profile.PostCode
/// </summary>
protected new dynamic Profile
{
get { return _dProfile.Value; }
}
/// <summary>
/// Provides access to the original profile object.
/// </summary>
protected ProfileBase ProfileBase
{
get { return base.Profile; }
}
}
I've literally just started using the Unity Application Blocks Dependency Injection library from Microsoft, and I've come unstuck.
This is my IoC class that'll handle the instantiation of my concrete classes to their interface types (so I don't have to keep called Resolve on the IoC container each time I want a repository in my controller):
public class IoC
{
public static void Intialise(UnityConfigurationSection section, string connectionString)
{
_connectionString = connectionString;
_container = new UnityContainer();
section.Configure(_container);
}
private static IUnityContainer _container;
private static string _connectionString;
public static IMovementRepository MovementRepository
{
get { return _container.Resolve<IMovementRepository>(); }
}
}
So, the idea is that from my Controller, I can just do the following:
_repository = IoC.MovementRepository;
I am currently getting the error:
Exception is:
InvalidOperationException - The type
String cannot be constructed. You must
configure the container to supply this
value.
Now, I'm assuming this is because my mapped concrete implementation requires a single string parameter for its constructor. The concrete class is as follows:
public sealed class MovementRepository : Repository, IMovementRepository
{
public MovementRepository(string connectionString) : base(connectionString) { }
}
Which inherits from:
public abstract class Repository
{
public Repository(string connectionString)
{
_connectionString = connectionString;
}
public virtual string ConnectionString
{
get { return _connectionString; }
}
private readonly string _connectionString;
}
Now, am I doing this the correct way? Should I not have a constructor in my concrete implementation of a loosely coupled type? I.e. should I remove the constructor and just make the ConnectionString property a Get/Set so I can do the following:
public static IMovementRepository MovementRepository
{
get
{
return _container.Resolve<IMovementRepository>(
new ParameterOverrides
{
{
"ConnectionString", _connectionString
}
}.OnType<IMovementRepository>() );
}
}
So, I basically wish to know how to get my connection string to my concrete type in the correct way that matches the IoC rules and keeps my Controller and concrete repositories loosely coupled so I can easily change the DataSource at a later date.
EDIT 09:52:
Just to re-iterate what I'm after. I want to know the correct way to pass the ConnectionString or an IRepositoryConfiguration object (prefer that idea, thanks Mc) to a concrete class from Unity. I'm not too fussed on what I pass, just how I pass it whilst maintaining loose coupling.
You can configure the unity container for this:
IUnityContainer container = new UnityContainer()
.RegisterType<IMovementRepository, MovementRepository>(
new InjectionConstructor("connectionstring goes here"));
in XLM that would probably be something like this:
<type type="foo.IMovementRepository,foo" mapTo="foo.MovementRepository,foo">
<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
<constructor>
<param name="connectionString" parameterType="System.String" >
<value value="conectionstring goes here" type="System.String"/>
</param>
</constructor>
</typeConfig>
</type>
or wrap the connectionstring as mcaaltuntas points out.
Probably the most straight forward way to do this is to set up a constructor section for your mapped type in the unity configuration and inside the constructor section have a parameter element for the connection string that passes in a name value for a connection string you have defined in the connectionStrings section of your web configuration.
Inside your constructor code for the Repository class, have some code that uses the name value of the connection string to get the full connection string from the connectionStrings section.
EDIT:
Here's an example for you using Unity 2.0
In your web.config, specify the connection string and a mapping for unity to map an IRepository<T> to a SqlRepository<T>. Based on your other questions, we'll assume that IRepository<T> is in your model project and SqlRepository<T> is in your DAL project.
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
</configSections>
<connectionStrings>
<add name="SqlConnection" connectionString="data source=(local)\SQLEXPRESS;Integrated Security= SSPI; Initial Catalog= DatabaseName;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<unity>
<containers>
<container>
<types>
<type type="ModelProject.IRepository`1, ModelProject" mapTo="DALProject.SqlRepository`1, DALProject">
<constructor>
<param name="connectionString">
<value value="SqlConnection" />
</param>
</constructor>
</type>
</types>
</container>
</containers>
</unity>
</configuration>
Now for the IRepository<T> interface in the model project. In this example, I'm also going to be using LINQ to SQL to return objects from the SQL Database
namespace ModelProject
{
/// <summary>
/// Interface implemented by a Repository to return
/// <see cref="IQueryable`T"/> collections of objects
/// </summary>
/// <typeparam name="T">Object type to return</typeparam>
public interface IRepository<T>
{
IQueryable<T> Items { get; }
}
}
And the SQLRepository<T> class in the DAL project
namespace DALProject
{
/// <summary>
/// Generic class for returning an <see cref="IQueryable`T"/>
/// collection of types
/// </summary>
/// <typeparam name="T">object type</typeparam>
public class SqlRepository<T> : IRepository<T> where T : class
{
private Table<T> _table;
public SqlRepository(string connectionString)
{
// use the connectionString argument value to get the
// connection string from the <connectionStrings> section
// in web.config
string connection = ConfigurationManager.ConnectionStrings[connectionString].ConnectionString;
_table = (new DataContext(connection)).GetTable<T>();
}
/// <summary>
/// Gets an <see cref="IQueryable`T"/> collection of objects
/// </summary>
public IQueryable<T> Items
{
get { return _table; }
}
}
}
Let's also use a custom controller factory to allow unity to return controllers for us. This way, unity will inject any dependencies that the controllers have
In global.asax
namespace WebApplicationProject
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
// your routes
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory());
}
}
public class UnityControllerFactory : DefaultControllerFactory
{
private IUnityContainer _container;
public UnityControllerFactory()
{
_container = new UnityContainer();
var controllerTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
where typeof(IController).IsAssignableFrom(t)
select t;
foreach (Type t in controllerTypes)
_container.RegisterType(t, t.FullName);
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(_container);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
// see http://stackoverflow.com/questions/1357485/asp-net-mvc2-preview-1-are-there-any-breaking-changes/1601706#1601706
if (controllerType == null) { return null; }
return (IController)_container.Resolve(controllerType);
}
}
}
And Here's a controller example. PageSize might be defined on a base controller or on the controller as a property.
namespace WebApplicationProject.Controllers
{
public class CustomersController : Controller
{
private IRepository<Customer> _customerRepository;
public int PageSize { get; set; }
public CustomersController() { }
public CustomersController(IRepository<Customer> customerRepository)
{
this._customerRepository = customerRepository;
// let's set it to 10 items per page.
this.PageSize = 10;
}
public ViewResult List(string customerType, int page)
{
var customerByType = (customerType == null) ?
customerRepository.Items : customerRepository.Items.Where(x => x.CustomerType == customerType);
int totalCustomers = customerByType.Count();
ViewData["TotalPages"] = (int)Math.Ceiling((double)totalCustomers/ PageSize);
ViewData["CurrentPage"] = page;
ViewData["CustomerType"] = customerType;
// get the right customers from the collection
// based on page number and customer type.
return View(customerByType
.Skip((page - 1) * PageSize)
.Take(PageSize)
.ToList()
);
}
}
}
When the customers list controller action is invoked, unity will correctly instantiate an instance of SqlRepository<Customer> for the controller and inject this into the constructor. the connectionString string used for SqlRepository<T> is set in the unity configuration and is passed into the constructor for a typed SqlRepository<T>.
I didnt use Unity but I have used configuration objects in these situations. For example you can write your code like this
class Configuration:IRepositoryConfiguration,IMailConfiguration
{
private string connectionString;
//IRepository configurations
public string ConnectionString
{
//read connection string from somewhere
get { return connectionString; }
}
//EMail configurations
public string Smtp
{
get { return smpt; }
}
}
interface IRepositoryConfiguration
{
string ConnectionString { get;}
}
public abstract class Repository
{
public Repository(IRepositoryConfiguration configuration)
{
_connectionString = configuration.ConnectionString;
}
public virtual string ConnectionString
{
get { return _connectionString; }
}
private readonly string _connectionString;
}
So you can register IRepositoryConfiguration and Unity will resolve your configuration objects. Also you can add extra parameters in this approach easily.
Update
I think it is ok to have a constructor that accepts IRepositoryConfiguration object in your concrete classes(abstract repository and MovementRepository). Becase they are implementations details and concrete implementations of IMovementRepository.so they need to know connection string.
Setter or Constructor Injection
I prefer constructor injection over setter injection. I think constructor injection leads more discoverable APIs. in Constructor injection as soon as when you want to instantiate object, you see what an object needs to work but in Setter injection you must learn which property to set to use API. For detailed information you can read Constructor ve Setter Injection
I ll add another way :)
You can use Injection parameters that get passed into the constructor when you register a type. This way you can still use constructor injection.
class Repository : IRepository {
readonly string m_ConnectionString;
public Repository(string connectionString) {
ConnectionString = connectionString;
}
}
//When registering
container.RegisterType<IRepository, Repository>(new InjectionConstructor("connectionstring"));