We are working on a large project right now which I believe is similar to what you are working on. We have many users who will be accessing this app from their desktop PC's as well as their mobile devices. I designed a service layer that is very flexible that delivers optimal performance depending on if the user is local or remote (note that VPN = local).
I cannot give you every detail due to lack of space but here are the big pieces:
Create three Visual studio projects (or one solution with three projects): 1) Your app, 2) A service project (.dll), 3) A WCF project.
Your service project is where the action is.
In your service project, create an interface called something like IMyServices (this is standard WCF stuff):
[ServiceContract]
public interface IMyServices : IDisposable
{
[OperationContract]
IEnumerable<Allocation> GetAllocations();
}
Next, add a class like what you see below. I call it ServiceRouter because if the user is remote, it routes the request to WCF but if the user is local it just gets data using ADO over the LAN. Note that this class implments IMyServices.
public class ServiceRouter : IMyServices
{
private readonly string ServiceURI;
/// <summary>
/// Routes data requests over the LAN if the client is connected locally or over a WCF service if the client is remote. Use this constructor to route data requests over the LAN.
/// </summary>
/// http://msdn.microsoft.com/en-us/library/ms735103.aspx
///
public ServiceRouter()
{
ServiceURI = null;
}
/// <summary>
/// Routes data requests over the LAN if the client is connected locally or over a WCF service if the client is remote.
/// </summary>
/// <param name="serviceURI">Fully qualified URI of a WCF server if the user is remote. Pass null if the user authenticated on the LAN (including using VPN)</param>
/// http://msdn.microsoft.com/en-us/library/ms735103.aspx
///
public ServiceRouter(string serviceURI)
{
ServiceURI = serviceURI;
}
public IEnumerable<Allocation> GetAllocations()
{
IMyServices client = GetClient();
var result = client.GetAllocations().ToList();
CloseClient(client);
return result;
}
#region WCFClient
private IMyServices GetClient()
{
IMyServices _client;
if (string.IsNullOrEmpty(ServiceURI))
_client = new MYServices();
else
{
_client = ChannelFactory<IMyServices>.CreateChannel(new BasicHttpBinding(), new EndpointAddress(ServiceURI));
((ICommunicationObject)_client).Open();
}
return _client;
}
private void CloseClient(IMyServices client)
{
ICommunicationObject wcfClient = client as ICommunicationObject;
if (wcfClient != null)
{
if (wcfClient.State == CommunicationState.Faulted)
wcfClient.Abort();
else
wcfClient.Close();
}
else
((IDisposable)client).Dispose();
}
#endregion
}
Next, in your service project, create a class for your services that implements IMyServices like this:
internal partial class MyServices : IMyServices
{
public IEnumerable<Allocation> GetAllocations()
{
// access your db here
}
Now here is how you you expose your services using WCF. You will need to configure your web.config and you will need to reference the .dll file from your service project.
In your WCF project add a WCF service like what you see below. Note this class inherits from ServiceRouter, which implements IMyService. The code below is the ONLY code in the WCF project! All this code does is create an instance of your ServiceRouter, passing it a null uri which tells it to get its data over the LAN. Your WCF server and you DB server need to be able to communicate over the LAN for this to work of course.
public class MyWCFService : MyServiceProject.ServiceRouter
{
public MyWCFService() : base()
{
// Since the WCF server is running on the local area network, this class only needs to create an instance of
// the service router in local mode and retrive the requested data. WCF serializes the data and sends it
// back over the wire.
}
}
Here is a fragment of how your web.config might look:
<service name="MyWCFService" behaviorConfiguration="xxx">
<endpoint address="" binding="basicHttpBinding" bindingNamespace="http://blah.com" contract="MyServiceProject.IMyServices">
In your app project, add a reference to your service .dll file. Look at your user's IP address and if it is local, use create instances of ServiceRouter passing null to the constructor. If the user is remote, pass the URI of your wcf server when you create insances of Service router: i.e. ServiceRouter router = new ServiceRouter(myServerName);