European Silverlight 4 & Silverlight 5 Hosting BLOG

BLOG about Silverlight 5 Hosting and Its Techologies - Dedicated to European Windows Hosting Customer

European WCF Hosting - Amsterdam :: How to Create WCF Service with SOAP/REST Endpoints

clock June 10, 2013 08:35 by author Scott

In this post I am going to describe a solution to the following problem.  I would like to create a single WCF Service and expose it via a standard SOAP endpoint and REST endpoint using Entity Framework, WCF and WCF REST.  Then I would like to consume it from WinRT from two different view models working against the same view.  This is an exercise of research into data options in WinRT.

First of, let’s create a service.  I am going to use the following data class:

    public class Session
    {
        public int SessionID { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Speaker { get; set; }
        public DateTime When { get; set; }
    }

My data context for EF Code First is just as simple:

    public class Context : DbContext
    {
        public Context() :
            base("Name=VSLive")
        {
        }
        public DbSet<Session> Sessions { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Session>().Property(p => p.Title).HasMaxLength(100).IsRequired();
            modelBuilder.Entity<Session>().Property(p => p.Speaker).HasMaxLength(50).IsRequired();
            modelBuilder.Entity<Session>().Property(p => p.Description).IsRequired();
            modelBuilder.Entity<Session>().Property(p => p.When).IsRequired();
        }
    }

Now, the service.  I am just going to perform basis CRUD opertions.  The key to the service is my interface that I am going to decorate with both SOAP(OperationContract) and REST(WebGet or WebInvoke) attributes.

    [ServiceContract]

    public interface IVSLiveService
    {
        [OperationContract]
        [WebGet(UriTemplate = "/GetList", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        Session[] GetList();

        [OperationContract]
        [WebInvoke(UriTemplate = "/Create", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        Session Create(Session session);


        [OperationContract]
        [WebInvoke(UriTemplate = "/Update", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        Session Update(Session session);

        [OperationContract]
        [WebInvoke(UriTemplate = "/Delete?sessionId={sessionId}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        void Delete(int sessionId);

    }

The implementation is not quite as interesting, but for the same of completeness of this post, here it goes:

using System.Data.Entity;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using WinRT.Data;
using WinRT.DataAccess;

namespace WcfService
{
    [AspNetCompatibilityRequirements(
      RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class VSLiveService : IVSLiveService
    {
        public VSLiveService()
        {
            Database.SetInitializer(new Initializer());
        }

        public Session[] GetList()
        {
            using (var context = new Context())
            {
                context.Configuration.LazyLoadingEnabled = false;
                context.Configuration.ProxyCreationEnabled = false;
                return context.Sessions.ToArray();
            }
        }

        public Session Create(Session session)
        {
            using (var context = new Context())
            {
                context.Sessions.Add(session);
                context.SaveChanges();
            }
            return session;
        }


        public Session Update(Session session)
        {
            using (var context = new Context())
            {
                context.Entry(session).State = System.Data.EntityState.Modified;
                context.SaveChanges();
            }
            return session;
        }

        public void Delete(int sessionID)
        {
            using (var context = new Context())
            {
                var session = new Session { SessionID = sessionID };
                context.Entry(session).State = System.Data.EntityState.Deleted;
                context.SaveChanges();
            }
        }
    }
}

Now, the part that took me the longest to figure out: web.config.

I have single service node, and I have two endpoints for it, using the same contract, but two different bindings and behaviors.  I am putting entire web.config:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add
          name="VSLive"
          connectionString="Server=.;Database=VSLive;Trusted_Connection=True;"
          providerName="System.Data.SqlClient"/>
  </connectionStrings>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="jsonBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>    
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!—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 -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding
          name="VSLiveService_BasicHttpBinding"
          maxBufferSize="1000000"
          maxReceivedMessageSize="1000000">
          <readerQuotas
            maxBytesPerRead="1000000"
            maxArrayLength="1000000"
            maxDepth="1024"
            maxStringContentLength="1000000"/>
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding
           name="VSLiveService_WebHttpBinding"
           maxBufferSize="1000000"
           maxReceivedMessageSize="1000000">
          <readerQuotas
            maxBytesPerRead="1000000"
            maxArrayLength="1000000"
            maxDepth="1024"
            maxStringContentLength="1000000"/>
        </binding>
      </webHttpBinding>

    </bindings>
    <services>
      <service name="WcfService.VSLiveService">
        <endpoint
          address="soap"
          binding="basicHttpBinding"
          bindingConfiguration="VSLiveService_BasicHttpBinding"
          contract="WcfService.IVSLiveService"/>
        <endpoint
            address="rest"
            binding="webHttpBinding"
            behaviorConfiguration="jsonBehavior"
            bindingConfiguration="VSLiveService_WebHttpBinding"
            contract="WcfService.IVSLiveService"/>
      </service>
    </services>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true">
        <listeners>
          <add name="traceListener"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData= "c:\Traces.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
</configuration>

As you can see above, SOAP endpoint comes first, and it is using basicHttpBinding.  My REST endpoint is second, and it is using webHttpBinding  I am asing a behavior configuration to the latter one, enabling webHttp get/post methods.

This is all nice and simple, and you can now test it in browser.

Today, I am documenting REST consumption.

I am using HttpClient class to accomplish this task.  For example, here is how I am going to get the list of sessions.

        public async Task LoadData()
        {
            IsBusy = true;
            _client = new HttpClient();
            _client.MaxResponseContentBufferSize = int.MaxValue;
            var response = await _client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri(_serviceUri + "GetList")));

            var data = response.Content.ReadAsString();

            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Session>));
            using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(data)))
            {
                var list = serializer.ReadObject(stream) as List<Session>;
                Sessions = new ExtendedObservableCollection<Session>(list);
            }
            IsBusy = false;
        }

A few points about the code above.  I should have wrapped the call inside Try/Catch, I am just skipping it for the sake of a demo and to minimize the code I am showing.  I am using standard serializer to convert my JSON message into an object.  I also have a little progress ring that is playing while server communication is going on, and that is what my IsBusy property above is bound to. 

Now, let’s take a look at Create/Update call.  It is just as simple, but I am using Post method of HttpClient and I am creating a string content to post by converting Session object to JSON, again using the same serializer.

        public async void OnSave(object parameter)
        {
            if (SelectedSession != null)
            {
                IsBusy = true;
                string method = "Update";
                if (selectedSession.SessionID == 0)
                {
                    method = "Create";
                }
                _client = new HttpClient();
                _client.MaxResponseContentBufferSize = int.MaxValue;
                var content = new StringContent(ConvertSessionToJson());
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                var response = await _client.PostAsync(new Uri(_serviceUri + method), content);

                var data = response.Content.ReadAsString();

                var session = ConvertJsonToSession(data);
                Sessions[Sessions.IndexOf(selectedSession)] = session;
                SelectedSession = session;
                IsBusy = false;
            }
        }

For delete method I am also using Post method, just my content is blank and my ID is passed to the server as query string parameter

        public async void OnDelete(Session parameter)
        {
            if (parameter != null)
            {
                if (parameter.SessionID > 0)
                {
                    IsBusy = true;
                    _client = new HttpClient();
                    _client.MaxResponseContentBufferSize = int.MaxValue;
                    var content = new StringContent(string.Empty);
                    content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
                    var response = await _client.PostAsync(new Uri(_serviceUri + "Delete?sessionId=" + parameter.SessionID.ToString()), content);

                    var data = response.Content.ReadAsString();
                    Sessions.Remove(parameter);
                    IsBusy = false;
                }
                else
                {
                    Sessions.Remove(parameter);
                    IsBusy = false;
                }
            }
        }

 



European WCF Hosting - Amsterdam :: How to Host WCF Service in IIS 8 (Windows Server 2012)

clock April 25, 2013 06:56 by author Scott

This blog cover brief information how to host your WCF service in IIS8 (Windows Server 2012).

Here is the solution.

Server Roles

1. First make sure you have enabled IIS function and .net 3.5 in Features.
For the IIS features, please remember to enable ASP.NET3.5 and ASP.NET 4.5

2. Second, check the IIS Hostable WebCore
3. Finally, I think the most important is this:

Check Application Sever->Web Server (IIS) Support

I have also check the HTTP Activation in Windows Process Activation Service Support, but I do not know if it is required.

For the freatures,

1. Check all items in .NET 3.5
2. Check WCF Service in .NET 4.5

That’s it.

Last but not least, I have register the WCF Service from

C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ServiceModelReg.exe –i

Run the above in command line.

 



European Silverlight Hosting - Amsterdam :: Dynamic Compression in IIS 7

clock February 5, 2013 10:17 by author Scott

This is the question from one of our clients. The client insisted on returning large datasets, well in excess of 10,000 records. I will leave the story of figuring out how to properly specify the MaxItemsInObjectGraph service behavior attribute for some other post, but the other problem I was constantly aware of, was the data size returned from the server. With all filters set to max, the data set was well in excess of 30 megabytes. This might not be a big problem on a local network, but if some of your users are located across the big pond called Atlantic, you might want to compress your data before shipping it over.

Now, the IIS 7 console only allows you to enable or disable static compression, but it does not let you control which dynamic types are being compressed as well as the level of compression desired for each content type.

The command you are supposed to use instead is AppCmd.exe located in C:\Windows\System32\inetsrv directory.

So here are three sample commands that helped me reduce the size of my WCF RIA Domain Service's binary response by 80%. Needless to say I was pleasantly shocked.

Enable compression on WebDevel webserver (when you have multiple servers and want to do it specifically for each)


C:\Windows\System32\inetsrv>Appcmd.exe set config "WebDevel" -section:urlCompression -doStaticCompression:true -doDynamicCompression:true


Add mime-type application/msbin1 to dynamic compression list (service wide)


C:\Windows\System32\inetsrv>Appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/msbin1',enabled='True']" /commit:apphost


Set compression levels for static and dynamic content (service wide)


C:\Windows\System32\inetsrv>Appcmd.exe set config -section:httpCompression -[name='gzip'].staticCompressionLevel:9 -[name='gzip'].dynamicCompressionLevel:5



Tag cloud

Sign in