Relationship between SVC files and WCF projects? - c#

When creating a WCF project, the default member files are just ordinary csharp class files, rather than svc files. Are svc files required with a WCF project? When should they be used?

.svc files are used when you host your WCF service in IIS.
See Microsoft's doc here and here.
There's a module within IIS that handles the .svc file. Actually, it is the ASPNET ISAPI Module, which hands off the request for the .svc file to one of the handler factory types that has been configured for ASPNET, in this case
System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
If you are hosting your WCF service in something other than IIS, then you don't need the .svc file.

If you are using .net 4.0 or later, you can now "simulate" the .svc via config with the following:
<system.serviceModel>
<!-- bindings, endpoints, behaviors -->
<serviceHostingEnvironment >
<serviceActivations>
<add relativeAddress="MyService.svc" service="MyAssembly.MyService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
Then you don't need a physical .svc file nor a global.asax

It is possible to create a WCF project and host it in IIS without using a .svc file.
Instead of implementing your DataContract in your svc code-behind, you implement it in a normal .cs file (i.e. no code behind.)
So, you would have a MyService.cs like this:
public class MyService: IMyService //IMyService defines the contract
{
[WebGet(UriTemplate = "resource/{externalResourceId}")]
public Resource GetResource(string externalResourceId)
{
int resourceId = 0;
if (!Int32.TryParse(externalResourceId, out resourceId) || externalResourceId == 0) // No ID or 0 provided
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
return null;
}
var resource = GetResource(resourceId);
return resource;
}
}
Then comes the thing making this possible. Now you need to create a Global.asax with code-behind where you add an Application_Start event hook:
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
// Edit the base address of MyService by replacing the "MyService" string below
RouteTable.Routes.Add(new ServiceRoute("MyService", new WebServiceHostFactory(), typeof(MyService)));
}
}
One nice thing about this is that you don't have to handle the .svc in your resource URLs. One not so nice thing is that you now have a Global.asax file.

Related

Configure WCF where ServiceContract and service implementation are in separate assemblies

I've looked at a lot of questions on this site that discuss, but don't directly answer this question. I have the following:
In Library.dll:
namespace LibraryNamespace
{
[ServiceContract]
public interface IService
{
[OperationContract]
void Operation();
}
}
In Implementation.dll:
namespace ImplementationNamespace
{
public class ServiceImplementation : IService
{
public void Operation()
{
// Do Something
}
}
}
In app.config:
<service name="ImplementationNamespace.ServiceImplementation">
<endpoint
address="ServiceImplementation"
binding="netTcpBinding"
contract="LibraryNamespace.IService" />
....
</service>
And I keep having a warning with contract="LibraryNamespace.IService". The program runs, but I have a feeling this warning is causing me more problems down the line.
The 'contract' attribute is invalid - The value
'LibraryNamespace.IService' is invalid according to its datatype
'serviceContractType' - The Enumeration constraint has failed.
It works when the ServiceContract and the service implementation are in the same assembly and namespace, but for some reason, it doesn't work here. How can I reference it properly?
I am not sure Why do you want to have the contract and implementation in separate dll? any specific reason? Generally they will be in same assembly and so in config file you can refer them with ease. One way to solve this is creating the service endpoint at runtime like below.
In your hosting project refer both the dll Library.dll and Implementation.dll and have the below code to add the endpoint
using LibraryNamespace;
using ImplementationNamespace;
// Specify a base address for the service
String baseAddress = "http://localhost/ServiceImplementation";
// Create the tcp binding
NetTcpBindings tcp = new NetTcpBindings();
// Define service and Create the endpoint
using(ServiceHost host = new ServiceHost(typeof(ServiceImplementation)))
{
host.AddServiceEndpoint(typeof(IService),tcp, baseAddress);
}

Enable CORS for static resources in ASP .NET MVC?

I've found plenty of resources regarding CORS in Web APIs and for general controllers in ASP .NET MVC.
However, I'm in a situation where I'd like all static resources (CSS and JS files) inside a specific folder to be downloadable through AJAX as well. In other words, enable CORS for those resources or that folder.
How can I accomplish this? I've found no similar question. They are all related to web APIs or general controllers.
Example adapted from Walkthrough: Creating and Registering a Custom HTTP Module. This should add the header to all .js and .css requests.
Create Module
using System;
using System.Web;
public class HelloWorldModule : IHttpModule
{
public HelloWorldModule()
{
}
public String ModuleName
{
get { return "HelloWorldModule"; }
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
// Create HttpApplication and HttpContext objects to access
// request and response properties.
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(".css") || fileExtension.Equals(".js"))
{
context.Response.AddHeader("Access-Control-Allow-Origin", "*");
}
}
public void Dispose() { }
}
To register the module for IIS 6.0 and IIS 7.0 running in Classic mode
<configuration>
<system.web>
<httpModules>
<add name="HelloWorldModule" type="HelloWorldModule"/>
</httpModules>
</system.web>
</configuration>
To register the module for IIS 7.0 running in Integrated mode
<configuration>
<system.webServer>
<modules>
<add name="HelloWorldModule" type="HelloWorldModule"/>
</modules>
</system.webServer>
</configuration>
As you are running MVC, make sure you alter the one in the root (not the Views folder).

Extending WebService Proxy class (imported from WSDL) methods

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.

How do I include my own wsdl in my web service in C#

I have a .wsdl file that my web service (old asmx style) must implement. That is taken care of. When I publish the web service you can call it with ?wsdl parameter to get a generated wsdl.
How do I include my .wsdl file so that is the one that is returned instead of the generated one?
Is it possible to do with an attribute in my web service class?
Is it a given to stay with "old-style" ASMX? Or could you move up to WCF? That's really the most current webservice offering by Microsoft, and if you're doing something new and you're on .NET 3.0 or higher - why spend time on "old" technology?
In WCF, you could definitely define a static physical WSDL file to be used by clients connecting to your metadata endpoint (your "...?wsdl" URL). Not sure if you can do it in ASMX, too.
OK, on ASMX / .NET 2.0, you could of course always put the actual WSDL file under the root of your web site, and then just reference it like this:
http://yourwebserver/YourVirtDir/MyService.wsdl
I don't know if there's a way to "redirect" the
http://yourwebserver/YourVirtDir/MyService.asmx?wsdl
call to go to that fixed URL instead. I'm sure someone else will know, though!
Marc
To avoid the confusion of having two different WSDLs available on two different URLs (i.e., the *.asmx?wsdl URL and a custom URL) in your web service application, you could write an HttpModule that intercepts the request to the *.asmx?wsdl URL and returns your custom WSDL instead.
EDIT: Here's an example, adapted and simplified from some code I previously wrote that makes a custom WSDL available at the standard *.asmx?wsdl URL.
using System;
using System.IO;
using System.Web;
using System.Web.Services.Configuration;
namespace DemoWebService
{
public class CustomWsdlModule :
IHttpModule
{
public void
Init(HttpApplication application)
{
// hook up to BeginRequest event on application object
application.BeginRequest += new EventHandler(this.onApplicationBeginRequest);
}
public void
Dispose()
{
}
private void
onApplicationBeginRequest(object source, EventArgs ea)
{
HttpApplication application = (HttpApplication)source;
HttpRequest request = application.Request;
HttpResponse response = application.Response;
// check if request is for WSDL file
if ( request.Url.PathAndQuery.EndsWith(".asmx?wsdl", StringComparison.InvariantCultureIgnoreCase) )
{
// if Documentation protocol is not allowed, throw exception
if ( (WebServicesSection.Current.EnabledProtocols & WebServiceProtocols.Documentation) == 0 )
{
throw new System.InvalidOperationException("Request format is unrecognized.");
}
// get path to physical .asmx file
String asmxPath = request.MapPath(request.Url.AbsolutePath);
// build path to .wsdl file; should be same as .asmx file, but with .wsdl extension
String wsdlPath = Path.ChangeExtension(asmxPath, ".wsdl");
// check if WSDL file exists
if ( File.Exists(wsdlPath) )
{
// read WSDL file
using ( StreamReader reader = new StreamReader(wsdlPath) )
{
string wsdlFileContents = reader.ReadToEnd();
// write WSDL to response and end response without normal processing
response.ContentType = "text/xml";
response.Write(wsdlFileContents);
response.End();
}
}
}
}
}
}
This simplified code assumes that your custom WSDL is in the same folder as your .asmx file with a .wsdl extension. The HttpModule needs to be hooked into your web service application via the web.config file:
<?xml version="1.0"?>
<configuration>
<!-- ... -->
<system.web>
<!-- ... -->
<httpModules>
<add
type="DemoWebService.CustomWsdlModule"
name="CustomWsdlModule"/>
<!-- ... -->
</httpModules>
<!-- ... -->
</system.web>
<!-- ... -->
</configuration>
You can generate a WSDL and DISCO file by pointing the disco.exe tool that ships with the .NET Framework at your web service.
disco.exe http://webserver/MyWebService.asmx
The following files are created:
results.discomap
MyWebService.disco
MyWebService.wsdl

How to define endpoint without web.config or httpModule?

I would like to make a RESTful app of HTTPhandlers without having to define every endpoint by making an entry in the web.config, i'd like the style of attaching attributes to a class constructor eg:
public class obj : IHttpHandler
{
[WebGet(UriTemplate = "/accounts/{id}")]
public obj(string id)
{
// this is just an eg, it worild normally include caching and
// a template system
String html = File.ReadAllText("/accounts/accounts.htm");
html.replace("id", id);
httpcontext.current.response.write(html)
}
}
instead of
<httpHandlers>
<clear />
<add verb="GET" path="/accounts/*" type="MyApp.obj" />
</httphandlers>
The way i'm doing it now i have 100's of endpoints in the web.config :( i'd rather define them in the class. And i don't want to make extra files (.asmx) either. I'd like an app of just .htm files with tokens and .cs files
Thanks!
You could automate the registration of the endpoints and so on, with a custom ServiceHost, which overrides the ApplyConfiguration() method, which then virtualizes the configuration so that it does not have to be in the web.config file.
Here's a starting point. It doesn't do exactly what you want, but it illustrates the concept of virtualizing the configuration.

Categories