Reliable Messaging accessible after adding ASMX web service via Service Reference - wcf

In VS2013 I have added Service Reference, not Web Reference, to existing legacy ASMX web service(s).
Does this somehow give this reference the ability to implement WCF features like Reliable Messaging, or any other new features of WCF?
I have done some reading and I think that at least for Reliable Messaging we do not magically get this ability, since as I understand it this feature has to be both enabled on the 'client' and 'server' side, ie both endpoints, and of course the old ASMX web services, the server in this case, don't support Reliable Messaging standard, even if I were to somehow configure the 'client' to do so.
I have been told by resident developers to add these old ASMX web services as a Service Reference as it gives the ability to do async calls and 'some' other configuration benefits, but I am not too sure about this from what I have now read.

What you need is an adapter pattern. The way I am looking at it is more from design patterns perspective rather than WCF.
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello(string toWhom);
}
public class Service : IService
{
public string SayHello(string toWhom)
{
// consume the ASMX service here and return the result.
}
}
in the binding you can use the features that are missing in ASMX.
<bindings>
<wsHttpBinding>
<binding>
<reliableSession/>
<security>
<message />
</security>
</binding>
</wsHttpBinding>
</bindings>

Related

Changing namespace and schemaLocation attributes in WCF

I developed a WCF service in C#. Our customer already has a client software written in Java. They say when they try to add our wcf service reference, they get an error. They think that the problem about namespaces.
I don't know much about namespaces or any other tag details in WCF.
They say wcf service's wsdl output has to be like the following:
<xsd:import id="base" namespace="http://helios.theircompanyName.com/im schemaLocation="http://wwwdev1.theirCompanyName.com:8000/HeliosIM/im?xsd=1"/>
But our service gives:
<xsd:import schemaLocation="http://myComputerName/MyWcfProjectFolder/MyWcfService.svc?xsd=xsd0" namespace="http://tempuri.org/"/>
As it can be seen, my service has no attribute like id="base" and namespace, schemaLocation attributes are different.
How can I change WCF to generate wsdl xml like they want?
If you want to change the namespace of your service from tempuri.org (which is the WCF default) you need to change it in 4 places:
Service contract
Data contracts
Service implementation
BindingNamespace in endpoint config element
For example:
// Service Contract
[ServiceContract(Namespace="http://myNamespace")]
public interface IMyService
{}
// Data Contract
[DataContract(Namespace="http://myNamespace")]
public class MyType
{}
// Service implementation
[ServiceBehavior(Namespace="http://myNamespace")]
public class Service : IMyService
{}
<!-- In config -->
<endpoint address="http://whatever"
bindingNamespace="http://myNamespace"
binding="basicHttpBinding"
contract="Something.IMyService" />
HOWEVER, I don't really understand why they are telling you this is necessary. As the provider of the service, it's up to you rather than them what namespace you provide. Whatever this value is set to they will likely have the same problems consuming the wsdl.
The same goes with the schemaLocation, again, it's not up to them where this location points to. Schemalocation is actually completely optiona when you're doing an import in xml schema, so if they're dependent on some value being in there then they're not xsd compliant.
I would guess they're having difficulties consuming your WSDL and do not quite understand what is wrong, so have chosen to blame your service. The service metadata exposed over the basicHttpBinding is the most interoperable of the entire WCF stack, and should be 100% consumable from java.
How are they trying to build their client? Is your service running somewhere they can see it?

What is the best way to dynamically host multiple WCF services in a single Windows service sharing a mex endpoint?

I have been given a request to dynamically host multiple WCF services in a single Windows service.
The requirements are the following:
The services are singletone instances implementing some service contract interfaces.
The services aren't known at compile-time - at runtime a collecion of unknown singletone services are passed to the application.
All services are exposed via the same mex endpoint
The endpoints are set programmatically (without using app.config)
I tried solving the problem from two different approaches:
The first approach is to create and open a ServiceHost for each service instance. The problem with this approach is that each ServiceHost is exposed via its own mex endpoint.
The second approach is to create a single ServiceHost for all services, and expose them all via the same mex endpoint.
I tried a couple of ways to implement the second approach:
The first way is to create a service type in runtime (using CodeDom or Reflection.Emit) that wraps all instances, and implements all of the service contracts and routes a given method call to the suitable service instance. This works but seems like a overkill. (I rather to not generate code if possible)
The second way is to programmatically setup ServiceEndpoints for requested contracts. I modified this following code example so it will route a method call to the corresponding service instance's method. The problem with this solution is that a hack is made in order to associate a ServiceEndpoint to its ChannelDispatcher.
Am I missing other approaches? Is there anyway to overcome the problems I mentioned?
If I'm understanding your problem correctly, then one approach would be to implement multiple contract interfaces in a single service class. By doing that, you should be able to get the metadata from a single mex endpoint. Here's a very simple example:
[ServiceContract]
public interface IServiceContract1
{
[OperationContract]
bool DoStuff1();
}
[ServiceContract]
public interface IServiceContract2
{
[OperationContract]
bool DoStuff2();
}
public class ServiceContractImplementation: IServiceContract1, IServiceContract2
{
bool DoStuff1()
{
return true;
}
bool DoStuff2()
{
return true;
}
}
Then in the web.config (or in code in your case):
<service name="ServiceImplementation">
<endpoint binding="basicHttpBinding" contract="IServiceContract1"/>
<endpoint binding="basicHttpBinding" contract="IServiceContract2"/>
<endpoint address="mex" contract="IMetadataExchange" />
</service>

Exposing a WCF Service as a COM+ Component

I am working with a client that has multiple Classic ASP websites, but would like to use AppFabric Caching within these sites to help lighten the load they currently have on their database.
The first approach we used was creating a .NET wrapper for the AppFabric API and exposing it to the ASP websites as a COM object. This method worked, but they soon began to experience high memory usage that crashed their webserver. The COM component is hosted within the application scope of the site so that could be a big part of the issue.
One of the options I came up with was creating a WCF Service and exposing it to the ASP sites as a COM+ Service. Unfortunately, my exposure to COM+ at this point is limited. My reasoning behind this is the service can be utilized from the ASP websites, but hosted out of process from the websites. This would also allow me to performance test the COM+ service independently from the websites.
I am having trouble coming up with start to finish documentation for creating and publishing the COM+ service. The MSDN documentation I’ve read appears to skip significant steps in the process.
As an example service I have the following:
namespace TestComService
{
[ServiceContract(SessionMode = SessionMode.Allowed, Namespace = "http://tempure.org/DD1F6C46-1A25-49CC-AA20-2D31A3D0C0AA", Name = "IService")]
public interface IServiceContract
{
[OperationContract]
string Get(string key);
[OperationContract]
void Set(string key, string value);
}
public class Service : IServiceContract
{
private readonly Dictionary<string, string> cache = new Dictionary<string, string>();
public string Get(string key)
{
return cache[key];
}
public void Set(string key, string value)
{
cache.Add(key, value);
}
}
}
The configuration is as follows:
<system.serviceModel>
<bindings>
<netNamedPipeBinding>
<binding name="comNonTransactionalBinding"/>
</netNamedPipeBinding>
</bindings>
<comContracts>
<comContract contract="{DD1F6C46-1A25-49CC-AA20-2D31A3D0C0AA}" name="IService" namespace="http://tempure.org/DD1F6C46-1A25-49CC-AA20-2D31A3D0C0AA" requiresSession="true">
<exposedMethods>
<add exposedMethod="Get"/>
<add exposedMethod="Set"/>
</exposedMethods>
</comContract>
</comContracts>
<services>
<service name="{3957AA9E-4671-4EF0-859B-1E94F9B21BEE},{5D180F85-65D8-4C0C-B5D6-9D28C59E29AE}">
<endpoint address="IService" binding="netNamedPipeBinding" bindingConfiguration="comNonTransactionalBinding" contract="{DD1F6C46-1A25-49CC-AA20-2D31A3D0C0AA}"/>
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/TestComService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
I’m still a bit confused as far as hosting goes. Can this be hosted within IIS, or to I need to create a separate service to host within that?
Once again, I'm open to any suggestions or input someone with more experience with the matter can provide.
We use COM+ called directly from a web service and WCF Service. I do not believe you need to use a WCF wrapper, called from your ASP.NET application, to use the COM+ component unless it will run on a different server.
You need to install the COM+ component separately from the Web App on the server. It must be registered in the GAC. You create a reference to the COM+ object/DLL file in your ASP.NET application. When you call the DLL file it uses the deployed object registered in the GAC which calls the registered COM+ object.
We had some problems with the native .NET installer. It does not always register the COM+ component correctly. So I wrote a simple batch file that does the job. Here is batch code. I deploy regsvsc.exe with the application so it always knows where to find it.
regsvcs /fc MYCOM+.DLL

WCF set bindings on service at runtime

My app has to be installed on my client's webservers. Some clients want to use SSL and some do not. My app has a WCF service and I currently have to go into the web.config for each install and switch the security mode from <security mode="Transport"> to <security mode="TransportCredentialOnly"> depending on the client's SSL situation. I am able to set the client bindings at runtime. However, I would like to know if there is a way to set the service bindings at runtime(on the server side).
Yes, absolutely! It depends on how you're hosting your WCF services. Saying it has to be installed on the webservers, I would assume you're hosting in IIS.
In that case, you need to create your own descendant of ServiceHostFactory - which really isn't that big a deal.
Your CustomServiceHostFactory is needed to return an instance of your ServiceHost, properly configured to your needs, to IIS.
In the CreateServiceHost method of the custom factory, you basically set up your ServiceHost and configure all its endpoints, behaviors, bindings, etc. - all in code, all under your full control. You can do whatever you need to do here, to configure your service just as needed.
In order to host your service. You'll need to adapt the MyService.svc file to include that CustomServiceHostFactory as the factory to use:
<% # ServiceHost Language="C#" Service="YourService"
Factory="CustomServiceHostFactory" %>
and that's it!
Check out Extending Hosting Using ServiceHostFactory on MSDN for more details, and see the A Custom ServiceHostFactory article on CodeProject for a sample.

Securing WCF service using basicHttpBinding which supports streaming

My question is in regards to the best (aka "least painful") way to secure access to a WCF service that is only exposed to our company's internal users. The goal is to ensure that the service is only accessed via a single Windows forms application that each of our users has installed. When the service is called, I want the service to be able to validate that it was called from the permitted application.
The service to be secured uses basicHttpBinding, which supports streaming, so I believe I am limited to Transport level security.
Below are simplified versions of the <bindings> and <services> sections from my service's config file.
<bindings>
<basicHttpBinding>
<binding name="Service1Binding" transferMode="Streamed"/>
</basicHttpBinding>
</bindings>
<services>
<service name="WCFServiceSecurity.Service1"
behaviorConfiguration="WCFServiceSecurity.Service1Behavior">
<endpoint address=""
binding="basicHttpBinding"
contract="WCFServiceSecurity.IService1"
bindingConfiguration="Service1Binding"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
Can anyone offer some details as to what actions I would need to take in order to implement security on this service?
Note: I'm new to WCF and am not familiar with security at all, so let me know if I haven't provided enough detail.
UPDATE:
As suggested by marc_s, I'd like to secure the WCF service using some sort of username/password mechanism. This gives a little more direction towards an answer, but I'm still somewhat blurry on how to actually do this.
Because my service requires streaming to be enabled, I have to use basicHttpBinding and Transport level security (right?); further to that, the method contained in my service can only accept a Stream object.
Taking those constraints into consideration along with my preference to use username/password validation...
How should I modify my service's config file to force username/password credentials to be supplied?
How will my service validate the supplied credentials?
How will my client application pass credentials the service when making a call?
Will this require using SSL and, if so, will all client machines require a certificate as well?
UPDATE:
After explaining the trouble I've been having with securing this service to my boss, I was given the go-ahead to try the Windows Authentication route. Sadly, I've had no luck in implementing this type of authentication with my Streamed service (argh). After making the appropriate changes (as outlined here - the only exception being that my transferMode="Streamed") and accessing my service, I was presented with the following error:
HTTP request streaming cannot be used in conjunction with HTTP authentication. Either disable request streaming or specify anonymous HTTP authentication.
I then stumbled upon the following quote here which offers some clarification:
You can't do transport auth. with streaming. If you have to use HTTP request streaming, you'll have to run without security.
The way security works is:
WCF Client makes an http request to the Server.
The Server responds with something saying, "You aren't authorized, send me a basic/digest/etc credential."
The Client gets that response and resends its message with the credentials tacked on.
Now the Server gets the message, verifies the credentials, and continues.
Request Streaming isn't designed to work with that security pattern. If it did, it would be really slow, since the Client would send the entire stream, get the message from the Server that it wasn't authorized, then it would have to resend the entire stream with credentials.
So now I'm looking for opinions, how would you secure your streaming-enabled WCF service? As mentioned previously, some sort of username/password mechanism would be preferred. Feel free to think outside the box on this one...
Any help is greatly appreciated!
Well, I found a lot of issues surrounding security/streaming while working on this problem. The hack (er...um...workaround) I finally ended up going with was to create a new DataContract that inherits MemoryStream and decorated it with a BaseStream property (for holding the data I want streamed) along with appropriate properties used for simple authentication.
Here is the resulting DataContract:
[DataContract]
[KnownType( typeof( MemoryStream ) )]
public class StreamWithCredentials : MemoryStream
{
[DataMember]
public Stream BaseStream { get; set; }
[DataMember]
public string Username { get; set; }
[DataMember]
public string Password { get; set; }
}
The above DataContract ends up being the input parameter of my service's method. The first action my service takes is to authenticate the supplied credentials against known valid values and to continue as appropriate.
Now I do know that this is not the most secure option but my directive was to avoid using SSL (which I'm not even sure is possible anyway - as stated here) for this internal process.
That being said, this was the best solution to the above stated problem I could come up with, hope this helps anyone else stricken with this issue.
Thanks to all who responded.
There's a number of things you could do:
add a certificate to each and every machine that's allowed to use your service, and check for that certificate. That only allows you to exclude "unauthorized" machines - you cannot limit it to a specific application
same as above, but include the certificate embedded in your winforms app and send it from there (do not store it in the machine's certificate store)
require a username / password that only that particular app of yours knows about and can transmit to your service; e.g. someone else would not be able to present the appropriate credentials
EDIT 2: OK, so the username/password approach seems to get out of hand.... what if you just have basic transport security (SSL) for basic protection, and then use the MessageContract to define header and body of your SOAP message, include a specific value in the header, and then just check for that presence of the element in the header in your service?
Something like that:
[DataContract]
class YourRequestData
{
...
}
[MessageContract]
public class YourRequest
{
[MessageBodyMember]
public YourRequestData bodyData { get; set; }
[MessageHeader]
public string AppThumbprint { get; set; }
}
And then on your server in your code just check for the presence and the validity of that AppThumbprint code:
public Stream RequestStream(YourRequest request)
{
if(AppThumbprintIsValid(request.AppThumbprint))
{
.... begin your streaming
}
}
That might end up being a lot easier than the username/password security scenario.
Marc
Please correct me if I am wrong, but:
if you are using forms authentication for your WCf service (on asp.net), just add a login method to your service, in it you create the required cookie (formsAuthentication.Authenticate()). which is automatically sent with the response, the client can then call the stream API without needing extra parameters (a requirement for it to be STREAM) and you can check the identity in the streaming api before you fire off the returning stream.
As for securing access to the whole WCF, I get the feeling that embedding a certificate in the .net app is one way to go. they would have to ildump your app to get at it.
you can tell asp.net/wcf not to provide the wsdl, or more accurately, to not automatically generate the wsdl. Without wsdl access it gets much harder for them to generate a proxy....
If you want to use basicHttpBinding (for interop) you can only pass your credential at the message level. You have to set your security configuration to TransportWithMessageCredential.
To do that you have to create a SSL channel, so you need a certificate at server side, and it's not necesary for a cliente to have one.
It is possible to use Windows authentication with Streaming and SSL, but you must use TransportWithMessageCredential:
<basicHttpBinding>
<binding name="FileService.FileServiceBinding" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" transferMode="Streamed">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
You need to set in code proxy.ClientCredentials.UserName.UserName and proxy.ClientCredentials.UserName.Password.
If this is going to be an application that lives on the intranet it might be easiest to just create a new group in your Active Directory and only give members of that group the ability to use the service.
You can add Authentication (using windows credentials) with something like this:
<basicHttpBinding>
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</basicHttpBinding>
Could then Authorise by decorating the Interface to your services methods:
<PrincipalPermission(SecurityAction.Demand, Role:="MyAppsUsers")> _
Public Function MyMethod() As String Implements IService.MyMethod
Heres a good link to Security in WCF. It has lots of How To's at the end (the one titled 'How To - Use basicHttpBinding with Windows Authentication and TransportCreditals' might be of use to you).
Wcf Secruity
[Disclaimer: I'm also new to WCF and haven’t done this exact case before so apologises if this is slightly off!]