Monday 21 November 2011

WCF 4.0 File-less Activation

So far till framework 3.5 we have used the service activation using the .SVC files, with Web Application. If you look closely to this pattern, some of the issues we face are

  • SVC file makes the Service tightly coupled with the activation process
  • Adds extra maintenance effort.
  • Every time we deploy our application we may need to change the config files with the updated Server URL’s.
  • If we want to rename our Service we have to physically change the file names.

WCF 4.0 had added lots of new features on 3.5 framework, you can get more details of these features here: What’s New in WCF 4.

In this post I am going to cover just the one very interesting feature of WCF 4.0 or 4.5 to solve the above mentioned issue, that is the File Less Activation, which says we do not require to keep any physical .SVC files anymore in our web application. Now you might be wondering how it is possible to add a reference or create a proxy to my service or invoke my Service, right ?

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.

The following example shows how to configure an activation endpoint.

<serviceHostingEnvironment>




  <serviceActivations>



    <add relativeAddress="CustomerService.svc" 



         service="WcfService1.CustomerService"/>



  </serviceActivations>



</serviceHostingEnvironment>




Now let us take a step back and see how our service was looking when we create using a default template for “WCF Service Application” Or “WCF Service Library”



image



By default your WCF Service Application create a Service1.svc files, follow the steps below to enable your Service with file-less activation.



1. Safely delete both the files Service1.svc and Service1.svc.cs.



2. Now add a class file which you want to create as Service, e.g FileService.cs, etc.



3. Implement the contract, IService1.cs in your FileService.cs,



4. Now coming back to the configuration file, add the relativeAddress with the desired SVC file name here and assign the service attribute with the namespace of your Service.



This will look similar to the line of code mentioned in the code snippet above.



So now with all the changes implemented given in the steps above your complete web.config file will be somewhat similar to the one below.





1: <?xml version="1.0"?>




   2: <configuration>



   3:   <system.web>



   4:     <compilation debug="true" targetFramework="4.0" />



   5:   </system.web>



   6:   <system.serviceModel>



   7:     <behaviors>



   8:       <serviceBehaviors>



   9:         <behavior>



  10:           <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->



  11:           <serviceMetadata httpGetEnabled="true"/>



  12:           <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->



  13:           <serviceDebug includeExceptionDetailInFaults="false"/>



  14:         </behavior>



  15:       </serviceBehaviors>



  16:     </behaviors>



  17:     



  18:     <serviceHostingEnvironment>



  19:       <serviceActivations>



  20:         <add relativeAddress="CustomerService.svc" 



  21:              service="WcfService1.CustomerService"/>



  22:       </serviceActivations>



  23:     </serviceHostingEnvironment>



  24:  



  25:     <!--<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />-->



  26:   </system.serviceModel>



  27:  <system.webServer>



  28:     <modules runAllManagedModulesForAllRequests="true"/>



  29:   </system.webServer>



  30:   



  31: </configuration>






Leaving all the configuration as default provided by your VS2010, we are just going to replace the <serviceHostingEnvironment> given in Line 18 above with codes required for file-less activation. Now lets hit F5 or Run, ideally this will display all the files contained in your Service directory in the browser windows with the Svc file missing. but don’t worry since we are creating the virtual service endpoints so there is no physical files to display here. But you can append the existing URL with  your desired “ServiceName + .SVC” file extension at the end of your browser URL “http://localhost:3035/CustomerService.svc



image



And that’s it, our service is now ready for hosting without any physical SVC file, you can deploy this Service application in IIS too or refer in any other applications without any change in config files, you just need to browse the service application and type the SVC service file name defined in your web.config file at the end of the URL in your browser window or Add Service Reference windows respectively.




The example given in this article gives you the basic idea of file-less activation. In actual scenarios you may have much bigger config files. To handle your complex configuration files you just need small changes to configure file-less activation, For example if you have defined single or multiple endpoints in your configurations then you just need to remove the address attribute from the endpoint tag and add the ServiceHostingEnvironment attribute mentioned above. This also works if you have multiple Service defined in the same application.




Few points to Note (Source: MSDN)





  • When using configuration-based activation, inline code in .svc files is not supported.




  • The relativeAddress attribute must be set to a relative address such as “<sub-directory>/service.svc” or “~/<sub-directory/service.svc”.




  • A configuration exception is thrown if you register a relative address that does not have a known extension associated with WCF.




  • The relative address specified is relative to the root of the virtual application.




  • Due to the hierarchical model of configuration, the registered relative addresses at machine and site level are inherited by virtual applications.




  • Registrations in a configuration file take precedence over settings in a .svc, .xamlx, .xoml, or other file.




  • Any ‘\’ (backslashes) in a URI sent to IIS/WAS are automatically converted to a ‘/’ (forward slash). If a relative address is added that contains a ‘\’ and you send IIS a URI that uses the relative address, the backslash is converted to a forward slash and IIS cannot match it to the relative address. IIS sends out trace information that indicates that there are no matches found.




References:











Share:

Saturday 15 October 2011

WCF REST and SOAP Service with WSSF (Service Factory 2010)

SOAP or REST this is always been a debated topic. In both the approach we have certain disadvantages as well as advantages. Internet is flooded with such an articles, but what if we want to keep both parallel, yes of course we can do this because WCF gives us all the freedom to use both the Approach using the same Service Contract. There is a very nice article which covers this topic you can find here.

Expose WCF 4.0 Service as SOAP and REST

Now what if we want to use Service Factory (I Assume reader of this article is familiar with WSSF 2010, if not then you can refer this section, you can get every thing you need to get started here.) . Service Factory is designed to help you quickly and consistently construct WCF Web services that adhere to well known architecture. But along with this WSSF does not support REST Service out of the box. We may either have to customize the WSSF Templates or may have to think something really out of the box and that's what I am going to show in this article.

There are couple of reason why I personally preferred the second approach and not to customize the template is for the design perspective if I keep both SOAP and REST in the same Contract it will be tightly coupled and SOAP and REST works in different way. And the same Service contract will have Dependency on both REST and SOAP. Also when we have to change something specific to REST or SOAP we will end up in changing for both even if we don't want to do so, this may force us to test both the clients of REST and SOAP. But this is just for case to case basis and I am leaving this on the reader. And now I am closing this design issues here and jumping directly to the implementation of the above.

Again I am not going to cover the basics of SOAP or REST or WSSF. I am going to create a WSSF guidance package with Service contract, data contract and host models, canvas diagrams of the same is given below.

image

image

For this example I am going to use the Northwind Database and to keep this simple and precise I am using only Customer table. With the above diagrams I hope you can now figure out my Service Contract, Data Contract and Message Contract. You can download the Northwind Database from here.

Northwind and pubs Sample Databases for SQL Server 2000

For the ORM tool I have used Entity Framework to keep this example simple, you may use your own Data Layer if you want. Generated solution structure  looks like the one below, where I have done few addition for REST highlighted in RED.

image

Here in the example we have defined separate Service contract for Rest Service called NorthwindRestService which implements the contract INorthwindRestServiceContract. These are the files which are not generated by WSSF. Now lets get more into the codes of these files below.

using System.Collections.Generic;



using System.ServiceModel;



using System.ServiceModel.Web;



using WssfRestNorthwind.DataContracts;



 



namespace WssfRestNorthwind.ServiceContracts.Rest



{



    /// <summary>



    /// Service Contract Class - NorthwindRestServiceContract



    /// </summary>



    [ServiceContract]



    public partial interface INorthwindRestServiceContract 



    {



        [WebGet(UriTemplate = "Customer",



            RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]



        List<Customer> GetAllCustomers();



 



        [OperationContract]



        [WebGet(UriTemplate = "Customer/{customerID}")]



        Customer GetCustomerById(string customerID);



        



        [WebInvoke(Method = "POST", UriTemplate = "Customer/Add")]



        void AddCustomer(Customer customer);



        



    }



}




In the code of Rest Service contract you can see I have used the WebGet and WebInvoke attributes to expose my service as a REST Service. And for my Service implementation I have used the codes below.





using System.Collections.Generic;



using WssfRestNorthwind.ServiceContracts.Rest;



 



namespace WssfRestNorthwind.ServiceImplementation



{



    public partial class NorthwindRestService : INorthwindRestServiceContract



    {



        BusinessLogic.Repository.CustomerRepository customerCotext = new BusinessLogic.Repository.CustomerRepository();



 



        #region INorthwindRestServiceContract Members



 



        public List<DataContracts.Customer> GetAllCustomers()



        {



            List<DataContracts.Customer> customers = new List<DataContracts.Customer>();



            DataContracts.Customer customer = new DataContracts.Customer(); 



 



            foreach (BusinessEntities.Customer cust in customerCotext.GetCustomers())



            {



                customers.Add(TranslateBetweenCustomerAndCustomer.TranslateCustomerToCustomer(cust));



            }



 



            return customers;



        }



 



        public DataContracts.Customer GetCustomerById(string customerID)



        {



            DataContracts.Customer customer = new DataContracts.Customer();



 



            return TranslateBetweenCustomerAndCustomer.TranslateCustomerToCustomer(customerCotext.GetCustomersById(customerID));



        }



 



        public void AddCustomer(DataContracts.Customer customer)



        {



            if (null != customer)



                customerCotext.AddCustomer(TranslateBetweenCustomerAndCustomer.TranslateCustomerToCustomer(customer));



        }



 



        #endregion



    }



}




As you can notice this service implementation is used for just passing the response and request objects from Service to Business Logic and Back to service. So we are reusing our common Business Logic, Data Contract and Business entities which are the heart of the service and we had put maximum effort in these layers. The only effort we are putting now in just creating the Service Contract and Service Implementation which is just few lines of codes. Now lets have a quick look into our Data Layers and Business Layers. The data model of our Database is given as below.



image



and we had used Repository Pattern to Query our models. You can get a quick look into the codes below for my CustomerRepository and ICustomerRepository respectively.





using System;



using System.Linq;



using WssfRestNorthwind.BusinessEntities;



 



namespace WssfRestNorthwind.BusinessLogic.Repository



{



    public class CustomerRepository: ICustomerRepository



    {



        NorthwindEntities context = new NorthwindEntities();



 



        #region ICustomerRepository Members



 



        public IQueryable<Customer> GetCustomers()



        {



            var customers = from result in context.Customers



                            select result;



 



            return customers;



        }



 



        public Customer GetCustomersById(string customerId)



        {



            var customer = (from result in context.Customers



                            where result.CustomerID == customerId



                            select result).FirstOrDefault();



 



            return customer;



        }



        



        public IQueryable<Order> GetCustomerOrders(string customerId)



        {



            var customerOrders = from result in context.Orders



                                 where result.CustomerID == customerId



                                 select result;



 



            return customerOrders;



        }



 



        public void AddCustomer(Customer customer)



        {



            context.AddToCustomers(customer);



            context.SaveChanges();



        }



 



        public void UpdateCustomer(Customer customer)



        {



            throw new NotImplementedException();



        }



 



        public void DeleteCustomer()



        {



            throw new NotImplementedException();



        }



 



        #endregion



 



    }



}






using System.Linq;



using WssfRestNorthwind.BusinessEntities;



 



namespace WssfRestNorthwind.BusinessLogic.Repository



{



    interface ICustomerRepository



    {



        IQueryable<Customer> GetCustomers();



        Customer GetCustomersById(string customerId);



        IQueryable<Order> GetCustomerOrders(string customerId);



        void AddCustomer(Customer customer);



        void UpdateCustomer(Customer customer);



        void DeleteCustomer();



    }



}




Above code is just for reference only to be used for this article, in real scenarios we may have huge amount of codes in these places and also much more files for repository and also for BusinessLogic. So if you have understood till now, these codes are common for both REST and SOAP service. We had also created a translator for converting the Business entities to Data Contract and from Data Contract back to Business Entities. Which is very much required if we are following this Service Factory Patterns. If you have any queries on basics of WSSF you can go back to the link of WSSF Tutorials which I have already given above earlier.



With these codes our Service is almost ready to consume, now the only thing which we are missing is the hosting of these services for our example I am using ASP.NET development server hosting but you can use any other hosting also as per your requirements. And also the endpoints. Since for SOAP we can use various amount of bindings which corresponds to WS-* standards, but for REST we have to use webHttpBinding. Code of my web.config of REST and SOAP Service is  given below.





<?xml version="1.0"?>



<configuration>



  <connectionStrings>



    <add name="NorthwindEntities" connectionString="metadata=res://*/NorthwindModel.csdl|res://*/NorthwindModel.ssdl|res://*/NorthwindModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=Northwind;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />



  </connectionStrings>



  <system.web>



    <compilation debug="true" targetFramework="4.0" />



  </system.web>



  <system.serviceModel>



    <behaviors>



      <endpointBehaviors>



        <behavior name="REST">



          <webHttp />



        </behavior>



      </endpointBehaviors>



      <serviceBehaviors>



        <behavior name="EndPointBehavior">



          <serviceMetadata httpGetEnabled="true"/>



          <serviceDebug includeExceptionDetailInFaults="true" />



        </behavior>



      </serviceBehaviors>



    </behaviors>



    <services>



      <service behaviorConfiguration="EndPointBehavior" name="WssfRestNorthwind.ServiceImplementation.NorthwindRestService">



        <endpoint address="" behaviorConfiguration="REST" binding="webHttpBinding" contract="WssfRestNorthwind.ServiceContracts.Rest.INorthwindRestServiceContract" />



        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />



      </service>



    </services>



  </system.serviceModel>



  <system.webServer>



    <modules runAllManagedModulesForAllRequests="true"/>



  </system.webServer>



</configuration>




and for SOAP Service





<?xml version="1.0"?>



<configuration>



  <connectionStrings>



    <add name="NorthwindEntities" connectionString="metadata=res://*/NorthwindModel.csdl|res://*/NorthwindModel.ssdl|res://*/NorthwindModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.;initial catalog=Northwind;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />



  </connectionStrings>



  <system.web>



    <compilation debug="true" targetFramework="4.0" />



  </system.web>



  <system.serviceModel>



    <behaviors>



      <endpointBehaviors>



        <behavior name="REST">



          <webHttp />



        </behavior>



      </endpointBehaviors>



      <serviceBehaviors>



        <behavior name="EndPointBehavior">



          <serviceMetadata httpGetEnabled="true"/>



          <serviceDebug includeExceptionDetailInFaults="true" />



        </behavior>



      </serviceBehaviors>



    </behaviors>



    <services>



      <service behaviorConfiguration="EndPointBehavior" name="WssfRestNorthwind.ServiceImplementation.NorthwindRestService">



        <endpoint address="" behaviorConfiguration="REST" binding="webHttpBinding" contract="WssfRestNorthwind.ServiceContracts.Rest.INorthwindRestServiceContract" />



        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />



      </service>



    </services>



  </system.serviceModel>



  <system.webServer>



    <modules runAllManagedModulesForAllRequests="true"/>



  </system.webServer>



 



</configuration>




So with this I guess my Service is 100% ready to host, for SOAP Service WSSF Provides with generated Test Client for my REST Service I have created a similar project only with few difference. To keep the article focussed I have included the Test Projects with Complete Source code in the download Section. Hosting model diagram is given below.



image



We can also test this using the browser with the following URL’s formats. Please note these URL’s are for indication only actual URL’s Server names may not be same for you environment



For Save Operation, this method will pass a new customer for addition into the DB http://localhost:3188/WssfRestNorthwind.Rest.Host/NorthwindService.svc/Customer/Add



Get Customer By Id http://localhost:3188/WssfRestNorthwind.Rest.Host/NorthwindService.svc/Customer/ALFKI



image



GetAllCustomers http://localhost:3188/WssfRestNorthwind.Rest.Host/NorthwindService.svc/Customer



image



For those who want to have a Bird’s eye view of the Solution structure can click on the image to get the snapshot of the Entire Solution.



image



To get the feel of the above article and get more into the implementation logic I recommend you to download the complete source from here.



Download Source Code.



Download Northwind Database from MSDN.



My objective of this article to show you the approach, so I am not getting into the details of few things like What is WSSF, REST, WCF, SOAP, etc.



This is my just a one approach from my point of view, if the reader of this article has a better idea or suggestion please feel free to comment. Thanks in advance.

Share: