Showing posts with label WCF service. Show all posts
Showing posts with label WCF service. Show all posts

Wednesday, May 4, 2011

WCF service in IIS with multiple host-headers

By Alejandro Villarreal

Hosting WCF services in IIS is usually a very straightforward process with no weird configuration settings or obscure errors. You create a site in IIS, publish your service in the site’s folder, and you’re ready to go.

However, things start to get a bit complicated in more advanced scenarios like the one I encountered some days ago: if your site in IIS answers to multiple host-headers, then when you try to access the service through a web browser, instead of seeing the usual page with the service description you will receive a screen indicating a Runtime Error, and you should be able to find an Error entry in Window’s Event Viewer (under “Windows Logs”/Application) whose Source is System.ServiceModel 3.0.0.0 (or whatever version you happen to be using), that says something like this:

Exception: System.ServiceModel.ServiceActivationException: The service '/YourService.svc' cannot be activated due to an exception during compilation. The exception message is: This collection already contains an address with scheme http. There can be at most one address per scheme in this collection.

This message was certainly more obscure than the errors I was used to when dealing with IIS-hosted WCF Services, and it didn’t make much sense until I found the solution and understood what the error was talking about. In summary, when the WCF Service is hosted in an IIS site, IIS passes all the host-headers to which the site answers as base addresses for the service when it instantiates it, and it in turn complains because it only expects 1 base address of a particular type. Several host-headers imply several base addresses for http, and thus the error.

A code-based solution is to create a custom class than inherits from ServiceHostFactory and filters out the unwanted addresses, and then tell IIS to use that class to instantiate your service. The class is nothing special:

1

Instead of returning the first address that was passed in (baseAddresses[0]) –which could impact your service if it depends heavily on this address, and the order of the host-headers changes in IIS– you can implement your own custom logic to determine which address the service is going to use.

Then you must tell IIS to actually use this class, which is achieved by modifying the .svc file that was generated when publishing your service, so it looks like this (notice the second line):

<%@ ServiceHost Service="Your.Namespace.YourService"

Factory="Your.Namespace.CustomHostFactory" %>

“CustomHostFactory” is just the name of the class you created, so it will depend on the actual name you give to it.

And that’s it! If you try to access your service through the web browser again, you should get the .NET generated help page. According to the site linked earlier, there is also a way to make this happen purely by editing the configuration file, but I haven’t tested it. However, feel free to go ahead and try it!

Have a nice day!

How to Handle the WCF Error on Silverlight

By David Espino.

When sending a call to a WCF service from a Silverlight application and the service returns an error, the Silverlight client backs up only a very generic error:

The remote server returns an error: NotFound

This message does not help to know the exact error neither for the user nor the developer. Even more, it is necessary for more security to make a division of the error information: one message for the user and one detailed message for the system administrator or developer.

The first part of the error dedicated to the application administrator consists on writing the exception that the code generates on a detailed way. There are a lot of tools that allows tomake this log of errors (Log for Net, Microsoft Enterprise Library and some other). For this example, we need to provide an easy log in to the server application log where our service would be found. This method captures two parameters: One “Entity” that indicates the business entity where the error occurred (for example the Countru, State, Employee, etc) and an action, which indicates the operation that could not be performed (Save, Select, Delete, etc). With these two parameters we can deliver a friendly message to be sent to the user.

public class ExceptionLogger

{

public static string LogAndReturnError(Exception ex, string entidad, string accion)

{

StringBuilder cadenaError = new StringBuilder();

cadenaError.Append("Ocurrió un error en el servicio de la aplicación: \n\n");

cadenaError.Append("Error: ");

cadenaError.Append(ex.Message);

cadenaError.Append("\n\nStackTrace: \n\n");

cadenaError.Append(ex.StackTrace);

EventLog.WriteEntry("NombreAplicación", cadenaError.ToString(), EventLogEntryType.Error);

cadenaError = new StringBuilder();

cadenaError.Append(string.Format("Ocurrió un error al intentar {0} {1}. Contacte al administrador del sistema.", accion, entidad));

return cadenaError.ToString();

}

}

The next step would be to generate an entity as a DataContract for the object that stores errors that occur during the service.

[DataContract]

public class ServiceError

{

[DataMember]

public string Type { get; set; }

[DataMember]

public string Message { get; set; }

}

The next thing to do is to make our service operations to return us an error object into the interface layer. We will use an output parameter in our methods of service and use the kind of login error.

public void DoWork(string Parameter1, out ServiceError errorService)

{

errorService = null;

try

{

//Operaciones a realizar

}

catch (Exception ex)

{

errorService = new ServiceError

{

Message = ExceptionLogger.LogAndReturnError(ex, "Acción", "Entidad"),

Type = "Aplicación"

};

}

Finally we need a Silverlight application to consume our service. For this, we need to add a Silverlight application and then a reference of our service. Once we call our service we are going to get the following:

MyServiceClient client = new MyServiceClient ();

client.DoWorkCompleted += new EventHandler<DoWorkCompletedEventArgs>(client_DoWorkCompleted);

client.DoWorkAsync("Hola Mundo");

void client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)

{

//Error propio de WCF

if (e.Error == null)

{

//Nuestro Parámetro de Error!!!

if (e.errorService == null)

{

if (e.Result != null)

{

//Manipular resultado

}

}

}

}

This is a very easy way to implement error management on Silverlight.

Deploying a Message Queue WCF Service

By David E.

The following guide was the created installing MSQueue and configuring a WCF using a Windows 7 OS.

1. Verify IIS is installed on the server machine where the WCF is going to be hosted.

2. Install MS Queue by following the instructions specified on this link:

http://msdn.microsoft.com/en-us/library/aa967729.aspx

3. Install or verify the following Windows Features :

1a

4. Verify the following services are running:

a. Message Queuing

b. Net.Msmq Listener Adapter

c. Windows Process Activation Service

5. Create a Web application inside IIS. For this example, the application name used is FGMQService. The app name is important because is going to be used to name the queue.

6. Copy the Service File and dll to the folder pointed by the recently created web application.

2a

7. Create a Private Message Queue by accessing Computer Management with the following name:

3a

Note: The Queue Name must match the name of WebApp/ServiceName. In this example, the app name is FGMQService and the name of the SVC file contained on Web App folder.

8. You may want to enable Journal for the queue, to identify a history of the queue execution.

4a

9. Allow permissions to the queue. The user account running the client and the one running the service and should have access to send and receive the queue respectively. Assign security to allow this.

5a

10. Use the client to test the queue and check if the Service is being called using the queue. If no execution of the service is detected. Check for the Dead-letter messages on System Queues to determine any execution errors or time outs.