Sitecore: inject dependency into sitecore components - dependency-injection

I'm using Sitecore 8.1 MVC with Autofac as the DI. I was wondering what is the recommended way to injecting resolved objects into sitecore created objects i.e. pipelines, commands, computed fields etc... As an example, i am using a membership provider in which i need to invoke my business layer. Is it possible for me to define a constructor on the class and sitecore would injects the objects?
Thanks

With things like pipeline processors, commands etc... Basically anything that Sitecore creates - you are fairly limited. The normal approach is to use the Service Locator pattern to resolve dependencies:
var membershipProvider = DependencyResolver.Current.Resolve<IMembershipProvider>()
There are other ways tho. This post: https://cardinalcore.co.uk/2014/07/02/sitecore-pipelines-commands-using-ioc-containers/ uses a container factory class to resolve the dependencies in the pipeline. This is the class used:
using System;
using System.Diagnostics.CodeAnalysis;
using Sitecore.Reflection;
public class ContainerFactory : IFactory
{
private readonly IContainerManager containerManager;
public ContainerFactory() : this(new LocatorContainerManager()) // service locate an appropriate container
{
}
public ContainerFactory(IContainerManager containerManager)
{
this.containerManager = containerManager;
}
public object GetObject(string identifier)
{
Type type = Type.GetType(identifier);
return this.containerManager.Resolve(type);
}
}
Then this would be setup as the factory for the event or processor using the factory attribute in the config. Example config:
<sitecore>
<events>
<event name="item:saved">
<handler factory="ContainerFactory" ref="MyApp.MyHandler, MyApp" method="MyMethod">
<database>master</database>
</handler>
</event>
</events>
<pipelines>
<MyPipeline>
<processor type="1" factory="ContainerFactory" ref="MyApp.MyProcessor, MyApp" />
</MyPipeline>
</pipelines>
<factories>
<factory id="ContainerFactory" type="MyApp.ContainerFactory"></factory>
</factories>
</sitecore>
With this second method, you can inject your dependencies in your constructor as you normally would.
These are probably the 2 most commonly used options.

Related

CDI in JUnit tests with Jersey Test Framework

We are using the Jersey Test Frameworks for API testing. In test-mode, we use an h2 database, mysql in production. Everything is fine to this point.
Now i want to write tests for our repositories to check if the data is written properly to the database.
I can't inject any classes in my tests so i am using the standard constructor the create an new instance of RepositoryA. Works for me.
Now the problem: RepositoryA is now injecting an instance of RepositoryB. And injection isn't working on test-scope.
Is it possible to get injection running in this environment?
Depending on the versions of the libraries you are using, running CDI in JUnit Test is different.
First you need to add this dependency, selecting the right version :
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-junit5</artifactId> // or weld-junit4
<version>1.3.0.Final</version>
<scope>test</scope>
</dependency>
Then you can enable Weld in your JUnit test. Here is an example of injecting a repository for an entity class called VideoGame :
#Slf4j
#EnableWeld
class VideoGameRepositoryTest
{
#WeldSetup
private WeldInitiator weld = WeldInitiator.performDefaultDiscovery();
#Inject
private VideoGameRepository repo;
#Test
void test()
{
VideoGame videoGame = VideoGameFactory.newInstance();
videoGame.setName("XENON");
repo.save(videoGame);
// testing if the ID field had been generated by the JPA Provider.
Assert.assertNotNull(videoGame.getVersion());
Assert.assertTrue(videoGame.getVersion() > 0);
log.info("Video Game : {}", videoGame);
}
}
The important parts are :
the #EnableWeld placed on the JUnit test class.
the #WeldSetup placed on a WeldInitiator field, to lookup to all annotated classes.
don't forget beans.xml in META-INF of your test classpath in order to setup the discovery-mode.
#Slf4j is a lombok annotation, you don't need it (unless you are already using Lombok)
Here the VideoGameRepository instance benefits injection as well, like in a classical CDI project.
Here is the code of the VideoGameFactory which gets a brand new instance of the entity class marked with #Dependent scope. This factory programmatically invokes the CDI current context.
public class VideoGameFactory
{
public static VideoGame newInstance()
{
// ask CDI for the instance, injecting required dependencies.
return CDI.current().select(VideoGame.class).get();
}
}
Alternately, you can have a look to Arquillian which can come with a full Java EE server in order to have all the needed dependencies.

How does EF7 bootstrap dependency injection (IServiceCollection) if you're not in ASP.NET 5?

I'm still trying to get my head around what's what with ASP.NET 5 / EF 7. I'm using DNX projects (.xproj).
Startup is used by OWIN/ASP.NET for configuring, loading services, etc. But it's also used for EF 7 migrations (to set your DbContextOptions for example).
My main goal is to know how EF7 (and ASP.NET 5) bootstrap with Startup and who's creating the startup class, initializing the DI container, etc.
An example of what I need to do, for context, is that in my xUnit unit tests (which are in their own assembly and reference my data assembly which doesn't have a Startup class), I need to AddDbContext to set my connection.
I have the sample startup class:
namespace Radar.Data
{
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<RadarDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
public void Configure(IApplicationBuilder app)
{
}
}
}
This is currently in my data assembly and not my unit test assembly. I tried adding the app setting (I know it's OWIN but I thought I'd give it a shot):
<appSettings>
<add key="owin:appStartup" value="Radar.Data.Startup, Radar.Data" />
</appSettings>
The startup class is not getting executed.
I'd really like an understanding of the overall mechanism with Startup, who calls it, etc., but for now, I just need an understanding of how EF 7 initializes dependencies/services so that I can properly initialize my unit tests.
UPDATE
Here's what I've got in my unit test so far and I thought I had it working at one point:
ServiceCollection serviceCollection = new ServiceCollection();
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
DbContextActivator.ServiceProvider = serviceProvider;
serviceCollection.AddEntityFramework()
.AddSqlServer()
.AddDbContext<RadarDbContext>(
options => options.UseSqlServer("Server=.;Database=SonOfRadar;Trusted_Connection=True;MultipleActiveResultSets=True"));
but now I'm getting No service for type 'Microsoft.Data.Entity.Internal.IDbSetInitializer' has been registered when my DbContext is instantiated. So obviously not getting all the EF services loaded.
If I comment out:
DbContextActivator.ServiceProvider = serviceProvider;
it errors earlier with: No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.
Setting DbContextActivator.ServiceProvider is the only place in EF7 where I can find a hook to set your own provider. I'd be just as happy getting an instance of EF7's internal service collection and working with that. I think I'm going to scour the EF7 unit test code again and see if I'm missing a critical piece.
Startup class is created by Microsoft.AspNet.Hosting package when you run you web application (see StartupLoader.cs).
You can also look onto WebApplication.Run method (WebApplication.Run) its an entry point to ASP.NET 5 web applications.
DI is initialized in WebHostBuilder class (WebHostBuilder.cs) and inside dnx in Bootstrapper class (Bootstrapper.cs)

Inject repositories into service?

I have following;
interface IRepository
--interface IRepositoryEF: IRepository
--interface IRepositoryNH: IRepository
----interface ICategoryRepositoryEF: IRepositoryEF
----interface ICategoryRepositoryNH: IRepositoryNH
I want to use CategoryRepositoryEF and CategoryRepositoryNH classes in the service. How can I inject them into CategoryService?
CategoryService(IRepository repository)
{
}
What is the best practice about this? Could I use a RepositoryFactory and inject it into service and then create repositories in the services?
I mean something like following;
CategoryService(CategoryRepositoryFactory factory)
{
var CategoryRepositoryEF = factory.Create("EF");
var CategoryRepositoryNH = factory.Create("NH");
}
Is this good idea? Or I m completely wrong?
The idea is a bit off.
the purpose of repository interfaces is to abstract away the data source. the goal is to allow all using classes to fetch information without knowing where the data comes from. But in your case you force the classes to know wether NHibernate or EntityFramework is used. Why?
I also wouldn't have a IRepository interface, as you create specific interfaces.
Now the question is rather "How do I map a nhibernate or entity framework repository to one of my interfaces".
That should be done when the application starts. You typically do something like this:
container.Register<ICategoryRepostitory, NHCategoryRepository>();
container.Register<IUserRepostitory, EFUserRepository>();
If you don't do that you have effectivly coupled the using code with a specific implementation. There is really no need for any interfaces at all then.
Update
Which repository is used if I inject like CategoryService(ICategoryRepository repository)?
The repository that you registered in your inversion of control container. My point is that you should not register both implementations but just one of them for every repository.
A simple example would be below.
First manage the repository type with your web.config:
<configuration>
<appSettings>
<add key="RepositoryType" value="NHibernate" />
</appSettings>
</configuration>
And then do this when you configure your inversion of control container:
if (ConfigurationManager.AppSettings["RepositoryType"] == "NHibernate"))
{
_autofac.RegisterType<NHCategoryRepository>.As<ICategoryRepository>();
_autofac.RegisterType<NHUserRepository>.As<IUserRepository>();
}
else
{
_autofac.RegisterType<EFCategoryRepository>.As<ICategoryRepository>();
_autofac.RegisterType<EFUserRepository>.As<IUserRepository>();
}
You could use a dependency injector as Ninject and inject parameter trough contructor
Your NinjectModule
public class NinjectModule : NinjectModule
{
public override void Load()
{
this.Bind<ICategoryRepositoryEF>().To<CategoryRepositoryEF>();
this.Bind<ICategoryRepositoryNH >().To<CategoryRepositoryNH>();
this.Bind<ICategoryService >().To<CategoryService>();
}
}
Inject dependencies
var kernel = new StandardKernel(new NinjectModule());
var categoryService = kernel.Get<ICategoryService>();
This is just an example but take into account that dependencies should be injected on the composition root (entry point) of the application.

Calling Remote EJB3 in separate EAR file from JSF in another EAR file on WAS 8

I'm new to EJB3 and having trouble calling a remote ejb from my JSF managed bean when the two are in separate ear files on the same server (WAS 8). If they are in the same ear file then I have no problems. But I need the call to work across different applications on the same server.
During the EJB injection into the Managed Bean, I get the following exception:
Caused by: javax.ejb.EJBException: The EJB/BelgianBeerSessionBean EJB reference in the null component in the BeerStoreWebProject.war module of the BeerStoreWebEAR application could not be resolved; nested exception is: com.ibm.ejs.container.EJBNotFoundException: EJB with interface com.ejb.view.BelgianBeerSessionBeanRemote not present in application BeerStoreWebEAR
Caused by: com.ibm.ejs.container.EJBNotFoundException: EJB with interface com.ejb.view.BelgianBeerSessionBeanRemote not present in application BeerStoreWebEAR
at com.ibm.ejs.container.HomeOfHomes.getHomeByInterface(HomeOfHomes.java:928)
at com.ibm.ws.ejbcontainer.injection.factory.EJBLinkObjectFactory.getObjectInstance(EJBLinkObjectFactory.java:261)
at com.ibm.ws.ejbcontainer.injection.factory.EJBLinkObjectFactory.getObjectInstance(EJBLinkObjectFactory.java:167)
I'm hoping that someone could help me get to the bottom of this and explain how I should be injecting and looking up a remote EJB if it is in a separate EAR file.
Here is my setup:
Project Setup
1) BelgianBeersEJMProjectClient (an ejb client project that contains the interfaces)
package com.ejb.view;
public interface BelgianBeerSessionInterface {
List<Country> getAllCountries();
void saveCountries(List<Country> countries);
}
package com.ejb.view;
#Remote
public interface BelgianBeerSessionBeanRemote extends
BelgianBeerSessionInterface {
}
2) BelgianBeersEJBProject (containing the ejb implementation)
package com.ejb;
#Stateless
public class BelgianBeerSessionBean implements BelgianBeerSessionBeanRemote,
BelgianBeerSessionBeanLocal {
public BelgianBeerSessionBean() {
// TODO Auto-generated constructor stub
}
public List<Country> getAllCountries() {
//to be implemented
return null;
}
public void saveCountries(List<Country> countries) {
//to be implemented
}
}
Also in the META-INF there is an ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="3.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd">
<display-name>BelgianBeersEJBProject </display-name>
<ejb-client-jar>BelgianBeersEJBProjectClient.jar</ejb-client-jar>
</ejb-jar>
3) BelgianBeersWebProject - contains the jsf app
#ManagedBean
#ViewScoped
public class BeerStorePageBean {
#EJB(name="EJB/BelgianBeerSessionBean")
private BelgianBeerSessionBeanRemote store;
public BelgianBeerSessionBeanRemote getStore() {
return store;
}
public void setStore(BelgianBeerSessionBeanRemote store) {
this.store = store;
}
private List<Country> countries = null;
#PostConstruct
public void populateCountries(){
System.out.println("Store = " + store);
countries = store.getAllCountries();
}
public List<Country> getAllCountries() {
return countries;
}
}
and in the web.xml there is an ejb entry:
<ejb-ref>
<description />
<ejb-ref-name>EJB/BelgianBeerSessionBean</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home />
<remote>com.ejb.view.BelgianBeerSessionBeanRemote</remote>
</ejb-ref>
Deployment Units
EAR file 1 (BelgianBeersEARProject.ear) contains:
1) BelgianBeersEJBProject.jar
2) BelgianBeersEJBProjectClient.jar
EAR file 2 (BeerStoreWebEAR.ear) contains:
1. BeerStoreWebProject.war
2. BelginaBeersEJBProjectClient.jar
Please could somebody explain to me the correct way of calling a remote EJB which is in a separate EAR file. Please help! I'm tearing my hair out!
The #EJB annotation (and corresponding <ejb-ref> in XML) will only automatically link if the target EJB is in the same application. From the javadoc:
If no explicit linking information is provided and there is only one
session bean within the same application that exposes the matching
client view type, by default the EJB dependency resolves to that
session bean.
To link to an EJB in another application, you need to specify a binding. You can do this in several ways:
Specify <lookup-name>targetBindingName</lookup-name> in the <ejb-ref> in ejb-jar.xml.
Specify <ejb-ref name="EJB/BelgianBeerSessionBean" binding-name="targetBindingName"/> in the WEB-INF/ibm-web-bnd.xml file of the WAR module containing the ejb-ref. See the InfoCenter for more information on the format of the binding files.
Specify the target binding name during application deployment (that is, do not "use default bindings").
In any case, you're going to need the binding name of the target EJB. The InfoCenter link above describes both the "classic" WebSphere Application Server binding names and the Java EE 6 standard "java:global" names. The former can be configured in ibm-ejb-jar-bnd.xml and the latter cannot (aside from specifying alternate <application-name> or <module-name>), but it doesn't really matter which you use. To find which names are being used, it's easiest to start the EJB application and then look for the CNTR0167I messages that are printed by the EJB container when it starts (the first one is the "classic" binding):
[6/6/13 17:26:04:531 CDT] 00000049 WASNameSpaceB I CNTR0167I: The server is binding the javax.management.j2ee.ManagementHome interface of the Management enterprise bean in the mejb.jar module of the ManagementEJB application. The binding location is: ejb/mgmt/MEJB
[6/6/13 17:26:04:544 CDT] 00000049 AbstractEJBRu I CNTR0167I: The server is binding the javax.management.j2ee.ManagementHome interface of the Management enterprise bean in the mejb.jar module of the ManagementEJB application. The binding location is: java:global/ManagementEJB/mejb/Management!javax.management.j2ee.ManagementHome

Unity and Object Creation

I am using unity as my IoC container. I am trying to implement a type of IProviderRepository. The concrete implementation has a constructor that accepts a type of IRepository. When I remove the constructor parameter from the concrete implementation everything works fine. I am sure the container is wired correctly. When I try to create the concrete object with the constructor I receive the following error:
"The current build operation (build key Build Key[EMRGen.Infrastructure.Data.IRepository1[EMRGen.Model.Provider.Provider], null]) failed: The current type, EMRGen.Infrastructure.Data.IRepository1[EMRGen.Model.Provider.Provider], is an interface and cannot be constructed. Are you missing a type mapping? (Strategy type BuildPlanStrategy, index 3)".
Is it possible to achieve the above mention functionality with Unity? Namely have Unity infer a concrete type from the Interface and also inject the constructor of the concrete type with the appropriate concrete object based on constructor parameters. Below is sample of my types defined in Unity and a skeleton class listing for what I want to achieve. IProviderRepository is implemented by ProviderRepository which has a constructor that expects a type of IRepository.
<typeAlias alias="ProviderRepositoryInterface" type="EMRGen.Model.Provider.IProviderRepository, EMRGen.Model" />
<typeAlias alias="ProviderRepositoryConcrete" type="EMRGen.Infrastructure.Repositories.Providers.ProviderRepository, EMRGen.Infrastructure.Repositories" />
<typeAlias alias="ProviderGenericRepositoryInterface" type="EMRGen.Infrastructure.Data.IRepository`1[[EMRGen.Model.Provider.IProvider, EMRGen.Model]], EMRGen.Infrastructure" />
<typeAlias alias="ProviderGenericRepositoryConcrete" type="EMRGen.Infrastructure.Repositories.EntityFramework.ApplicationRepository`1[[EMRGen.Model.Provider.Provider, EMRGen.Model]], EMRGen.Infrastructure.Repositories" />
<!-- Provider Mapping-->
<typeAlias alias="ProviderInterface" type="EMRGen.Model.Provider.IProvider, EMRGen.Model" />
<typeAlias alias="ProviderConcrete" type="EMRGen.Model.Provider.Doctor, EMRGen.Model" />
Illustrate the call being made inside my class:
public class PrescriptionService
{
PrescriptionService()
{
IUnityContainer uc = UnitySingleton.Instance.Container;
UnityServiceLocator unityServiceLocator = new UnityServiceLocator(uc);
ServiceLocator.SetLocatorProvider(() => unityServiceLocator);
IProviderRepository pRepository =
ServiceLocator.Current.GetInstance<IProviderRepository>();
}
}
public class GenericRepository<IProvider> : IRepository<IProvider>
{
}
public class ProviderRepository : IProviderRepository
{
private IRepository<IProvider> _genericProviderRepository;
//Explict public default constructor
public ProviderRepository(IRepository<IProvider> genericProviderRepository)
{
_genericProviderRepository = genericProviderRepository;
}
}
What you want to do is possible, but you need to tell Unity how to map from interfaces to concrete types. AFAICT, your current configuration registers a lot of types, but doesn't specify how they relate to each other.
That said, static Service Locator is an anti-pattern. Consider changing your code to use proper Constructor Injection instead. That would also simplify your code considerably:
public class PrescriptionService
{
private readonly IProviderRepository pRepository;
public PrescriptionService(IProviderRepository pRepository)
{
if (pRepository == null)
{
throw new ArgumentNullException("pRepository");
}
this.pRepository = pRepository;
}
}
Using Unity, you would be able to wire it up like this:
var container = new UnityContainer();
container.RegisterType<PrescriptionService>();
container.RegisterType<IProviderRepository, ProviderRepository>();
container.RegisterType<IRepository<IProvider>, MyRepository<IProvider>>();
var ps = container.Resolve<PrescriptionService>();
Configure the container and resolve all components in the application's Composition Root.
You should only use XML configuration if you need to be able to change certain components without recompiling your application.

Resources