gRPC doesn't seem to generate client side code needed for accessing service - asp.net-core

I am exploring gRPC by downloading and following the PortfoliosSample from here.
The sample code are all working fine. When I tried to create my own simple service and client by following the sample, however, I noticed that the generated code on the client side doesn't include the class and functions needed for accessing the service.
In the PortfoliosSample, the client side code generated based on the portfolios.proto includes and class named PortfoliosClinet (in PortfoliosGrpc.cs)
public partial class PortfoliosClient : grpc::ClientBase<PortfoliosClient>
Various functions (such as Get, in the class) are available for client side program to use for invoking the service.
In my generated code, BrokerGrpc.cs, there is no "GroupClient" class or anything similar in it. As a result, my client side code cannot use the generated code to access the service. What am I missing?
Here is the TSAPIBroker.proto file defined on the server
syntax = "proto3";
option csharp_namespace = "Test.API.TSAPIBroker.Protos";
package TSAPIBroker;
message Group {
int32 id = 1;
string name = 2;
}
message Groups {
repeated Group group = 1;
}
message GetRequest {
int32 groupId = 1;
}
message GetResponse {
Group group = 1;
}
service GroupService
{
rpc Get(GetRequest) returns (GetResponse);
}
And here is the generated TSAPIBrokerGrpc.cs
// <auto-generated>
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: TSAPIBroker.proto
// </auto-generated>
#pragma warning disable 0414, 1591
#region Designer generated code
using grpc = global::Grpc.Core;
namespace Test.API.TSAPIBroker.Protos {
public static partial class GroupService
{
static readonly string __ServiceName = "TSAPIBroker.GroupService";
static readonly grpc::Marshaller<global::Test.API.TSAPIBroker.Protos.GetRequest> __Marshaller_TSAPIBroker_GetRequest = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Test.API.TSAPIBroker.Protos.GetRequest.Parser.ParseFrom);
static readonly grpc::Marshaller<global::Test.API.TSAPIBroker.Protos.GetResponse> __Marshaller_TSAPIBroker_GetResponse = grpc::Marshallers.Create((arg) => global::Google.Protobuf.MessageExtensions.ToByteArray(arg), global::Test.API.TSAPIBroker.Protos.GetResponse.Parser.ParseFrom);
static readonly grpc::Method<global::Test.API.TSAPIBroker.Protos.GetRequest, global::Test.API.TSAPIBroker.Protos.GetResponse> __Method_Get = new grpc::Method<global::Test.API.TSAPIBroker.Protos.GetRequest, global::Test.API.TSAPIBroker.Protos.GetResponse>(
grpc::MethodType.Unary,
__ServiceName,
"Get",
__Marshaller_TSAPIBroker_GetRequest,
__Marshaller_TSAPIBroker_GetResponse);
/// <summary>Service descriptor</summary>
public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor
{
get { return global::Test.API.TSAPIBroker.Protos.TSAPIBrokerReflection.Descriptor.Services[0]; }
}
/// <summary>Base class for server-side implementations of GroupService</summary>
[grpc::BindServiceMethod(typeof(GroupService), "BindService")]
public abstract partial class GroupServiceBase
{
public virtual global::System.Threading.Tasks.Task<global::Test.API.TSAPIBroker.Protos.GetResponse> Get(global::Test.API.TSAPIBroker.Protos.GetRequest request, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}
}
/// <summary>Creates service definition that can be registered with a server</summary>
/// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
public static grpc::ServerServiceDefinition BindService(GroupServiceBase serviceImpl)
{
return grpc::ServerServiceDefinition.CreateBuilder()
.AddMethod(__Method_Get, serviceImpl.Get).Build();
}
/// <summary>Register service method with a service binder with or without implementation. Useful when customizing the service binding logic.
/// Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
/// <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
/// <param name="serviceImpl">An object implementing the server-side handling logic.</param>
public static void BindService(grpc::ServiceBinderBase serviceBinder, GroupServiceBase serviceImpl)
{
serviceBinder.AddMethod(__Method_Get, serviceImpl == null ? null : new grpc::UnaryServerMethod<global::Test.API.TSAPIBroker.Protos.GetRequest, global::Test.API.TSAPIBroker.Protos.GetResponse>(serviceImpl.Get));
}
}
}
#endregion

Using the container image mcr.microsoft.com/dotnet/sdk:5.0, I'm able to use your proto to generate both files:
TSAPIBroker.cs
TSAPIBrokerGrpc.cs
Repro:
dotnet new console
dotnet add package Grpc --version 2.33.1
dotnet add package Grpc.Tools --version 2.33.1
dotnet add package Google.Api.CommonProtos --version 2.2.0
Reference your proto from ther project file and then build.
My generated *Grpc.cs contains GroupServiceClientclass.
NOTE the message Groups is defined but not used.

Related

akkaNet test kit issue when testing messges sent from tested actor

I was trying to test message sent from tested actor, but getting timeout exception and a Dead letter info.
As I am using ninject - created a mock method which always replays with probe actor reference.
Am I missing something here?
Assert.Fail failed. Failed: Timeout 00:00:03 while waiting for a message of type System.Type
at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`2 assert, String hint, Boolean shouldLog)
at Akka.TestKit.TestKitBase.InternalExpectMsgEnvelope(Nullable`1 timeout, Action`1 msgAssert, Action`1 senderAssert, String hint)
at Akka.TestKit.TestKitBase.InternalExpectMsg(Nullable`1 timeout, Action`1 msgAssert, String hint)
at Akka.TestKit.TestKitBase.ExpectMsg(T message, Nullable`1 timeout, String hint)
at
AutoApply.UnitTests.SomethingProcessorActors.SomethingProcessorActorTests.SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat()
in SomethingProcessorActorTests.cs: line 58
[WARNING][12/02/2016 16:12:43][Thread 0009][akka://test/user/testProbe] DeadLetter from [akka://test/temp/d]
to [akka://test/user/testProbe]:
[INFO][12/02/2016 16:12:43][Thread 0011][akka://test/user/testProbe] Message
GetOneSomethingAndRemoveFromList from akka://test/temp/d to
akka://test/user/testProbe was not delivered. 1 dead letters
encountered.
Debug Trace:
Setting probe reference: akka://test/user/testProbe
GetDataActorPath for:SomethingsDataActor
GetDataActorPath =>akka://test/user/testProbe
GetDataActorPath for:SomethingCollectorActor
GetDataActorPath =>akka://test/user/testProbe
[TestClass]
public class SomethingProcessorActorTests : TestKit
{
/// <summary>The factory helper</summary>
private IMockingExtension factoryHelper;
private TestProbe probeActorRef;
/// <summary>Configurations this instance.</summary>
[TestInitialize]
public void Config()
{
this.probeActorRef = this.CreateTestProbe("testProbe");
this.factoryHelper = new MockingFactoryHelper();
this.factoryHelper.SetProbe(this.probeActorRef.TestActor);
}
/// <summary>Somethings the processor actor when merge data and gets no profiles logs that.</summary>
[TestMethod]
public void SomethingProcessorActorWhenMergeDataAndGetsNoProfilesLogsThat()
{
// arrange
var actor =
this.Sys.ActorOf(
Props.Create(() => new SomethingProcessorActor(this.factoryHelper as IActorPathAndFactory)),
"SomethingActor");
// act
actor.Tell(new SomethingProcessorActor.ProcessSomethings());
// assert
this.probeActorRef.ExpectMsgFrom<SomethingsDataActor.GetOneSomethingAndRemoveFromList>(actor, new TimeSpan(0, 0, 0, 5));
}
}
=======================
public partial class SomethingProcessorActor : ReceiveActor
{
/// <summary>The helper</summary>
private readonly IActorPathAndFactory helper;
/// <summary>The log</summary>
private readonly ILoggingAdapter log = Context.GetLogger();
/// <summary>The vote execution profile</summary>
private List<SomethingProcessingObject> voteExecutionProfile = new List<SomethingProcessingObject>();
/// <summary>
/// Initializes a new instance of the <see cref="SomethingProcessorActor"/> class.
/// </summary>
/// <param name="helper">
/// The helper.
/// </param>
public SomethingProcessorActor(IActorPathAndFactory helper)
{
this.helper = helper;
this.Receive<ProcessSomethings>(
x =>
{
this.log.Debug("Received: ProcessSomethings");
this.BecomeStacked(this.Working);
this.RetriveSomethingAndPushForProcessing();
});
}
/// <summary>Supervisors strategy.</summary>
/// <returns>Supervisors strategy for that actor</returns>
protected override SupervisorStrategy SupervisorStrategy()
{
return new AllForOneStrategy(10, 3000, Decider.From(x => Directive.Stop));
}
/// <summary>
/// The merge data.
/// </summary>
private void RetriveSomethingAndPushForProcessing()
{
this.log.Debug($"Processing Somethings...");
var SomethingActor1 = this.helper.GetActorPath(ActorsEnum.SomethingsDataActor);
var SomethingActor2 = this.helper.GetActorPath(ActorsEnum.SomethingCollectorActor);
var something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
while (Something.SomethingId>0)
{
this.log.Debug($"Sending data to SomethingCollector with Something id: {Something.SomethingId}");
SomethingActor2.Tell(new SomethingCollectorActor.ProcessSomethingDto(Something));
Something = (SomethingDto)SomethingActor1.Ask(new SomethingsDataActor.GetOneSomethingAndRemoveFromList()).Result;
}
this.log.Debug("Sending data to SomethingCollector -- ALL SENT");
this.UnbecomeStacked();
}
The mock objects just send probe actor as per every request
public ActorSelection GetActorPath(ActorsEnum actorsEnum)
{
Debug.WriteLine("GetDataActorPath for:" + actorsEnum);
Debug.WriteLine("GetDataActorPath =>" + this.probeRef.Path);
return this.Sys.ActorSelection(this.probeRef.Path);
}
public void SetProbe(IActorRef actorRef)
{
Debug.WriteLine("Setting probe reference: " + actorRef.Path);
this.probeRef = actorRef;
}
ignition overview
Ok so a few things.
First of all: You are expecting on a message of the type: SomethingsDataActor.GetOneSomethingAndRemoveFromList.
But it doesn't look like you are actually passing this message to an actorref that is represented by the testprobe. But its hard to be sure since you only pasted halve the code.
Second:
Using ask inside an actor is considered an anti pattern, and can easily be avoided by employing a more conversationalist style of communication.
Using actor.ask().Result is even worse because it can lead to deadlocks if your not carefull. (what happens when the database actor crashes because your network is down? potentially no response will ever be send back and the default Ask timeout is infinite)
Ask should only really be used to communicate with an actor from outside the actor system.
The problem was with mocking class that was inhering TestClass,
decided for that to have "Actor System Reference"
return this.Sys.ActorSelection(this.probeRef.Ref.Path);
but should be:
return this.probeRef.ActorSelection(this.probeRef.Ref.Path);
This inheritance was creating a second independednt actor system.....
Thanks #Dantar for help!

Possible to do custom method processing with ResteasyClient (Proxy Framework)?

Is it possible to register a DynamicFeature with an ResteasyClient (Proxy Framework) similar to what can be done on server side?
So something similar to this:
final ResteasyClient client = new ResteasyClientBuilder().build();
client.register(new MyDynamicFeature());
Where MyDynamicFeature implements DynamicFeature
I'm trying to figure out how to have a ClientResponseFilter check the http return status depending on the annotation that is present on the resource method, and the DynamicFeature appeared to be the most promising lead to get access to the ResourceInfo.
So essentially, I want to do something like this:
#POST
#Path("some/path/user")
#ExpectedHttpStatus(201) // <- this would have to be passed on somehow as expectedStatus
User createUser(User request);
And then in the ClientResponseFilter (or any other solution) something like this:
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
if (responseContext.getStatus() != expectedStatus) {
// explode
}
}
Cause in the ClientResponseFilter, I don't see any way to know what the resource method is that defined the REST call that the filter is currently analyzing.
And the problem is that the framework right now only checks whether the response status is success, it doesn't check whether it's 200 or 201 and we'd like to refine that.
Here are some articles that seems to explain something very similar, yet this doesn't seem to be working with the ClientResponseFilter / ResteasyClient:
Match Filter with specific Method through NameBinding on RESTeasy
What is the proper replacement of the Resteasy 3.X PreProcessInterceptor?
First of all, I can't take credit for the solution really, but I'm going to paste the answer here.
Also, you could ask why the heck we're doing this? Because we need / want to test that the service returns the right http status, but unfortunately the service we are testing does not always return the same http status for the same http method.
E.g. in the example below, the post returns HttpStatus.OK, and another post method of the same service could return HttpStatus.CREATED.
Here's the solution we ended up with, a combination of ClientResponseFilter:
import java.io.IOException;
import java.util.UUID;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
/**
* {#link ClientResponseFilter} which will handle setting the HTTP StatusCode property for use with
* {#link HttpStatusResponseInterceptor}
*/
public class HttpStatusResponseFilter implements ClientResponseFilter {
public static final String STATUS_CODE = "StatusCode-" + UUID.randomUUID();
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
requestContext.setProperty(STATUS_CODE, responseContext.getStatusInfo());
}
}
And ReaderInterceptor:
import java.io.IOException;
import java.lang.annotation.Annotation;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
/**
* {#link ReaderInterceptor} which will verify the success HTTP status code returned from the server against the
* expected successful HTTP status code {#link SuccessStatus}
*
* #see HttpStatusResponseFilter
*/
public class HttpStatusResponseInterceptor implements ReaderInterceptor {
#Override
public Object aroundReadFrom(ReaderInterceptorContext interceptorContext) throws ServerErrorException, IOException {
Status actualStatus = (Status) interceptorContext.getProperty(HttpStatusResponseFilter.STATUS_CODE);
if (actualStatus == null) {
throw new IllegalStateException("Property " + HttpStatusResponseFilter.STATUS_CODE + " does not exist!");
}
Status expectedStatus = null;
for (Annotation annotation : interceptorContext.getAnnotations()) {
if (annotation.annotationType() == SuccessStatus.class) {
expectedStatus = ((SuccessStatus) annotation).value();
break;
}
}
if (expectedStatus != null && expectedStatus != actualStatus) {
throw new ServerErrorException(String.format("Invalid status code returned. Expected %d, but got %d.",
expectedStatus.getStatusCode(), actualStatus.getStatusCode()), actualStatus);
}
return interceptorContext.proceed();
}
}
We register both those when we create the client:
final ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
client.register(new HttpStatusResponseFilter());
client.register(new HttpStatusResponseInterceptor());
And the SuccessStatus is an annotation that we use to annotate the methods that we want to specifically check, e.g. like that:
#POST
#Path("some/foobar")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#SuccessStatus(Status.OK)
Foobar createFoobar(Foobar foobar);
It's not possible to register a DynamicFeature in your client.
See the DynamicFeature documentation:
A JAX-RS meta-provider for dynamic registration of post-matching
providers during a JAX-RS application setup at deployment time.
Dynamic feature is used by JAX-RS runtime to register providers that
shall be applied to a particular resource class and method and
overrides any annotation-based binding definitions defined on any
registered resource filter or interceptor instance.
Providers implementing this interface MAY be annotated with #Provider
annotation in order to be discovered by JAX-RS runtime when scanning
for resources and providers. This provider types is supported only as
part of the Server API.
The JAX-RS Client API can be utilized to consume any Web service exposed on top of a HTTP protocol, and is not restricted to services implemented using JAX-RS.
Please note the JAX-RS Client API does not invoke the resource classes directly. Instead, it generates HTTP requests to the server. Consequently, you won't be able to read the annotations from your resource classes.
Update 1
I'm not sure if this will be useful for you, but since you would like to access the server resource classes from your client, it would be interesting to mention that Jersey provides a proxy-based client API (org.glassfish.jersey.client.proxy package).
The basic idea is you can attach the standard JAX-RS annotations to an interface, and then implement that interface by a resource class on the server side while reusing the same interface on the client side by dynamically generating an implementation of that using java.lang.reflect.Proxy calling the right low-level client API methods.
This example was extracted from Jersey documentation:
Consider a server which exposes a resource at http://localhost:8080. The resource can be described by the following interface:
#Path("myresource")
public interface MyResourceIfc {
#GET
#Produces("text/plain")
String get();
#POST
#Consumes("application/xml")
#Produces("application/xml")
MyBean postEcho(MyBean bean);
#GET
#Path("{id}")
#Produces("text/plain")
String getById(#PathParam("id") String id);
}
You can use WebResourceFactory class defined in this package to access the server-side resource using this interface. Here is an example:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/");
MyResourceIfc resource = WebResourceFactory.newResource(MyResourceIfc.class, target);
String responseFromGet = resource.get();
MyBean responseFromPost = resource.postEcho(myBeanInstance);
String responseFromGetById = resource.getById("abc");
I'm not sure if RESTEasy provides something similar to it.
Update 2
RESTEasy also provides a proxy framework. See the documentation:
RESTEasy has a client proxy framework that allows you to use JAX-RS annotations to invoke on a remote HTTP resource. The way it works is that you write a Java interface and use JAX-RS annotations on methods and the interface. For example:
public interface SimpleClient {
#GET
#Path("basic")
#Produces("text/plain")
String getBasic();
#PUT
#Path("basic")
#Consumes("text/plain")
void putBasic(String body);
#GET
#Path("queryParam")
#Produces("text/plain")
String getQueryParam(#QueryParam("param") String param);
#GET
#Path("matrixParam")
#Produces("text/plain")
String getMatrixParam(#MatrixParam("param") String param);
#GET
#Path("uriParam/{param}")
#Produces("text/plain")
int getUriParam(#PathParam("param") int param);
}
RESTEasy has a simple API based on Apache HttpClient. You generate a proxy then you can invoke methods on the proxy. The invoked method gets translated to an HTTP request based on how you annotated the method and posted to the server. Here's how you would set this up:
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget) target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
simple.putBasic("hello world");
Alternatively you can use the RESTEasy client extension interfaces directly:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/base/uri");
SimpleClient simple = target.proxy(SimpleClient.class);
simple.putBasic("hello world");
[...]
The framework also supports the JAX-RS locator pattern, but on the client side. So, if you have a method annotated only with #Path, that proxy method will return a new proxy of the interface returned by that method.
[...]
It is generally possible to share an interface between the client and server. In this scenario, you just have your JAX-RS services implement an annotated interface and then reuse that same interface to create client proxies to invoke on the client-side.
Update 3
Since you are already using RESTEasy Proxy Framework and assuming your server resources implement the same interfaces you are using to create your client proxies, the following solution should work.
A ProxyFactory from Spring AOP, which is already packed with RESTEasy Client will do trick. This solution, basically, creates a proxy of the proxy to intercept the method that is being invoked.
The following class stores the Method instance:
public class MethodWrapper {
private Method method;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
And the following code makes the magic:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/api");
ExampleResource resource = target.proxy(ExampleResource.class);
MethodWrapper wrapper = new MethodWrapper();
ProxyFactory proxyFactory = new ProxyFactory(resource);
proxyFactory.addAdvice(new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
wrapper.setMethod(invocation.getMethod());
return invocation.proceed();
}
});
ExampleResource resourceProxy = (ExampleResource) proxyFactory.getProxy();
Response response = resourceProxy.doSomething("Hello World!");
Method method = wrapper.getMethod();
ExpectedHttpStatus expectedHttpStatus = method.getAnnotation(ExpectedHttpStatus.class);
int status = response.getStatus();
int expectedStatus = annotation.status();
For more information, have a look at the documentation:
MethodInterceptor
ProxyFactory
MethodInvocation

AutoStart/Pre-warm features not working in IIS 7.5 / WCF service

For testing the many headaches of IIS/WCF implementation from scratch, I built the HelloWorld service and client walked through (very nicely) here. I added endpoints for net.tcp, and the service is working properly end-to-end for both bindings under IIS 7.5 (on Windows 7) in its own ApplicationPool called HW.
What I'm trying to get working is the announced AutoStart and Preload (or "pre-warm caching") features. I've followed the instructions laid out here and here (quite similar to one another, but always good to have a second opinion) very closely. Which means I
1) Set the application pool startMode...
<applicationPools>
<!-- ... -->
<add name="HW" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" />
</applicationPools>
2) ...enabled serviceAutoStart and set a pointer to my serviceAutoStartProvider
<site name="HW" id="2">
<application path="/" applicationPool="HW" serviceAutoStartEnabled="true" serviceAutoStartProvider="PreWarmMyCache" />
<!-- ... -->
</site>
3) ...and named said provider, with the GetType().AssemblyQualifiedName of the class listed in its entirety below
<serviceAutoStartProviders>
<add name="PreWarmMyCache" type="MyWCFServices.Preloader, HelloWorldServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</serviceAutoStartProviders>
using System;
namespace MyWCFServices
{
public class Preloader : System.Web.Hosting.IProcessHostPreloadClient
{
public void Preload(string[] parameters)
{
System.IO.StreamWriter sw = new System.IO.StreamWriter(#"C:\temp\PreloadTest.txt");
sw.WriteLine("Preload executed {0:G}", DateTime.Now);
sw.Close();
}
}
}
Alas, all this manual configuration, plus a couple iisreset calls, and I get nothing. No w3wp.exe process firing up in Task Manager (though I get it if I launch the HelloWorldClient), no text file, and above all, no satisfaction.
There is a frustratingly scant amount of discussion about this feature, either on SO or the wider web, and the few similar questions here got little attention, all of which rings an alarm bell or two. Perhaps needlessly though--any experts out there who have been down this very road a time or two care to chime in? (Happy to offer up the entire solution if you can suggest a good place to host it.)
EDIT: I tried resetting that path in the Preload method to the relative App_Data folder (another SO answer suggested that), didn't matter. Also, I learned the w3wp.exe process fires on a simple browse to the localhost. The process consumes an impressive 17MB of memory to serve up its single tiny OperationContract, while for the price offering zero Preload value. 17MB of ColdDeadCache.
This is a slightly different approach for your problem:
Use Windows Server AppFabric for service auto-start
Use WCF infrastructure to execute custom startup code
Re 1: The Appfabric AutoStart feature should just work out of the box (provided you're not using MVC's ServiceRoute to register your services, they MUST be specified either in the Web.config's serviceActivations section or using physical *.svc files.
Re 2: To inject custom startup code into the WCF pipeline you could use an attribute like this:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace WCF.Extensions
{
/// <summary>
/// Allows to specify a static activation method to be called one the ServiceHost for this service has been opened.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class ServiceActivatorAttribute : Attribute, IServiceBehavior
{
/// <summary>
/// Initializes a new instance of the ServiceActivatorAttribute class.
/// </summary>
public ServiceActivatorAttribute(Type activatorType, string methodToCall)
{
if (activatorType == null) throw new ArgumentNullException("activatorType");
if (String.IsNullOrEmpty(methodToCall)) throw new ArgumentNullException("methodToCall");
ActivatorType = activatorType;
MethodToCall = methodToCall;
}
/// <summary>
/// The class containing the activation method.
/// </summary>
public Type ActivatorType { get; private set; }
/// <summary>
/// The name of the activation method. Must be 'public static void' and with no parameters.
/// </summary>
public string MethodToCall { get; private set; }
private System.Reflection.MethodInfo activationMethod;
#region IServiceBehavior
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
serviceHostBase.Opened += (sender, e) =>
{
this.activationMethod.Invoke(null, null);
};
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
// Validation: can get method
var method = ActivatorType.GetMethod(name: MethodToCall,
bindingAttr: System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,
callConvention: System.Reflection.CallingConventions.Standard,
types: Type.EmptyTypes,
binder: null,
modifiers: null);
if (method == null)
throw new ServiceActivationException("The specified activation method does not exist or does not have a valid signature (must be public static).");
this.activationMethod = method;
}
#endregion
}
}
..which can be used like this:
public static class ServiceActivation
{
public static void OnServiceActivated()
{
// Your startup code here
}
}
[ServiceActivator(typeof(ServiceActivation), "OnServiceActivated")]
public class YourService : IYourServiceContract
{
}
That's the exact approach we've been using for quite a while and on a large number of services. The extra benefit of using a WCF ServiceBehavior for custom startup code (as opposed to relying on the IIS infrastructure) is that it works in any hosting environment (incl. self-hosted) and can be more easily tested.
I know this sounds absurd but I faced the same issue (w3wp.exe not firing automatically after making the config changes) and it was because I hadn't run the text editor in Admin mode when I was editing the applicationHost.config file. Stupid mistake on my part.
In my defense I was using Notepad++ which told me it was saving when it actually wasn't.
I've done the same. it works...
In preload method I have some code copied from a nice white paper available here!
Preload method looks like...
public void Preload(string[] parameters)
{
bool isServceActivated = false;
int attempts = 0;
while (!isServceActivated && (attempts <10))
{
Thread.Sleep(1 * 1000);
try
{
string virtualPath = "/Test1/Service1.svc";
ServiceHostingEnvironment.EnsureServiceAvailable(virtualPath);
isServceActivated = true;
}
catch (Exception exception)
{
attempts++;
//continue on these exceptions, otherwise fail fast
if (exception is EndpointNotFoundException ||
exception is ServiceActivationException ||
exception is ArgumentException)
{
//log
}
else
{
throw;
}
}
}
}
Maybe you are on a 64-bit system? There is a known "feature" in Windows where the save gets redirected to the 32 bit folder and thus no changes will be picked up
(I have converted my comment to an answer as answers might be easier to find)

Sharepoint 2010 Event receiver not firing for subsite

I have an event receiver (WebAdding and WebProvisioned) which works just fine for sites created off the root of the site collection. However, subsites (for example, teamsites created within other areas) do not trigger the code at all.
Does anyone have any idea as to why?
using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using System.Text;
namespace TestEventReceiver.EventReceiver1
{
/// <summary>
/// Web Events
/// </summary>
public class EventReceiver1 : SPWebEventReceiver
{
/// <summary>
/// A site is being provisioned.
/// </summary>
public override void WebAdding(SPWebEventProperties properties)
{
base.WebAdding(properties);
using (SPWeb web = properties.Web)
{
StringBuilder output = new StringBuilder();
output.AppendFormat("Web Adding");
output.AppendFormat("<br>Web title: {0}",web.Title);
SendMyEmail(web, "SendItToMe#MyTestAddress.com", "Web Adding", output.ToString());
}
}
/// <summary>
/// A site was provisioned.
/// </summary>
public override void WebProvisioned(SPWebEventProperties properties)
{
base.WebProvisioned(properties);
using (SPWeb web = properties.Web)
{
StringBuilder output = new StringBuilder();
output.AppendFormat("Web Provisioned");
output.AppendFormat("<br>Web title: {0}", web.Title);
SendMyEmail(web, "SendItToMe#MyTestAddress.com", "Web Provisioned", output.ToString());
}
}
private void SendMyEmail(SPWeb Web, String toAddress, String subject, String message)
{
bool appendHtmlTag = false;
bool htmlEncode = true;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
SPUtility.SendEmail(Web, appendHtmlTag, htmlEncode, toAddress, subject, message);
});
}
}
}
Thanks in advance,
Matt
I think you should not be using 'Using' .
The SPWeb object reference you get is from properties.Web which is being passed to the WebAdding method. You will run into issues because of this.
Have a look at how your event receiver is provisioned - it may be the scope needs to be changed to Site rather than Web. Perhaps you could post here so we can see.
On my site I had the same issue. Still figuring out the xml files, but in my Elements.xml file for the Receivers, each receiver had the same sequence number. Once I made them unique within the Elements.xml file, the WebProvisioned event started firing. Don't know if this is the same issue you were having.
This code is showing the WebAdding event and that event is occurring on the parent Web.
http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spwebeventreceiver.webadding.aspx
Try to change scope of your receiver (in Elements.xml file add attribute ). Also, make sure that the feature of your Event receiver is activated in you site features in the subsite.

Can we host a Workflow Service as a Windows Service?

I am working on a logging application that requires me to have a Workflow that is exposed as a Service (Workflow Service). We want to host it as a Windows Service (don't want to host workflow service as .svc file in IIS). Another reason for having it as windows service is to be able to communicate with the service through the Named pipes.
Can we expose a Workflow Service through Named Pipes without hosting it in IIS?
Yep bep, you sure can. At least, I have accomplished as much with Workflow 4 Release Candidate.
Consider,
// a generic self-hosted workflow service hosting thingy. Actual
// implementation should contain more logging and thread safety, this
// is an abbreviated version ;)
public class WorkflowHost
{
// NOTE: with Workflow, it helps to maintain a concept of
// Workflow definition [the Activity or WorkflowService from
// a designer] and a Workflow instance [what is running within
// WorkflowInvoker, WorkflowApplication, WorkflowServiceHost].
// a definition may be used to generate an instance. an instance
// contains run-time state and cannot be recycled into a new
// instance. therefore, to repeatedly re-host a WorkflowService
// we need to maintain references to original definitions and
// actual instances. ergo services and hosts maps
//
// if you are special purpose and require support for one and
// only one service and endpoint\uri, then you may reduce this
// to a simple tuple of Uri, WorkflowService, WorkflowServiceHost
// services represents a definition of hosted services
private readonly Dictionary<Uri, WorkflowService> _services =
new Dictionary<Uri, WorkflowService> ();
// hosts represents actual running instances of services
private readonly Dictionary<Uri, WorkflowServiceHost> _hosts =
new Dictionary<Uri, WorkflowServiceHost> ();
// constructor accepts a map of Uris (ie service endpoints) to
// workflow service definitions
public WorkflowHost (IDictionary<Uri, WorkflowService> services)
{
foreach (KeyValuePair<Uri, WorkflowService> servicePair in services)
{
_services.Add (servicePair.Key, servicePair.Value);
}
}
// have your windows service invoke this to start hosting
public void Start ()
{
if (_hosts.Count > 0)
{
Stop ();
}
foreach (KeyValuePair<Uri, WorkflowService> servicePair in _services)
{
WorkflowService service = servicePair.Value;
Uri uri = servicePair.Key;
WorkflowServiceHost host = new WorkflowServiceHost (service, uri);
host.Open ();
_hosts.Add (uri, host);
}
}
// have your windows service invoke this to stop hosting
public void Stop ()
{
if (_hosts.Count > 0)
{
foreach (KeyValuePair<Uri, WorkflowService> servicePair in
_services)
{
WorkflowService service = servicePair.Value;
Uri uri = servicePair.Key;
IDisposable host = _hosts[uri];
host.Dispose ();
}
_hosts.Clear ();
}
}
}
I believe endpoint configuration may be set via standard Wcf service configuration sections in App.config. I have not personally attempted a change to default transport layer in my experiments with Workflow.
The above represents a generic pure hosting class [ie it self-hosts WorkflowServices]. This allows us to re-use this hosting functionality within a console, WinForm, WPF, or yes, even a WindowsService application. Below is a WindowsService that leverages our host class
// windows service. personally i would abstract service behind
// an interface and inject it, but again, for brevity ;)
public partial class WorkflowWindowsService : ServiceBase
{
WorkflowHost _host;
public WorkflowWindowsService ()
{
InitializeComponent();
Dictionary<Uri, WorkflowService> services =
new Dictionary<Uri, WorkflowService> ();
// do your service loading ...
// create host
_host = new WorkflowHost (services);
}
protected override void OnStart(string[] args)
{
_host.Start ();
}
protected override void OnStop()
{
_host.Stop ();
}
}
If you have fiddled with WorkflowServices in VS2010RC, then you may already know that WorkflowServices are not first class Xaml classes like their Workflow cousins. Instead, they are saved as loose Xaml files with the .xamlx extension. There is no design-time intellisense support for WorkflowServices [as far as I know] and are not recognized as declared types, so our only options to load a WorkflowService at run-time are
Read pure Xaml markup from .xamlx file directly
Read pure Xaml markup from some other source [embedded string, resource, or other source]
Either way, we must interpret markup and create a WorkflowService definition. The following will transform a string [that may be a filename or markup] into a WorkflowService. Keeners may also note that there is a difference between this process and the process for transforming Workflow markup to Workflow definitions.
// converts a string value [either pure xaml or filename] to a
// WorkflowService definition
public WorkflowService ToWorkflowService (string value)
{
WorkflowService service = null;
// 1. assume value is Xaml
string xaml = value;
// 2. if value is file path,
if (File.Exists (value))
{
// 2a. read contents to xaml
xaml = File.ReadAllText (value);
}
// 3. build service
using (StringReader xamlReader = new StringReader (xaml))
{
object untypedService = null;
// NOTE: XamlServices, NOT ActivityXamlServices
untypedService = XamlServices.Load (xamlReader);
if (untypedService is WorkflowService)
{
service = (WorkflowService)(untypedService);
}
else
{
throw new ArgumentException (
string.Format (
"Unexpected error reading WorkflowService from " +
"value [{0}] and Xaml [{1}]. Xaml does not define a " +
"WorkflowService, but an instance of [{2}].",
value,
xaml,
untypedService.GetType ()));
}
}
return service;
}
Yes it is possible. You will have to create your own service. See Hosting and Consuming WCF Services on MSDN, especially the section Hosting in Windows Services.