Exposing lookup interface for MemoryCache in a windows service - c#

Using .net 4.6, I have a windows service which has a timer that wakes up everyday at a configured time, connects to a remote database and caches some data in memory using the MemoryCache class:
string id = rec.ID;
string surname = rec.Surname;
string dateOfBirth = rec.DateOfBirth;
string agreement = rec.Agreement;
CachedData cd = new CachedData(id, surname, dateOfBirth, agreement);
MemoryCache.Default.Set(id, cd, new DateTimeOffset(DateTime.Now.AddDays(1)));
I need to expose a lookup interface on this MemoryCache. I have a separate WCF service which exposes a lookup interface but I don't know how to communicate the lookup request/result between the WCf service and windows service so if I get an id in the WCf service:
string id = HttpContext.Current.Request.QueryString.Get("id");
I could pass it on to the windows service which would look it up in memory cache and return a result.
I followed https://msdn.microsoft.com/en-us/library/ff649818.aspx to host a wcf service in a windows service. At the end I just added the following to the OnStart() method of the Service1.cs class of my WindowsService:
MemoryCache.Default.Set("K", "Hello World", new DateTimeOffset(DateTime.Now.AddDays(1)));
and assuming that because the WCF service (also called Service1.cs) is now hosted by the windows service and will share the same app domain, I modified the default GetData method in the WCF as follows:
public string GetData(int value)
{
var kv = MemoryCache.Default["K"] as string;
if (kv != null)
{
return kv;
}
else
{
return string.Format("Entered: {0}", value);
}
}
However when I use the test client and call the GetData it can't find the cached item in the MemoryCache.

You cannot get to MemoryCache instance from another process that hosts your WCF service.
Can you host WCF service inside Windows Service? In that case it won't be any issue to use MemoryCahce instance updated by timer in WCF service.

I would go for a (persistent) cache outside your application, that way you can have multiple instance, make deploys and so on without losing your cached data (what happens if your windows services goes tits up within those 24h today?). Memcached and Redis are the first ones that I think of.

Related

Call WCF Service from another decoupled WCF service on the same site without HttpClient

I have a plugin model architecture that creates my Restful WCF services.
(It will be a couple years before we move to Web Api from WCF so, moving to Web Api isn't exactly a solution.)
I have decoupled WCF Microservices that don't reference each other.
EntityAWebService
EntityBWebService
EnityAWebService knows that a service EntityBWebService exists from a configuration, but doesn't reference it.
EntityAWebService and EntityBWebService are plugins. As such, they could be on loaded on the same site.
EntityAWebService makes a call to EntityBWebService using configuration information. The EntityBWebService could be on the same server or a different server.
- If on a different server, the code will continue to use HttpClient.
- If on the same server, go cract the message and send it through the channel without going through HttpClient, operating system's network, and IIS.
Below is the architecture. The orange is what I want to create.
Using HttpClient means EntityAWebService sends a message that is going to hit the operating systems network layer and go through IIS. Neither of which is necessary. It causes performance issues, and as the Entity plugins increase, so does the number of sockets and even using a singleton httpclient, the sockets are leaking.
The orange in the architecture is what doesn't exist yet.
The code knows the Url to call for Entity B Web Service, the message content, and the headers. How would I simulate, in the code represented by the orange box, what IIS does to forward the call through the behaviors and to the Endpoint?
FYI, my current project is too complex to post, so I will create a sample and post it soon.
Sample project: https://github.com/rhyous/DecoupledWcfServices
Turns out I didn't need to use named pipes. However, investigating how to use named pipes taught me what I needed to know. I just needed to use reflection and ChannelFactory. As the ChannelFactory for IIS hosting already exists, named pipes would be redundant.
Example Project here: https://github.com/rhyous/DecoupledWcfServices
And the appropriate snippet (the meat of the solution) is below.
using System;
using System.Collections.Specialized;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace DecoupledWcfServices
{
/// <summary>
/// Service 1 and Service 2 are in the same namespace in this project
/// </summary>
public class MessageBus
{
public string CallOtherWcfService(string url, object content, NameValueCollection headers)
{
var service = GetServiceName(url);
try
{
var netPipeUrl = $"http://localhost:54412/{service}/{service}.svc";
var serviceContractType = typeof(IService2);
var genericChannelFactoryType = typeof(WebChannelFactory<>).MakeGenericType(serviceContractType);
var binding = new WebHttpBinding();
var channelFactory = Activator.CreateInstance(genericChannelFactoryType, binding, new Uri(netPipeUrl)) as WebChannelFactory<IService2>; // I actually won't know it is an IService2 in my project, but getting this far should be enough
var proxy = channelFactory.CreateChannel() as IService2;
using (new OperationContextScope((IContextChannel)proxy))
{
var task = proxy.GetData("some data"); // Might need more work here to know which method to call based on the Url
task.Wait();
return task.Result; // Serialized JSON
}
}
catch (Exception)
{
throw;
}
}
internal string GetServiceName(string url)
{
var index = url.IndexOf(".svc");
var sub = url.Substring(0, index);
index = sub.LastIndexOf("/") + 1;
var sub2 = url.Substring(index, sub.Length - index);
return sub2;
}
}
}

Web services and client DLL

I have a web service and a client DLL. The web service uses an Oracle database.
For testing the client DLL, I copied the web service and made it point to a test database. Then I copied the client DLL and added this test web service using "Add web reference".
What I would like to do is to use one web service and one client DLL but be able to tell the client DLL to use either use the test or production database rather than two identical web serivces and client DLLs.
Edit
I mis-stated the issue. What I need to do is use one client DLL and two web services (one production version, one development/test version) and be able to, somehow, tell the client DLL which web services to use.
This is a sample of how the web service, client DLL and client app are used:
public class DSSService : System.Web.Services.WebService
{
public DSSService()
{
}
[WebMethod(MessageName = "GetFacility", BufferResponse=true, Description = "blah.")]
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
Facility oFacility = ...;
...
return oFacility;
}
....
}
Client DLL:
namespace DSSConfig
{
string sWSURL;
public class Config
{
public Config()
{
}
public void SetWSURL(string sURL)
{
sWSURL = sURL;
}
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
DSSService Proxy = new DSSService();
proxy.Url = sWSURL;
Facility oFacility = Proxy.GetFacility(sFDBID, sZip, sFinNo);
return oFacility;
}
In client application, having DSSConfig DLL as reference:
DSSConfig oConfig = new DSSConfig();
oConfig.SetWSURL("http://myserver/WebService1/service.asmx");
oConfig.GetFacility("blah", "blah", "blah");
What you need to do is change the WEB Service to take a parameter that it will use to construct the connection string to the DB.
Then change client DLL to pass that parameter as part of the call or connection.
Then you can configure the Client DLL to using any technique you like to pass the parameter. My suggestion is perhaps derive a class from the generated proxy in the client DLL and use this in the client code.
Without specific implementation details I can't be more precise.

How to maintain Session in WCF Service

I have a WCF Service in which I want to maintain session for my Authentication method.
I have gone through from various articles and applied some of the below changes which are required to maintain session in WCF Service, as WCF not supported Session by default.
1- [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] in svc file.
2- [ServiceContract(SessionMode = SessionMode.Allowed)] in ServiceContract
3- Use the wsHttpBinding as basicHttpBinding not supported Session.
I am using WCFTestClient to call my service. I have checked the config of my TestClient and it is using basicHttpBinding, here is the cause of issue.
I am unable to implement the 3 point in my Service webconfig and also unable to change the config of my TestClient. Can anyone please guide me. Thanks
To solve this I implemented my own SessionHandler within the service.
a thread safe singleton class containing a Dictionary<Guid, SessionData>
Service Method: Guid RegisterClient(ClientName clientName) { /* add client to session */ }
Service Method: bool UnregisterClient(Guid clientGuid) { /* remove client from session */ }
Service Method: void DoThisOnServer(Guid clientGuid) { /* the service functionality */}
void CheckTimeout() { /* iterate over dictionary and remove out timed sessions */ }
Hints:
SessionData contains ClientName, TimeOfConnection, YourUsefulData
ClientName is a placeholder for IP-Adresse or some other initial identificator
Client has to register and all following operations are done only if the provided Guid exists in SessionHandler.

Do I need to change web service path everytime when I call it?

I am developing on Windows application in c# and I am using web server's web service in this Windows application.
The web service should be dynamic and I need to change it in the application.
I managed to do it by this code :
CallWebService.MyWS ws = new CallWebService.MyWS();
ws.Url = "new url";
This new url will be set as per client's web server url.
I am calling this web service (I mean web service functions) 20 to 25 times in my application.
Do I need to change this path everytime when I call it or for the first time will be ok ?
Use a fixed port number for your service and configure this url in your app/web.config file and use it in your code.
Create a helper class and use that. Make it configurable by using an app setting or better store in config table in database if you are using one.
If you are using WCF client, you can pass URL in client constructor. Otherwise create a partial class for your webservice to create that constructor.
public class MyWebServiceHelper
{
private string _url = null;
public MyWebServiceHelper()
{
this._url = GetWsUrlFromDbOrAppConfig();
}
public CallWebService.MyWS GetMyWebServiceProxy()
{
return new CallWebService.MyWS("WcfBindingConfig", _url);
}
}

Error in access to Entity Framework data from WCF service in web and win form application?

I am in the middle of big web application, I use Entity Framework as my data service, now we need some windows application to work with our data, so I want to give them a service with WCF
But when my client wants to get service some error is happened from my public property which I use for caching Entity Model
public partial class DepositEntities : ObjectContext
{
public static DepositEntities Current
{
get
{
DepositEntities oc =
HttpContext.Current.Items["ObjectContext"] as DepositEntities;
if (oc == null)
{
oc = new DepositEntities();
HttpContext.Current.Items["ObjectContext"] = oc;
}
return oc;
}
}
}
I know the problem is from this line, after I debug my code
DepositEntities oc = System.Web.HttpContext.Current.Items["ObjectContext"] as DepositEntities;
When I change my Current property body to some thing like this
public static DepositEntities Current
{
get
{
DepositEntities oc = new DepositEntities();
return oc;
}
}
everything is OK when I get data from services I have no problem
But everywhere I have join in my codes I have problem because It thinks there are different data source because of new DepositEntities();
You're most likely experiencing problems because WCF doesn't have HttpContext.Current. Read more about contexts in WCF - this question may be a good start: http://social.msdn.microsoft.com/Forums/en/wcf/thread/27896125-b61e-42bd-a1b0-e6da5c23e6fc.
I also think it would be better for you to manage lifetime of an ObjectContext with a DI Container (ie. Castle Windsor). Thanks to this, it won't be necessary to expose static property Current which is a problem for WCF service, unit tests, etc.
Check out "Hosting WCF Services in ASP.NET Compatibility Mode" in wcf service and ASP.NET. It explains how to get a valid HttpContext in a wcf service.

Categories

Resources