Extending WebService Proxy class (imported from WSDL) methods - c#

I've created an WebService proxy class based on a WSDL (in my Visual Studio 2010 .NET solution).
Now what I need is, that the soap header of my request to the remote web service have a specific format, imagine something with two or three fields is not very relevant.
So my solution was, I edited the code generated by Visual Studio and commented out the method where i needed that custom soap header.
Next, because the web service class is marked as partial, I created safe code (that cannot be touched by the generator) in a class with the same name of the generated one (so it's the same class) and declared there the method commented out previously.
I declared it like this:
//this is the generated code file
public partial class Invoices: InvoicesWS.invoices
{
//[System.Web.Services.Protocols.SoapDocumentMethodAttribute( ...
//public RegisterInvoiceResponseType RegisterInvoice(RegisterInvoiceType ...)
//{ ... }
}
//this is the class I created else where in my project
public partial class Invoices: InvoicesWS.invoices
{
public SecureSoapHeader Security { get; set; }
[SoapHeader("Security", Direction = SoapHeaderDirection.In)]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"http://someurl.pt/invoices/RegisterInvoice",
Use = System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare),
TraceExtension()]
public RegisterInvoiceResponseType RegisterInvoice(RegisterInvoiceType RegisterInvoiceElem)
{
object[] results =
this.Invoke("RegisterInvoice", new object[] {RegisterInvoiceElem});
return ((RegisterInvoiceResponseType)(results[0]));
}
}
So, to make my proxy class send a custom header I did this.
But every time I remember to update the web reference, I'll have to manually comment out the method above that is being generated by the Visual Studio tool, to avoid conflicts due to having to methods
with the same signature.
Is there a better way, or best practice to address this situation?
Please do not advise me to do it with WCF, I know the solution for that,
but correctly this is the code that has been working and changing it at
this time is not a possibility.
Thanks.

You can achieve it with SoapExtension. You can create class that implements SoapExtension, and register it in web.config.
Sample of soap extension:
public class SecureSoapExtension : SoapExtension
{
public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return null;
}
public override object GetInitializer(Type serviceType)
{
return null;
}
public override void Initialize(object initializer)
{
}
public override void ProcessMessage(SoapMessage message)
{
// just for out requests
if (message.Stage == SoapMessageStage.BeforeSerialize)
{
// add needed soap header here
message.Headers.Add(new SecureSoapHeader());
}
}
}
And register in web.config to apply to all web services:
<system.web>
<webServices>
<soapExtensionTypes>
<add type="MyTestMvcApplication.SecureSoapExtension, MyTestMvcApplication, Version=1.0.0.0, Culture=neutral"></add>
</soapExtensionTypes>
</webServices>
</system.web>
Important note: If you are calling your Web Service from an external project, let's say, you have a Class Library where you program all your Proxy handling logic. You must add this to your calling project web.config/app.config too, otherwise it will not work:
<system.web>
<webServices>
<soapExtensionTypes>
<add type="MyTestMvcApplication.SecureSoapExtension, MyTestMvcApplication, Version=1.0.0.0, Culture=neutral"></add>
</soapExtensionTypes>
</webServices>
</system.web>
What kind of makes sense, since it's an Web Service extension it's let up to you "final caller" of the proxy, to decide whether to extend or not the web service request.

Related

Calling Entity Framework db context from WCF Service

We have developed an ASP Net MVC application using the Repository pattern.
We are creating a db context instance by using a context provider class like:
public class ContextProvider
public static DBEntities GetContext()
{
return HttpContext.Current.Items["_EntityContext"] as DBEntities;
}
}
Here we are making sure that the DBEntities db call exists only during the existence of the request - we are putting an instance into a Session map - HttpContext.Current.Items["_EntityContext"] in this example.
We are using this in our entire Asp Net Mvc Project as following:
public class TeamRepository
{
#region Members
private DBEntities storeDB = null;
#endregion Members
#region Constructors
public TeamRepository()
{
storeDB = ContextProvider.GetContext();
}
#endregion Constructors
#region Methods
...
Now we need to create a WCF service to enable access to some features to other vendors.
Since all the Repository classes are a part of a project - they were not excluded to a separated DLL I made a reference to the entire project in my new WCF project so that I could use already existing DB Repository method calls.
Here I am facing an issue since I am not able to access to the Session variable HttpContext.Current.Items["_EntityContext"] - method call public static DBEntities GetContext() is always returning null when called from WCF Service.
I tried to make HttpContext.Current Available Within a WCF Service available by placing
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Required)]
on my Service class,
and tweaking the serviceHostingEnvironment section of web.config, which now looks like this:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
<baseAddressPrefixFilters>
<add prefix="http://localhost” />
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
but with no results.
I am using Windows 10.
Do you know a way I can access HttpContext.Current.Items["_EntityContext"]... contained within Asp Net Mvc project from my WCF Project?
Regards
The issue is resolved using the following steps:
I decorated my service implementation with the AspNetCompatibilityRequirements attribute:
[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Required)]
public class Service : IService {
. . .
}
The last thing I had to do was necessitated by WCF not supporting multiple host headers; I had to hard-wire the WCF endpoint to listen on a specific hostname. In this case, this involved tweaking the serviceHostingEnvironment section of web.config, which now looks like this:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
<baseAddressPrefixFilters>
<add prefix=http://services.mydomain.com” />
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
And then adding another attribute to the service implementation class and initializing the HttpContext.Current.Items session:
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
[AspNetCompatibilityRequirements(RequirementsMode= AspNetCompatibilityRequirementsMode.Required)]
public class Service : IService {
HttpContext.Current.Items["_EntityContext"] = new DBEntities();
...
}

How to get rid of app.config and move it all into code?

I tried this question in a generic way on this post: https://stackoverflow.com/q/18968846/147637
But that did not get us to the result.
Soooo, here it is concretely!
I have the code below. It works. In VS, you add a web reference, code up the below, and then.... start fiddling the app.config.
And it works.
But I need to get rid of the app config. It is a problem that crucial pieces of the code are not in the.... code. It is hard to document, and easy for folks looking at this example to forget to look in the app config (this is an example for other devs).
So the question is: How do I move the contents of app.config into code?
(I am a part part part time coder. Pointing me at generic documentation won't get me there, sorry to say!)
**// .cs file:**
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// create the SOAP client
joesWebServerClient server = new joesWebServerClient();
string payloadXML = Loadpayload(filename);
// Run the SOAP transaction
string response = server.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
=================================================
**app.config**
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<!-- Some non default stuff has been added by hand here -->
<binding name="IjoesWebServerbinding" maxBufferSize="256000000" maxReceivedMessageSize="256000000" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://joesWebServer/soap/IEntryPoint"
binding="basicHttpBinding" bindingConfiguration="IjoesWebServerbinding"
contract="myNameSpace.joesWebService.WebAPI.SOAP.IjoesWebServer"
name="IjoesWebServerSOAP" />
</client>
</system.serviceModel>
</configuration>
Generally speaking, a config file is preferred over hard-coding the settings because all you need to do with a config file is change the values you want to change and then restart the application. If they're hardcoded, you have to modify the source, recompile and redeploy.
Having said that, you can pretty much do everything in code that you do in the config file for WCF (I seem to recall a few exceptions, but don't remember them off hand).
One way to achieve what you're looking for is to define the binding in your code and create the client via ChannelFactory<T>, where T is the interface for your service (more accurately the service contract, which is usually in an interface and then implemented by a class).
For example:
using System.ServiceModel;
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// Create the binding
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.MaxBufferSize = 256000000;
myBinding.MaxReceivedMessageSize = 256000000;
// Create the Channel Factory
ChannelFactory<IjoesWebServer> factory =
new ChannelFactory<IjoesWebServer>(myBinding, "http://joesWebServer/soap/IEntryPoint");
// Create, use and close the client
IjoesWebService client = null;
string payloadXML = Loadpayload(filename);
string response;
try
{
client = factory.CreateChannel();
((IClientChannel)client).Open();
response = client.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
((IClientChannel)client).Close();
}
catch (Exception ex)
{
((ICientChannel)client).Abort();
// Do something with the error (ex.Message) here
}
}
}
Now you don't need a config file. The additional settings you had in the example are now in the code.
The advantage of ChannelFactory<T> is that once you create an instance of the factory, you can generate new channels (think of them as clients) at will by calling CreateChannel(). This will speed things up as most of your overhead will be in the creation of the factory.
An additional note - you're using I<name> in a lot of places in your config file. I usually denotes an interface, and if a full time developer were to look at your project it might be a little confusing for them at first glance.
With WCF 4.5, if you add a static config method to your WCF service class, then it will load automatically and ignore what's in app.config file.
<ServiceContract()>
Public Interface IWCFService
<OperationContract()>
Function GetData(ByVal value As Integer) As String
<OperationContract()>
Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
End Interface
Public Class WCFService
Implements IWCFService
Public Shared Function CreateClient() As Object
End Function
Public Shared Sub Configure(config As ServiceConfiguration)
'Define service endpoint
config.AddServiceEndpoint(GetType(IWCFService), _
New NetNamedPipeBinding, _
New Uri("net.pipe://localhost/WCFService"))
'Define service behaviors
Dim myServiceBehaviors As New Description.ServiceDebugBehavior With {.IncludeExceptionDetailInFaults = True}
config.Description.Behaviors.Add(myServiceBehaviors)
End Sub
Public Function GetData(ByVal value As Integer) As String Implements IWCFService.GetData
Return String.Format("You entered: {0}", value)
End Function
Public Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType Implements IWCFService.GetDataUsingDataContract
End Function
End Class
I'm still looking into how to do the same for the client. I'll try to update when I figure it out if there's any interest.

WCF service generated by WSCF.blue Service Error "implementation type is an interface or abstract class and no implementation object was provided"

I am using C# Visual Studio 2012 to create a wcf service.
I had the WSCF.blue tool generate the wsdl from the xsd-s. Then I generated the web service code using the same tool. WSCF.blue does not create a Service Contract and a Data Contract. It creates an interface and a .svc file that contains a class that implements the interface.
When generating the web service code I selected the option to create the abstract classes because I want to be able to keep the implementation of these classes in a separate file.
The abstract class looks like this:
[KnownType(typeof(WebMobileImplementation))]
public abstract class WebMobile : IWebMobile
{
public abstract PutLocationsResponse PutLocations(PutLocationsRequest request);
}
The implementing class (in a different file) looks like this (for now):
public class WebMobileImplementation : WebMobile
{
public override PutLocationsResponse PutLocations(PutLocationsRequest request)
{
PutLocationsResponse response = new PutLocationsResponse();
return response;
}
}
When trying to browse the service I get the message: "Service implementation type is an interface or abstract class and no implementation object was provided"
I thought that adding the knowntype to the implementing class will do the trick but it seems that the implementation is not 'seen' when running the service. What else can I do to 'connect' them?
In WCF 4.0, you can define virtual service activation endpoints that map to your service types in Web.config. This makes it possible to activate WCF services without having to maintain physical .svc files.
<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="WebMobile.svc"
service="WebMobileNamespace.WebMobileImplementation"/>
</serviceActivations>
</serviceHostingEnvironment>

ServiceStack - how to host multiple versioned endpoints in one service?

Where I was
I'm trying to convert some WCF services to use ServiceStack instead. For the most part it's achieving what I want but there's definitely differences. eg with WCF I had something like:
interface IMethod1{ ResultDTO Method1(InputDTO input); }
interface IMethod2{ ResultDTO Method2(InputDTO input); }
interface IMethod3{ ResultDTO Method3(InputDTO input); }
interface IMyService : IMethod1, IMethod2, IMethod3
then implement with:
public class MyService : ServiceBase, IMyService { /* ... */ }
Where I'm at
With ServiceStack it's more like:
public class Method1{
// parameters for method as properties
}
public class Method2{
// parameters for method as properties
}
public class Method3{
// parameters for method as properties
}
I've tried various thing and the latest dead-end I've hit was with:
public class MyServiceHost<T> : AppHostBase
{
public MyServiceHost(string version)
: base("My Service v" + version, typeof(T).Assembly)
{ }
public override void Configure(Funq.Container container){
Routes.AddFromAssembly(typeof(T).Assembly);
}
}
protected void Application_Start(object sender, EventArgs e) {
new MyServiceHost<Foo.Bar.V0101.MyService>("1.1").Init();
new MyServiceHost<Foo.Bar.V0102.MyService>("1.2").Init();
new MyServiceHost<Foo.Bar.V0201.MyService>("2.1").Init();
}
where it complains that AppHost has already been initialised.
Where I want to be
I want to expose something like this:
http://www.sandwich.com/example/v0101/sandwichservice.wsdl
http://www.sandwich.com/example/v0102/sandwichservice.wsdl
http://www.sandwich.com/example/v0201/sandwichservice.wsdl
or
http://www.sandwich.com/example/sandwich_v0101.wsdl
http://www.sandwich.com/example/sandwich_v0102.wsdl
http://www.sandwich.com/example/sandwich_v0201.wsdl
ideally hosted in the same service process.
So is there a simple answer I'm missing or am I approaching the whole thing fundamentally wrong? Or in a nutshell: using ServiceStack, is it possible to and how can I expose multiple endpoints and WSDLs for versioned web services in the same host service?
See this answer for recommended versioning strategies with ServiceStack.
You can't expose multiple versions of SOAP/WSDL's in ServiceStack, you're encouraged to evolve the same DTO's which means there are no previous type versions to create an older version of the WSDL. You would need to host older versions of ServiceStack project for the auto-generated WSDL to match up with older types.
You could also take a snapshot of a WSDL and host it statically, but whether a new SOAP endpoint accepts a client sending an old SOAP version is up to .NET's WCF Message class doing the parsing. But as SOAP is a brittle format, YMMV.

Can I create a custom roleprovider through a WCF service?

I have a web application that accesses a database through a wcf service. The idea is to abstract the data from the web application using the wcf service. All that works fine but I am also using the built in roleprovider using the SqlRoleManager which does access the aspnetdb database directly. I would like to abstract the roleprovider by creating a custom roleprovider in a wcf service and then accessing it through the wcf service.
I have created the custom role provider and it works fine but now I need to place it in a wcf service.
So before I jump headlong into trying to get this to work through the WCF service, I created a second class in the web application that accessed the roleprovider class and changed my web config roleprovider parameters to use that class. So my roleprovider class is called, "UcfCstRoleProvider" and my web.config looks like this:
<roleManager
enabled="true"
defaultProvider="UcfCstRoleProvider">
<providers>
<add
name="UcfCstRoleProvider"
type="Ucf.Security.Wcf.WebTests.UcfCstRoleProvider, Ucf.Security.Wcf.WebTests"
connectionStringName="SqlRoleManagerConnection"
applicationName="SMTP" />
</providers>
</roleManager>
My class starts like this:
public class UcfCstRoleProvider : RoleProvider
{
private readonly WindowsTokenRoleProvider _roleProxy = new WindowsTokenRoleProvider();
public override string ApplicationName
{
get
{
return _roleProxy.ApplicationName;
}
set
{
_roleProxy.ApplicationName = value;
}
}
As I said, this works fine. So the second class is called BlRoleProvider that has identical properties and parameters as the roleprovide but does not implement RoleProvider. I changed the web.config to point to this class like this:
<roleManager
enabled="true"
defaultProvider="BlRoleProvider">
<providers>
<add
name="UcfCstRoleProvider"
type="Ucf.Security.Wcf.WebTests.BlRoleProvider, Ucf.Security.Wcf.WebTests"
connectionStringName="SqlRoleManagerConnection"
applicationName="SMTP" />
</providers>
</roleManager>
But I get this error.
"Provider must implement the class 'System.Web.Security.RoleProvider'."
I hope I have explained well enough to show what I am trying to do. If I can get the roleprovider to work through another class in the same application, I am sure it will work through the WCF service but how do I get past this error?
Or maybe I took a wrong turn and there is a better way to do what I want to do??
I think your best bet is to create a custom role provider and implement each method. In the implementation of each method, call the WCF service to do the data access. Eg:
public class WcfRoleProvider: RoleProvider
{
public bool IsUserInRole(string username, roleName)
{
bool result = false;
using(WcfRoleService roleService = new WcfRoleService())
{
result = roleService.IsUserInRole(username, roleName);
}
return result;
}
}
No, you have to have a class that must implement RoleProvider. That will not work. If you can't have this class inherit from RoleProvider directly, consider creating a RoleProvider wrapper class that implements RoleProvider's props/methods, but utilizes whatever you need to do with this second class.
This error isn't specific to WCF, but is specific to the role provider framework.
HTH.
Looking at your code it appears that you already have your configuration using a custom role provider.
If you want to be able to authentiacate users mking calls through your web service you should implement a custom header that authenticates each request against your configured role provider.
Things work slightly differently in WCF, it's not like you have access to session and application states since each call is considered to be a stateless one, a custom header however will offset that by handling this stuff as the call is made.

Categories