Spring documentation on http://springframework.net/docs/1.3.1/reference/html/objects.html says:
"In addition to object definitions which contain information on how to
create a specific object, the IApplicationContext implementations also
permit the registration of existing objects that are created outside
the container, by users. This is done by accessing the
ApplicationContext's IObjectFactory via the property ObjectFactory
which returns the IObjectFactory implementation
DefaultListableObjectFactory. DefaultListableObjectFactory supports
registration through the methods RegisterSingleton(..) and
RegisterObjectDefinition(..)."
I'm trying to access the ObjectFactory object after doing the following:
var context = ContextRegistry.GetContext();
But there is no ObjectFactory property. I'm using Spring.Net v1.3.1.20711 and have Spring.Core referenced in my project.
What am I missing?
The ObjectFactory property is not exposed by the IApplicationContext interface, but is part of the IConfigurableListableObjectFactory interface.
Your context variable will be of inferred type IApplicationContext, because of the return type of ContextRegistry.GetContext(), so in VS it will appear that the ObjectFactory property is not available. However, if you take a closer look, you will see that it is an XmlApplicationContext that has the ObjectFactory property, because it implements IConfigurableListableObjectFactory. As Sebastian points out in his answer, most (if not all) application contexts implement this interface.
E.g.:
var ctx = new XmlApplicationContext("objects.xml");
ctx.ObjectFactory.RegisterSingleton("MyObject2", new MyClass() { Name = "MyObject2"});
var o2 = (MyClass)ctx.GetObject("MyObject2");
Assert.AreEqual("MyObject2", o2.Name);
Just to add to Marjin's answer; here is an extension method to get the Factory which I use.
/// <summary>
/// Gets the ObjectFactory from the Spring context.
/// </summary>
/// <param name="context">The context.</param>
/// <returns></returns>
public static IConfigurableListableObjectFactory Factory(this IApplicationContext context)
{
return ((IConfigurableApplicationContext)context).ObjectFactory;
}
Related
I am using serilog with the sinks File and RollingFile in a crosscutting dll that delivers a logging service. I am configuring with the Appsettings nuget, therefore I have no static dependency to the mentioned sinks. However I do need them at runtime and they are not copied to the bin folder of the application, only to the bin folder of the dll. That means I get a Runtime Exception because the sink-dlls are not there. Is there a way to fix that? My workaround is creating a Variable of type RollingFileSink that I never use. But it is kind of ugly. UPDATE: that solution does not work in Release btw.
I had this issue before with Serilog, and the way I resolved it was to create a static reference to a type inside the assemblies I needed, via an assembly-level attribute that I declare inside the AssemblyInfo.cs of my main project.
Something like this:
[assembly: ImplicitDependency(typeof(Serilog.Sinks.RollingFile.RollingFileSink))]
[assembly: ImplicitDependency(typeof(Serilog.Sinks.File.PeriodicFlushToDiskSink))]
// etc...
And this is the attribute I created inside my project...
/// <summary>
/// Indicates that the marked assembly depends on the type that is specified in the constructor.
/// Typically used to force a compile-time dependency to the assembly that contains the type.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class ImplicitDependencyAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="ImplicitDependencyAttribute"/> class.
/// </summary>
/// <param name="dependencyType">A type from the assembly that is used dynamically.</param>
public ImplicitDependencyAttribute(Type dependencyType)
{
DependencyType = dependencyType;
}
/// <summary>
/// Gets the dependent type reference.
/// </summary>
/// <value>The dependent type reference.</value>
public Type DependencyType { get; private set; }
}
Im trying to use a HTML view to string rendering class i found online. Its called PDFRender atm cause this is what it will be used for. Ive been told that its set up using Dependency Injection and that it should work out of the box.
Im not sure how to instantiate it though. Since the dependencies are supposed to be injected through the constructor.
My class constructor look like this
public PdfRender(IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider)
{
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
And i set it up in the Startup classes ConfigureMetod() like this
// Set up Report PDF html renderer
services.AddScoped<PdfRender, PdfRender>();
Now m trying this in my code (after looking at the links example)
PdfRender pdfRender;
string iWantToBetml = pdfRender.ModelToHTML(inspection);
But i get an error (ofcourse) saying that i cant use an unassigned variable. I guess i dont understand how the DI is supposed to be used. Im assuming the idea is to give the default viewEngine, dataProvider and serviceProviders.
You just need to add a PdfRender parameter to the constructor of your controller:
public PdfRender(PdfRender pdfRender, IRazorViewEngine viewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider)
{
_pdfRender = pdfRender;
_viewEngine = viewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
Then you can use in in an instance method on that controller
public SomeMethod(){
string iWantToBetml = _pdfRender.ModelToHTML(inspection);
}
Note that in your controller constructor you don't necessarily need to specify all these parameters, just specify the ones that the class needs and then those will be injected in from the DI container provided their types have been registered with the DI container at startup.
It seems like StdSchedulerFactory returns a singleton with the name defined in config:
<add key="quartz.scheduler.instanceName" value="MyQuartzScheduler" />
As quartz config section consists of key value pairs it looks like using factory for instantiating scheduler limits the number of available schedulers to one.
AFIAK, you can create as many schedulers as you like within any application, but you cannot use default quartz config approach for this, as it expect only one collection of scheduler properties (look into StdSchedulerFactory implementation and this blog if interesting):
By default, In Quartz.Net, the StdSchedulerFactory is responsible for configuring the scheduler. When the Quartz.Net scheduler is started, the factory will try to automatically configure a scheduler by looking for configuration information in different places:
the hosting application’s configuration file
a file specified in an environment variable
the quartz.config file
the embedded configuration file
SO what you can do is not to use automatic scheduler configuration, but
by himself create a separate collections of properties and pass them to scheduler creation constructors:
public StdSchedulerFactory(NameValueCollection props);
using code approach:
NameValueCollection scheduler1Properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "SingleThreadScheduler";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "1";
...
var factory = new StdSchedulerFactory(scheduler1Properties);
or you can create separate quartz configs and directly use quartz PropertiesParser class to read
/// <summary>
/// Reads the properties from file system.
/// </summary>
/// <param name="fileName">The file name to read resources from.</param>
/// <returns></returns>
public static PropertiesParser ReadFromFileResource(string fileName)
and get collection:
/// <summary>
/// Gets the underlying properties.
/// </summary>
/// <value>The underlying properties.</value>
public virtual NameValueCollection UnderlyingProperties
{
get { return props; }
}
// PropertiesParser class is directly used in default config reading implementation.
I have a console app and web API both referencing the same data layer which is a separate project.
In that data layer, I have a class that requires a repository that we are grabbing from the container when that class is instantiated.
In that class, it has a base class which we are doing the following in the constructor to setup the Repository:
IContainerAccessor containerAccessor = HttpContext.Current.ApplicationInstance as IContainerAccessor;
Repository = containerAccessor.Container.Resolve<IRepository>();
What would be the best way to set this up? This is obviously a problem for our console application as it has no HttpContext.
If I'm correct you want to setup your console app so it can inject classes from the shared data layer.
To do so, you need to create an installer for the console app and tell it to run the installers in the shared library, but to modify the life style from 'PerWebRequest' to 'Singleton' or 'Transient'.
For more information read this article:
http://blog.ploeh.dk/2010/04/26/ChangingWindsorlifestylesafterthefact/
Be aware that changing this may cause problems.
I.e.: If multiple components configured as "perWebRequest" require a 'Unit-Of-Work' to be injected, then this uow will be different for all components if you change the life style to transient.
Changing it to Singleton causes the same but opposite problem. Objects that are created now will have the same object for different requests ...
If you are okay with the problems this code should get you starting
public class ConsoleAppInstaller: IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// 1) make sure we do not use PerWebRequest life style types
var convertWebToTransient = new WebToTransientConvertor();
container.Kernel.ComponentModelBuilder.AddContributor(convertWebToTransient);
// 2) call installers on all libraries we use ...
container.Install(FromAssembly.Containing<SharedDataLayerInstaller>());
// 3) link internal services ...
container.Register(Component.For<IXxxxFactory>().AsFactory());
container.Register(Component.For<IYyyyFactory>().AsFactory());
container.Register(Classes.FromThisAssembly().Where(c => typeof(Form).IsAssignableFrom(c)).LifestyleTransient());
}
public static IWindsorContainer Bootstrap()
{
return new WindsorContainer().Install(FromAssembly.This());
}
}
/// <summary>
/// This class allows to intercept installers using PerWebRequest lifestyles and replaces them with Transient life styles.
/// <code>container.Kernel.ComponentModelBuilder.AddContributor(new WebToTransientConvertor())</code>
/// </summary>
public class WebToTransientConvertor : IContributeComponentModelConstruction
{
//http://blog.ploeh.dk/2010/04/26/ChangingWindsorlifestylesafterthefact/
public void ProcessModel(IKernel kernel, ComponentModel model)
{
if (model.LifestyleType == LifestyleType.PerWebRequest)
//model.LifestyleType = LifestyleType.Transient;
model.LifestyleType = LifestyleType.Singleton;
}
}
Is there a way to use Property Injection in Ninject 2 without using the [Inject] attribute? This creates a dependency to Ninject in the class that will be wired using it and I prefer to avoid having unneeded dependencies to my IoC container, that's why I end up using Constructor Injection more often.
I guess the same applies to Method Injection
I followed Ruben's tip and posted a small blog post on how to achieve this, but here's the quick answer:
Create a custom attribute:
public class InjectHereAttribute : Attribute
{
}
The target class will now look like this:
public class Samurai
{
[InjectHere]
public IWeapon Context { get; set; }
}
Now Ninject must be configured to use the custom attribute, this can be done by creating an implementation of IInjectionHeuristic that recognizes the custom attribute:
public class CustomInjectionHeuristic : NinjectComponent, IInjectionHeuristic, INinjectComponent, IDisposable
{
public new bool ShouldInject(MemberInfo member)
{
return member.IsDefined(
typeof(InjectHereAttribute),
true);
}
}
And finally add this behavior to the Ninject Kernel using the Components collection, it will run along the existing components, namely the default implementation of IInjectionHeuristic, which means either the default or the custom attribute can be used.
// Add custom inject heuristic
kernel.Components.Add<IInjectionHeuristic, CustomInjectionHeuristic>();
You can pass in another [attribute] type to the Kernel upon creation which can be used instead of InjectAttribute, but you'll still have to reference something centrally OOTB.
There was a similar question very recently about doing PI without attributes - there's no OOTB (as in directly on the fluent configuration interface) to put in a custom scanner but the extensibility points (you add a component that implements a Ninject interface as you build your Kernel that dictates how that aspect is to be work if looking for a given attribute isnt't what you want) are in there to determine where to inject based on Convention over Configuration - there's nothing stopping you amending the scanning to be based on just an attribute name (so it doesnt necessarily have to live in a central location).
Note that, in general, constructor injection is good for lots of reasons anyway, including this one, and keeping you code container agnostic is important (even if you're currently happy with one!)
I was able to accomplish this using a Heuristic class:
public sealed class MyInjectionHeuristic : NinjectComponent, IInjectionHeuristic
{
private static readonly IList<Type>
_propertyInjectible =
new List<Type>
{
typeof(IMyService),
};
/// <summary>
/// Returns a value indicating whether the specified member should be injected.
/// </summary>
/// <param name="member">The member in question.</param>
/// <returns><c>True</c> if the member should be injected; otherwise <c>false</c>.</returns>
public bool ShouldInject(MemberInfo member)
{
var info = member as PropertyInfo;
if( member == null || info == null )
return false;
if (info.CanWrite)
return _propertyInjectible.Contains(info.PropertyType);
if( this.Settings == null )
return false;
var propList = member.GetCustomAttributes(this.Settings.InjectAttribute, true);
return propList.Length > 0;
}
}
When creating your kernel:
var heuristics = _kernel.Components.Get<ISelector>().InjectionHeuristics;
heuristics.Add(new MyInjectionHeuristic());
Simple add additional types to the IList when you want to inject other types via properties.