2

I'm trying to create an Orchard CMS module that enables a RESTful web service using OpenRasta for a given route (/openrasta/* for example).

I need to get to the Orchard ContentManager to get the content for the service to return, so my OpenRasta handler (ContentHandler) uses a ContentService, which implements IContentService, which inherits IDependency. Normally this would work because Orchard will inject a ContentManager into the constructor:

public class ContentService : IContentService {
    public IContentManager content;

    public ContentService(IContentManager content) {
        this.content = content;
    }

    public IEnumerable<string> GetContentTypeDefinitionNames() {
        return content.GetContentTypeDefinitions().Select(d => d.Name);
    }
}

But when I run it I get an error because OpenRasta doesn't know anything about the Orchard dependencies and it's trying to create ContentService, not Orchard, which is fair enough:

OpenRasta.DI.DependencyResolutionException: Could not resolve type ContentService because its dependencies couldn't be fullfilled Constructor: Orchard.ContentManagement.IContentManager

Is there a way to achieve this, can I go to an Orchard class somewhere and say "give me an instance of the ContentManager"?

Update: See my comments on @rfcdejong's answer for updates on my progress.

greg84
  • 7,541
  • 3
  • 36
  • 45

2 Answers2

4

Are u using a ServiceRoute, added in a class implementing IRouteProvider Look at the ServiceRoute summary, it says "Enables the creation of service routes over HTTP in support of REST scenarios."

public class Routes : IRouteProvider
{
    public void GetRoutes(ICollection<RouteDescriptor> routes)
    {
        foreach (var routeDescriptor in GetRoutes())
            routes.Add(routeDescriptor);
    }

    private static ServiceRoute _rastaService = new ServiceRoute(
        "openrasta",
        new MyServiceHostFactory<IOpenRastaService>(), 
        typeof(IOpenRastaService));

    public IEnumerable<RouteDescriptor> GetRoutes()
    {
        return new[] 
        {
            new RouteDescriptor
            {
                Priority = -1,
                Route = _rastaService
            }
        };
    }
}

And want to resolve ContentService? U might have to resolve the interface.

i guess u want the following to work:

var contentService = LifetimeScope.ResolveNew<IContentService>();

I have used HostContainer.Resolve directly and had issues as well. I will describe the solution i'm using at the moment in my own ServiceHostFactory

Do u have a own ServiceHostFactory deriven from OrchardServiceHostFactory? In that case u can implement the following code to help u resolve instances

    private ILifetimeScope _lifetimeScope = null;
    private ILifetimeScope LifetimeScope
    {
        get
        {
            if (_lifetimeScope == null)
            {
                IHttpContextAccessor accessor = HostContainer.Resolve<IHttpContextAccessor>();
                IRunningShellTable runningShellTable = HostContainer.Resolve<IRunningShellTable>();
                ShellSettings shellSettings = runningShellTable.Match(accessor.Current());
                IOrchardHost orchardHost = HostContainer.Resolve<IOrchardHost>();
                ShellContext shellContext = orchardHost.GetShellContext(shellSettings);
                _lifetimeScope = shellContext.LifetimeScope;
            }
            return _lifetimeScope;
        }
    }

I also created LifetimeScopeExtensions that has the following code

public static class LifetimeScopeExtensions
{
    public static T ResolveNew<T>(this ILifetimeScope scope)
    {
        IWorkContextAccessor workContextAccessor = scope.Resolve<IWorkContextAccessor>();
        WorkContext workContext = workContextAccessor.GetContext();
        if (workContext == null)
        {
            using (IWorkContextScope workContextScope = workContextAccessor.CreateWorkContextScope())
            {
                ILifetimeScope lifetimeScope = workContextScope.Resolve<ILifetimeScope>();
                return lifetimeScope.Resolve<T>();
            }
        }
        else
        {
            ILifetimeScope lifetimeScope = workContext.Resolve<ILifetimeScope>();
            return lifetimeScope.Resolve<T>();
        }
    }

    public static object ResolveNew(this ILifetimeScope scope, Type type)
    {
        IWorkContextAccessor workContextAccessor = scope.Resolve<IWorkContextAccessor>();
        WorkContext workContext = workContextAccessor.GetContext();
        if (workContext == null)
        {
            using (IWorkContextScope workContextScope = workContextAccessor.CreateWorkContextScope())
            {
                ILifetimeScope lifetimeScope = workContextScope.Resolve<ILifetimeScope>();
                return lifetimeScope.Resolve(type);
            }
        }
        else
        {
            ILifetimeScope lifetimeScope = workContext.Resolve<ILifetimeScope>();
            return lifetimeScope.Resolve(type);
        }
    }
}


        var settingsService = LifetimeScope.ResolveNew<ITokenServiceSettingsService>();
rfcdejong
  • 2,219
  • 1
  • 25
  • 51
  • PS: I don't know about OpenRasta and what u are doing with it. Orchard uses Autofac and all IDependency interfaces are registered automaticly. – rfcdejong Feb 22 '12 at 13:17
  • Hi @rfcdejong! Thanks so much for the reply. What you've got there looks like it would do the job but I'm a little confused. I don't have a ServiceHostFactory and I'm not sure what a HostContainer is to be honest - can I get the HostContainer for the current Orchard instance or do I need to create one? – greg84 Feb 22 '12 at 13:49
  • Also, I actually want to resolve IContentManager: `var contentManager = LifetimeScope.ResolveNew();` - IContentService can already be resolved by OpenRasta as I've registered the dependency with it directly. It's the link between IContentService and IContentManager I'm struggling with. – greg84 Feb 22 '12 at 14:09
  • Hm, think I'm getting there slowly, but on this line `ShellSettings shellSettings = runningShellTable.Match(accessor.Current());` it throws a NullReferenceException because the current HttpContext is null :( – greg84 Feb 22 '12 at 14:51
  • i inserted a example of serviceRoute in the first part of my answer – rfcdejong Feb 22 '12 at 15:31
  • Sorry I must be a bit thick because I **really** don't get this. My understanding is that the ServiceHostFactory is for WCF web services, but that's not what OpenRasta is. I need to point the route to the HttpHandler for OpenRasta, which works great. I might just give up to be honest... :( – greg84 Feb 22 '12 at 16:00
  • Just create a WebServiceHost for REST and use a WebGet in your contract instead of Operation attribute. There is enough information available on google hehe. There is even a special WebServiceHostFactory that returns a WebServiceHost – rfcdejong Feb 22 '12 at 16:32
  • 1
    Right, sorted. I inherited `OrchardServiceHostFactory`, implemented the `LifetimeScope` property and the `ResolveNew` extension you provided. Then changed the `runningShellTable.Match` method to use the overload for host name and path instead of `HttpContext`. Works beautifully. The only strange thing is that I get an NHibernate exception the first time it runs but only if the module has just been amended and compiled and only if it's the first request to the application after this and only if that first request is to the OpenRasta service - very weird. @rfcdejong, thanks for all your help :) – greg84 Feb 22 '12 at 16:51
  • I'm glad i helped a bit, however that exception is strange in those odd conditions. But i'm glad u got it working after all. – rfcdejong Feb 22 '12 at 16:57
1

So the issue is that your CMS uses its own IoC container. By default OpenRasta does that too.

This means that services that are present in Orchard won't be visible to OpenRasta.

For all other IoC containers, the answer is damn right simple: You use the IoC adaptation layer that lets OpenRasta live in whatever ioc container you want. We support unity, structuremap, castle and ninject. That said, autofac is not supported as no one ever built it.

The cleanest way for you to solve this problem (and any other you may encounter in the future for those issues) would be to build your own autofac ioc adaptation layer for openrasta. If you need help doing that, you can join the openeverything mailing list where the devs would be happy to help you.

SerialSeb
  • 6,701
  • 24
  • 28