WELCOME

This blog is where I post about my consulting work with Microsoft Technologies, and other random tidbits that don't fit in my Photo Blog or my Iraq Blog.

Thursday, July 24, 2008

Implementing a simple HelloWorld service in WCF using Interfaces and generic proxies

I needed a complete end to end demonstration of a .NET service implemented in WCF and taking advantage of Interface based contracts and generic proxies. I found lots of bits and pieces on the web, but I thought I would post my complete demo here in case somebody else finds value in it...

First the interface that defines our service contract:


using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

namespace SandBox.SharedClassLibrary

{

[ServiceContract]

public interface IHelloWorld

{

[OperationContract]

string GetHelloWorld(string Request);

}

}



The service itself (note in this case we are hosting the service in a ASP.NET project on IIS 7.0):


using System;

using System.Collections.Generic;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

using SandBox.SharedClassLibrary;

namespace SandBoxWeb

{

public class HelloWorldService : IHelloWorld

{

public string GetHelloWorld(string Request)

{

return Request + DateTime.Now.ToShortTimeString();

}

}

}



The web.config for the service:


<system.serviceModel>

<behaviors>

<serviceBehaviors>

<behavior name="SandBoxWeb.Service1Behavior">

<serviceMetadata httpGetEnabled="true"/>

<serviceDebug includeExceptionDetailInFaults="false"/>

</behavior>

</serviceBehaviors>

</behaviors>

<services>

<service behaviorConfiguration="SandBoxWeb.Service1Behavior" name="SandBoxWeb.HelloWorldService">

<endpoint address="" binding="wsHttpBinding" contract="SandBox.SharedClassLibrary.IHelloWorld">

<identity>

<dns value="localhost"/>

</identity>

</endpoint>

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

</service>

</services>

</system.serviceModel>



The client application which is a console application:

using System;

using System.Collections.Generic;

using System.Text;

using System.Reflection;

using System.ServiceModel;

namespace SandboxConsole

{

class Program

{

static void Main(string[] args)

{

Console.WriteLine(SandBox.SharedClassLibrary.ServiceClient.CallServiceByName("WSHttpBinding_HelloWorldService"));

Console.WriteLine(SandBox.SharedClassLibrary.ServiceClient.CallServiceByAddress("http://localhost:3456/HelloWorldService.svc"));

Console.ReadLine();

}

}

}




public class ServiceClient

{

public static string CallServiceByName(string EndPointName)

{

using (GenericProxy<IHelloWorld> service = new GenericProxy<IHelloWorld>(EndPointName))

{

return service.Channel.GetHelloWorld("Test by Name");

}

}

public static string CallServiceByAddress(string EndPointAddress)

{

Binding _binding = new WSHttpBinding();

EndpointAddress _endpointAddress = new EndpointAddress(EndPointAddress);

using (GenericProxy<IHelloWorld> service = new GenericProxy<IHelloWorld>(_binding, _endpointAddress))

{

return service.Channel.GetHelloWorld("Test by Address");

}

}

}



The (optional) app.config for the console application. This very verbose version was created for demonstration purposes using the "Create Service Reference" command.

Note: The second version of our call doesn't use the app.config at all.


<system.serviceModel>

<bindings>

<wsHttpBinding>

<binding name="WSHttpBinding_HelloWorldService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>

<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false"/>

<security mode="Message">

<transport clientCredentialType="Windows" proxyCredentialType="None" realm=""/>

<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true"/>

</security>

</binding>

</wsHttpBinding>

</bindings>

<client>

<endpoint address="http://localhost:3456/HelloWorldService.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_HelloWorldService" contract="SandBox.SharedClassLibrary.IHelloWorld" name="WSHttpBinding_HelloWorldService">

<identity>

<dns value="localhost"/>

</identity>

</endpoint>

</client>

</system.serviceModel>



Finally our GenericProxy class that creates the Channel that we use to call the service:


public class GenericProxy<T> : System.ServiceModel.ClientBase<T> where T : class

{

T _serviceInstance;

public GenericProxy(string endpointName)

: base(endpointName)

{

}

public GenericProxy(Binding ServiceBinding, EndpointAddress ServiceEndpointAddress)

: base(ServiceBinding, ServiceEndpointAddress)

{

}

public new T Channel

{

get

{

if (_serviceInstance == null)

{

//this.ChannelFactory.Credentials.UserName.UserName = "user";

//this.ChannelFactory.Credentials.UserName.Password = "123456";

_serviceInstance = this.ChannelFactory.CreateChannel();

}

return _serviceInstance;

}

}

}

Tuesday, July 15, 2008

My client doesn't allow the use of Foreign Key Constraints in SQL Server

I'm working on a project at a Fortune 500 Microsoft customer and ran across something interesting: They DO NOT allow foreign key constraints in production SQL Server databases. The theory being that they have negative performance impact, and that developers can check for related data as needed in stored procedure code.

This is new to me, but I wanted to double check with some SQL Gurus before I call their baby ugly... :-)

Here is a great response from my friend Brian:

"Their baby is UGLY! But you really can't tell a parent that!

They are correct, you do take a noticeable hit ( seen up to 5% on heavily updated DB's)
In particular you can see a lot locking and deadlocks.

However I've yet to see development staff that consistently check the data or deal properly with errors and/or partial rollbacks! I'll put money on the fact they have corrupt data!

I like FK's because they 'document' the DB and I use them to automate data archiving.

That said, I am in the process of converting my current app to ignore FK relationships.
(We do 24,000,000 + insert/update/deletes daily so performance is an issue)

I define relationships in the DB.
Enable then in Dev and Test to facilitate testing.
Disable them in perf and prod for performance

Once a day/week check the FK's. Not much more expensive than DBCC CheckDB
IF we find corrupt data, we enable the FK so we throw errors to facilitate route cause analysis.
Note this happens regularly after a code release ;) A little less often when new customer/data patterns hit the DB.

While this is not a 'Best Practice' it is a better than what they are doing today. "


Example code:

-- Create the tables and populate with valid data
use tempdb
go

if object_id('child') is not null drop table child
if object_id('parent') is not null drop table parent
go

create table parent (id int primary key)
create table child (id int primary key, pid int)
alter table child with nocheck add constraint FK_child_parent foreign key ( pid) references parent(id) -- Use 'with nocheck' so FK can be added even if existing data is corrupt
go

insert into parent values (1)
insert into parent values (3)
insert into child values (1, 1)
insert into child values (3, 3)
go

-- Try adding invalid data
insert into child values (2, 2)
go

-- Disable FK and try again
alter table child nocheck constraint FK_child_parent
insert into child values (2, 2)

-- Check the table
DBCC checkconstraints (FK_child_parent)

-- Enable the FK so code now throws errors
alter table child with nocheck check constraint FK_child_parent -- Use 'with nocheck' so existing corrupt data is ignored, but new data will error
insert into child values (2, 2)