Create WCF Module

Topics: General, Writing modules
May 9, 2011 at 5:12 PM

I'm trying to create simple Orchard Module which include wcf service:

[ServiceContract]
public interface IService : IDependency
{
	[OperationContract]
	void DoWork();
}

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service : IService
{
	public void DoWork()
	{
	}
}
In Service.svc:
<%@ ServiceHost Language="C#" Debug="true" Service="TestWcf.IService, TestWcf" Factory="Orchard.Wcf.OrchardServiceHostFactory, Orchard.Framework" %>
Routes.cs: 
public class Routes : IRouteProvider
{
	public void GetRoutes(ICollection<RouteDescriptor> routes)
	{
		foreach (var routeDescriptor in GetRoutes())
			routes.Add(routeDescriptor);
	}
	public IEnumerable<RouteDescriptor> GetRoutes()
	{
		return new[] {
                    new RouteDescriptor {   Priority = 20,
                                            Route = new ServiceRoute(
                                                "Services",
                                                 new OrchardServiceHostFactory(),
                                                 typeof(IService))

                    }
                };
	}
}

In Orchard root web.config i added this strings:
 <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    <standardEndpoints>
      <webHttpEndpoint>
        <!-- 
            Configure the WCF REST service base address via the global.asax.cs file and the default endpoint 
            via the attributes on the <standardEndpoint> element below
        -->
        <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true"/>
      </webHttpEndpoint>
    </standardEndpoints>
  </system.serviceModel>

So, my service must be available at address: http://localhost/Services/Service
But actually i get HTTP 400 error.
If i change in Routes.cs typeof(IService)) on typeof(Service)), i get error: Operation is not valid due to the current state of the object.
I tried to find in internet how to start wcf service in Orchard site. But there is no answer.
Please help me to resolve my problem.
May 9, 2011 at 5:36 PM

I find solution for my problem.

I'm add routing with prefix  "Services". So the address of my service is: http://localhost/Services

May 13, 2011 at 1:49 PM

When u go to the Admin panel and look at the action link "Your Site"
@Html.ActionLink(T("Your Site").ToString(), "Index"new { Area = "", Controller = "Home" })</div>

do u see: http://localhost/Orchard/Services?action=Index&controller=Home ?

if not i would like to know the answer to that lol..

May 13, 2011 at 3:14 PM

I found a blog from maarten Balliauw about writing a DynamicServiceRoute instead of ServiceRoute, maybe that would fix some things..
http://blog.maartenballiauw.be/post/2011/05/09/Using-dynamic-WCF-service-routes.aspx

May 16, 2011 at 4:42 AM

Yes, in admin panel i see: http://localhost/Orchard/Services?action=Index&controller=Home

May 16, 2011 at 9:53 AM
Edited May 16, 2011 at 9:55 AM

not only that but i keep getting an error, and only rebuilding the entire solution fixes it for a few...
Whenever orchard sees a change it goes and rebuild some modules... then it goes wrong again

 

 

 

 

Server Error in '/Orchard' Application.

A route with the resolved virtual path '~/Services/VerhuisService' has already been added.
Parameter name: virtualPath

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentException: A route with the resolved virtual path '~/Services/VerhuisService' has already been added.
Parameter name: virtualPath

Source Error:

Line 34: 
Line 35:         public void Activate() {
Line 36:             _routePublisher.Publish(_routeProviders.SelectMany(provider => provider.GetRoutes()));
Line 37:             _modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders()));
Line 38: 


Source File: C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardShell.cs    Line: 36

Stack Trace:

[ArgumentException: A route with the resolved virtual path '~/Services/VerhuisService' has already been added.
Parameter name: virtualPath]
   System.ServiceModel.Activation.ServiceRouteHandler.AddServiceInfo(String virtualPath, ServiceDeploymentInfo serviceInfo) +105
   System.ServiceModel.Activation.ServiceRouteHandler..ctor(String baseAddress, ServiceHostFactoryBase serviceHostFactory, Type webServiceType) +198
   System.ServiceModel.Activation.ServiceRoute..ctor(String routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType) +47
   Stp.Relatie.Routes.GetRoutes() +605
   Orchard.Environment.DefaultOrchardShell.<Activate>b__0(IRouteProvider provider) in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardShell.cs:36
   System.Linq.<SelectManyIterator>d__14`2.MoveNext() +238
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +325
   System.Linq.<GetEnumerator>d__0.MoveNext() +96
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
   System.Linq.Enumerable.ToArray(IEnumerable`1 source) +78
   Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Mvc\Routes\RoutePublisher.cs:27
   Orchard.Environment.DefaultOrchardShell.Activate() in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardShell.cs:36
   Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardHost.cs:117
   Orchard.Environment.DefaultOrchardHost.<CreateAndActivate>b__4(ShellSettings settings) in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardHost.cs:106
   System.Linq.WhereSelectArrayIterator`2.MoveNext() +85
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
   System.Linq.Enumerable.ToArray(IEnumerable`1 source) +78
   Orchard.Environment.DefaultOrchardHost.BuildCurrent() in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardHost.cs:93
   Orchard.Environment.DefaultOrchardHost.BeginRequest() in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardHost.cs:166
   Orchard.Environment.DefaultOrchardHost.Orchard.Environment.IOrchardHost.BeginRequest() in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard\Environment\DefaultOrchardHost.cs:70
   Orchard.WarmupStarter.Starter.OnBeginRequest(HttpContext context, Action`1 registrations) in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard.Startup\Starter.cs:29
   Orchard.Web.MvcApplication.Application_BeginRequest() in C:\DEVELOPMENT\STP Frontend\Development\FeatureTeamWF\src\Orchard.Web\Global.asax.cs:24

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +0
   System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +72
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) +335
   System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +28
   System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19
   System.Web.Util.ArglessEventHandlerProxy.Callback(Object sender, EventArgs e) +57
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +148
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

 

 

 

May 16, 2011 at 11:17 AM

I get same error when trying to disable/enable module.

How can i fix this problem?

May 16, 2011 at 12:47 PM

It goes wrong at creating the ServiceRoute in your own class as IRouteProvider. Creating a ServiceRoute isn't just a definition but it seems that it also register itself in the "ServiceRouteHandler"/
I've no idea how to fix it other then finding a place where to register it just once on the right time.

May 16, 2011 at 2:13 PM
Edited May 16, 2011 at 2:16 PM

It seems to be an issue in the (WCF) .NET framework, the ServiceRoute has a ServiceRouteHandler which has a static routeServiceTable..
I don't know how to fix this.. i hope the team does

PS: Here is someone with the same problem: http://social.msdn.microsoft.com/Forums/en/wcf/thread/10611cac-8286-4300-8ce7-ac39af39aa55

This will throw an exception complaining that a route with the resolved virtual path “~/” already exists.  This happens because the ServiceRoute class uses a hidden ServiceRouteHandler as its RouteHandler and this contains a non-public static routeServiceTable where all ServiceRoutes register their virtual path.  At this point I am only guessing but I would imagine that the ServiceRoute.GetRouteData() simply calls a static ServiceRouteHandler.GetRouteData() that iterates this table searching for a route to take.

I believe that this design creates some issues.  First, RouteTable.Routes is already a table of routes iterated in search of a route to take and creation of another is unnecessary.  In fact, I suspect that the design as described could make a O(N) search into an O(N^2) search.  Consider: I somehow come up with a design in which I add 1000 ServiceRoutes to RoutTable.Routes followed by a route for a default.  Iterating through RouteTable.Routes we first encounter a ServiceRoute which then iterates the hidden routeServiceTable, doesn’t find a route and returns null.  We step in RouteTable.Routes and encounter another ServiceRoute which then iterates the same hidden routeServiceTable.


Anyone from the team knows a quick and dirty workaround ?

May 16, 2011 at 4:13 PM

The workaround is kinda easy.. just construct the ServiceRoute once and assign it to a static..
I though i didn't work but somehow my module was disabled so searched for a hour why o why i did get 404 lol

    public class Routes : IRouteProvider
    {
        private static ServiceRoute _route = new ServiceRoute("Services/VerhuisService",
                                                       new OrchardServiceHostFactory(),
                                                       typeof(IVerhuisService));
                                                        

        public IEnumerable<RouteDescriptor> GetRoutes() {
            return new[] {
                            new RouteDescriptor {   Priority = 0,
                                                    Route = _route
                                                }
Sep 4, 2012 at 10:21 AM
Edited Sep 4, 2012 at 10:34 AM

hi  , 

first question:

When u go to the Admin panel and look at the action link "Your Site"
@Html.ActionLink(T("Your Site").ToString(), "Index"new { Area = "", Controller = "Home" })</div>

do u see: http://localhost/Orchard/Services?action=Index&controller=Home ?

 

second qustion:

I get same error when trying to disable/enable module.

How can i fix this problem?

Sep 4, 2012 at 10:24 AM

2012-09-04 17:22:48,765 [13] Orchard.Environment.DefaultOrchardHost - A tenant could not be started: Default
System.ArgumentException: A route with the resolved virtual path '~/MyTest/Test' has already been added.
Parameter name: virtualPath
   at System.ServiceModel.Activation.ServiceRouteHandler.AddServiceInfo(String virtualPath, ServiceDeploymentInfo serviceInfo)
   at System.ServiceModel.Activation.ServiceRouteHandler..ctor(String baseAddress, ServiceHostFactoryBase serviceHostFactory, Type webServiceType)
   at System.ServiceModel.Activation.ServiceRoute..ctor(String routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
   at VanceInfo.Routes.GetRoutes()
   at Orchard.Environment.DefaultOrchardShell.<Activate>b__0(IRouteProvider provider) in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in D:\Orchard\src\Orchard\Mvc\Routes\RoutePublisher.cs:line 27
   at Orchard.Environment.DefaultOrchardShell.Activate() in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 157
   at Orchard.Environment.DefaultOrchardHost.CreateAndActivateShells() in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 135
2012-09-04 17:22:49,031 [11] Orchard.Environment.DefaultOrchardHost - A tenant could not be started: Default
System.ArgumentException: A route with the resolved virtual path '~/MyTest/Test' has already been added.
Parameter name: virtualPath
   at System.ServiceModel.Activation.ServiceRouteHandler.AddServiceInfo(String virtualPath, ServiceDeploymentInfo serviceInfo)
   at System.ServiceModel.Activation.ServiceRouteHandler..ctor(String baseAddress, ServiceHostFactoryBase serviceHostFactory, Type webServiceType)
   at System.ServiceModel.Activation.ServiceRoute..ctor(String routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
   at VanceInfo.Routes.GetRoutes()
   at Orchard.Environment.DefaultOrchardShell.<Activate>b__0(IRouteProvider provider) in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in D:\Orchard\src\Orchard\Mvc\Routes\RoutePublisher.cs:line 27
   at Orchard.Environment.DefaultOrchardShell.Activate() in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 157
   at Orchard.Environment.DefaultOrchardHost.CreateAndActivateShells() in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 135
2012-09-04 17:23:01,984 [7] Orchard.Environment.DefaultOrchardHost - A tenant could not be started: Default
System.ArgumentException: A route with the resolved virtual path '~/MyTest/Test' has already been added.
Parameter name: virtualPath
   at System.ServiceModel.Activation.ServiceRouteHandler.AddServiceInfo(String virtualPath, ServiceDeploymentInfo serviceInfo)
   at System.ServiceModel.Activation.ServiceRouteHandler..ctor(String baseAddress, ServiceHostFactoryBase serviceHostFactory, Type webServiceType)
   at System.ServiceModel.Activation.ServiceRoute..ctor(String routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
   at VanceInfo.Routes.GetRoutes()
   at Orchard.Environment.DefaultOrchardShell.<Activate>b__0(IRouteProvider provider) in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in D:\Orchard\src\Orchard\Mvc\Routes\RoutePublisher.cs:line 27
   at Orchard.Environment.DefaultOrchardShell.Activate() in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 157
   at Orchard.Environment.DefaultOrchardHost.CreateAndActivateShells() in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 135
2012-09-04 17:23:02,265 [25] Orchard.Environment.DefaultOrchardHost - A tenant could not be started: Default
System.ArgumentException: A route with the resolved virtual path '~/MyTest/Test' has already been added.
Parameter name: virtualPath
   at System.ServiceModel.Activation.ServiceRouteHandler.AddServiceInfo(String virtualPath, ServiceDeploymentInfo serviceInfo)
   at System.ServiceModel.Activation.ServiceRouteHandler..ctor(String baseAddress, ServiceHostFactoryBase serviceHostFactory, Type webServiceType)
   at System.ServiceModel.Activation.ServiceRoute..ctor(String routePrefix, ServiceHostFactoryBase serviceHostFactory, Type serviceType)
   at VanceInfo.Routes.GetRoutes()
   at Orchard.Environment.DefaultOrchardShell.<Activate>b__0(IRouteProvider provider) in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Orchard.Mvc.Routes.RoutePublisher.Publish(IEnumerable`1 routes) in D:\Orchard\src\Orchard\Mvc\Routes\RoutePublisher.cs:line 27
   at Orchard.Environment.DefaultOrchardShell.Activate() in D:\Orchard\src\Orchard\Environment\DefaultOrchardShell.cs:line 40
   at Orchard.Environment.DefaultOrchardHost.ActivateShell(ShellContext context) in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 157
   at Orchard.Environment.DefaultOrchardHost.CreateAndActivateShells() in D:\Orchard\src\Orchard\Environment\DefaultOrchardHost.cs:line 135

Sep 4, 2012 at 3:43 PM

I (think) I solved that exception by storing the service route in a static.

 

So on first 'GetRoutes' call you create the instance, and on all the next 'GetRoutes' calls you return the ServiceRoute you stored in the static

Sep 5, 2012 at 3:02 AM

ablout sercond question ,i seems to work.

whate about first quesion.....

 

first question:

When u go to the Admin panel and look at the action link "Your Site"
@Html.ActionLink(T("Your Site").ToString(), "Index"new { Area = "", Controller = "Home" })</div>

do u see: http://localhost/Orchard/Services?action=Index&controller=Home ?

Sep 17, 2012 at 9:58 AM

To fix link problem in admin panel, just set DataTokens on your ServiceRoute

private static readonly Route TestService =
    new ServiceRoute("Services/TestService", new OrchardServiceHostFactory(), typeof(ITestService))
    {
        DataTokens = new RouteValueDictionary { { "area", "Test.Web.Module" } }
    };

Sep 17, 2012 at 10:55 AM
igoriok wrote:

To fix link problem in admin panel, just set DataTokens on your ServiceRoute

 

private static readonly Route TestService =
    new ServiceRoute("Services/TestService", new OrchardServiceHostFactory(), typeof(ITestService))
    {
        DataTokens = new RouteValueDictionary { { "area", "Test.Web.Module" } }
    };

 

Thanks for this! :)

Sep 19, 2012 at 8:53 AM

Sadly enough this causes another issue : in my module containing the WCF service I also have an admin controller.

All the links that I put in the admin menu now point to the service url instead :/

I just 'hacked' the 'Your Site' to always point to the webroot as a workaround and this is a sufficient solution for us (for now)