The MSDN documentation says
The Framework caches SSL sessions as they are created and attempts to reuse a cached session for a new request, if possible. When attempting to reuse an SSL session, the Framework uses the first element of ClientCertificates (if there is one), or tries to reuse an anonymous sessions if ClientCertificates is empty.
How can I disable this caching?
At the moment I am experiencing a problem with a reconnect to a server (i.e., the first connection works good, but at attempt to reconnect the servers breaks the session). Restarting the application helps (but of course only for the first connection attempt). I assume the problem root is caching.
I've checked the packets with a sniffer, the difference is at just single place only at Client Hello messages:
First connection to the server (successful):
Second connection attempt (no program restart, failed):
The difference seems to be just the session identifier.
P.S. I'd like to avoid using 3rd-party SSL clients. Is there a reasonable solution?
This is a translation of this question from ru.stackoverflow
Caching is handled inside SecureChannel - internal class that wraps SSPI and used by SslStream. I don't see any points inside that you can use to disable session caching for client connections.
You can clear cache between connections using reflection:
var sslAssembly = Assembly.GetAssembly(typeof(SslStream));
var sslSessionCacheClass = sslAssembly.GetType("System.Net.Security.SslSessionsCache");
var cachedCredsInfo = sslSessionCacheClass.GetField("s_CachedCreds", BindingFlags.NonPublic | BindingFlags.Static);
var cachedCreds = (Hashtable)cachedCredsInfo.GetValue(null);
cachedCreds.Clear();
But it's very bad practice. Consider to fix server side.
So I solved this problem a bit differently. I really didn't like the idea of reflecting out this private static method to dump the cache because you don't really know what you're getting into by doing so; you're basically circumventing encapsulation and that could cause unforeseen problems. But really, I was worried about race conditions where I dump the cache and before I send the request, some other thread comes in and establishes a new session so then my first thread inadvertently hijacks that session. Bad news... anyway, here's what I did.
I stopped to think about whether or not there was a way to sort of isolate the process and then an Android co-worker of mine recalled the availability of AppDomains. We both agreed that spinning one up should allow the Tcp/Ssl call to run, isolated from everything else. This would allow the caching logic to remain intact without causing conflicts between SSL sessions.
Basically, I had originally written my SSL client to be internal to a separate library. Then within that library, I had a public service act as a proxy/mediator to that client. In the application layer, I wanted the ability to switch between services (HSM services, in my case) based on the hardware type, so I wrapped that into an adapter and interfaced that to be used with a factory. Ok, so how is that relevant? Well it just made it easier to do this AppDomain thing cleanly, without forcing this behavior any other consumer of the public service (the proxy/mediator I spoke of). You don't have to follow this abstraction, I just like to share good examples of abstraction whenever I find them :)
Now, in the adapter, instead of calling the service directly, I basically create the domain. Here is the ctor:
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vcrypt_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
All this does is creates a named domain from which my actual service will run in isolation. Now, most articles that I came across on how to actually execute within the domain sort of over-simplify how it works. The examples typically involve calling myDomain.DoCallback(() => ...); which isn't wrong, but trying to get data in and out of that domain will likely become problematic as serialization will likely stop you dead in your tracks. Simply put, objects that are instantiated outside of DoCallback() are not the same objects when called from inside of DoCallback since they were created outside of this domain (see object marshalling). So you'll likely get all kinds of serialization errors. This isn't a problem if running the entire operation, input and output and all can occur from inside myDomain.DoCallback() but this is problematic if you need to use external parameters and return something across this AppDomain back to the originating domain.
I came across a different pattern here on SO that worked out for me and solved this problem. Look at _rklServiceRuntime = in my sample ctor. What this is doing is actually asking the domain to instantiate an object for you to act as a proxy from that domain. This will allow you to marshall some objects in and out of it. Here is my implemenation of IRklServiceRuntime:
public interface IRklServiceRuntime
{
RklResponse Run(RklRequest request, string hostname, int port, Guid clientId, IHsmLogger logger);
}
public class VCServiceRuntime : MarshalByRefObject, IRklServiceRuntime
{
public RklResponse Run(
RklRequest request,
string hostname,
int port,
Guid clientId,
IHsmLogger logger)
{
Ensure.IsNotNull(request, nameof(request));
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
// these are set here instead of passed in because they are not
// serializable
var clientCert = ApplicationValues.VCClientCertificate;
var clientCerts = new X509Certificate2Collection(clientCert);
using (var client = new VCServiceClient(hostname, port, clientCerts, clientId, logger))
{
var response = client.RetrieveDeviceKeys(request);
return response;
}
}
}
This inherits from MarshallByRefObject which allows it to cross AppDomain boundaries, and has a single method that takes your external parameters and executes your logic from within the domain that instantiated it.
So now back to the service adapter: All the service adapters has to do now is call _rklServiceRuntime.Run(...) and feed in the necessary, serializable parameters. Now, I just create as many instances of the service adapter as I need and they all run in their own domain. This works for me because my SSL calls are small and brief and these requests are made inside of an internal web service where instancing requests like this is very important. Here is the complete adapter:
public class VCRklServiceAdapter : IRklService
{
private readonly string _hostname;
private readonly int _port;
private readonly IHsmLogger _logger;
private readonly AppDomain _instanceDomain;
private readonly IRklServiceRuntime _rklServiceRuntime;
public Guid ClientId { get; }
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vc_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
public RklResponse GetKeys(RklRequest rklRequest)
{
Ensure.IsNotNull(rklRequest, nameof(rklRequest));
var response = _rklServiceRuntime.Run(
rklRequest,
_hostname,
_port,
ClientId,
_logger);
return response;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
AppDomain.Unload(_instanceDomain);
}
}
Notice the dispose method. Don't forget to unload the domain. This service implements IRklService which implements IDisposable, so when I use it, it used with a using statement.
This seems a bit contrived, but it's really not and now the logic will be run on it's own domain, in isolation, and thus the caching logic remains intact but non-problematic. Much better than meddling with the SSLSessionCache!
Please forgive any naming inconsistencies as I was sanitizing the actual names quickly after writing the post.. I hope this helps someone!
Related
We are in the process of migrating from Dynamics on-prem to Dynamics 365 online. So we had to change also a webapp that is connecting to Dynamics.
The webapp is still working but the performance is poor. We started to do some traces to see why. In this trace we saw that the app is doing for a single request different calls to Dynamics online. Reason for this is that we are indeed retrieving different sets of data. But we are surprised to see that authentication is done also multiple times. This authentication is slowing down the response from dynamics. We were expecting that authentication was done only with the first request.
The code I'm using to make the connection is in an abstract class that is instantiated by different other classes. This classes are using the following property returning a CrmServiceClient from the namespace Microsoft.Xrm.Tooling.Connector:
protected CrmServiceClient CustomCrmServiceProxy
{
get
{
CrmServiceClient client = new CrmServiceClient(CrmConnection.Connectionstring);
client.CallerId = GetCallerId();
return client;
}
}
The connectionstring is AuthType=ClientSecret;url={0};ClientId={1};ClientSecret={2}; with the values replaced.
In the classes using the abstract class we are calling the property like
var data = this.CustomCrmServiceProxy.RetrieveMultiple("fetch xml");
Important is that we are passing the callerid to the CrmServiceClient and this one can be different when the visitor switch to a page in another language.
Is there a way to prevent the multiple authentication?
Is implementing a singleton pattern an option? But what with the different callerids in that case? And is there maybe a good example for CrmServiceClient?
The CrmServiceClient offers a number of constructors giving the opportunity to reuse a connection instance.
E.g. the following constructor overload has parameter useUniqueInstance. Pass a false value to it when a cached instance should be used.
public CrmServiceClient(string userId, SecureString password, string domain, string homeRealm, string hostName, string port, string orgName, bool useUniqueInstance = false, bool useSsl = false, OrganizationDetail orgDetail = null)
In most cases however a web app will be consumed by multiple clients, often concurrently, and a singleton connection object would not scale well in those scenarios. In these cases you can introduce a connection pool.
A connection pool class would be responsible for maintaining a collection of CrmServiceClient instances. A factory pattern could be used to claim instances from the pool object. When disposed, the factory instance returns all claimed instances to the pool.
Your factory class could implement the existing IOrganizationServiceFactory. This interface is designed with impersonation requirements in mind.
Note: do not make classes that currently use CrmServiceClient connections responsible for creating these instances. Instead inject IOrganizationServiceFactory or IOrganizationService objects into the constructors of these classes.
Can you try something like this?
This would create a single instance of the connection (with the callerID) and then just continually refer to that instance.
CrmServiceClient _client = null;
protected CrmServiceClient CustomCrmServiceProxy
{
get
{
if (_client == null)
{
_client = new CrmServiceClient(CrmConnection.Connectionstring);
_client.CallerId = GetCallerId();
}
return _client;
}
I have seen situations where a program executes a get accessor more times than I think it should.
Rather than having it as a property, I'd say try making it a method.
protected CrmServiceClient GetService()
{
var client = new CrmServiceClient(CrmConnection.Connectionstring);
client.CallerId = GetCallerId();
return client;
}
Then, one option for lazy instantiation would be:
private CrmServiceClient _svc;
private CrmServiceClient svc => _svc ?? (_svc = GetService());
I know that, when using the Microsoft dependency injection container, the best practice to handle HttpClient instances is using the IHttpClientFactory interface provided by the Microsoft.Extensions.Http nuget package.
Unfortunately the classes implementing the IHttpClientFactory interface are not public (as you can verify here), so the only way to exploit this pattern is using the Microsoft dependency injection container (at least it's the only one that I know). Sometimes I need to maintain old applications using a different container, so I need to figure out a best practice even when the IHttpClientFactory approach cannot be used.
As explained in this famous article and confirmed in the Microsoft docs too the HttpClient class is designed to be instantiated once per application lifetime and reused across multiple HTTP calls. This can safely be done because the public methods used to issue HTTP calls are documented to be thread safe, so a singleton instance can be safely used. In this case, it is important to follow the tips given in this article in order to avoid issues related with DNS changes.
So far so good.
Sometimes it is handy to use properties like BaseAddress or DefaultRequestHeaders, which are not thread safe (at least, they are not documented to be thread safe, so I assume they are not) to configure the HttpClient instance.
This opens a question: what happens if I have a singleton HttpClient instance and somewhere in my code I use the property DefaultRequestHeaders to set some common HTTP request headers useful to call one of the host my application needs to communicate with ? This is potentially dangerous, because different hosts could require different values for the same request header (think of authentication as an example of that). Furthermore, modifying DefaultRequestHeaders concurrently from two threads could potentially mess up the internal state of the HttpClient instance, because of the lack of thread safety guarantees.
For all these reasons, I think that the best approach to use HttpClient (when IServiceCollection is not available) is the following:
create one instace of HttpClient for each host the application
needs to communicate with. Every call to one specific host will
then use the same instance of HttpClient. Concurrent calls to the
same host are safe, because of the documented thread safety of
methods used to perform calls.
create one service for each host the application needs to
communicate with. The HttpClient instance is injected inside this
service and the service itself is used as a singleton in the
application. This service is used to abstract away the access to the
host it is coupled with. Classes like this are fully testable as illustrated here.
the only point where instances of HttpClient are created and configured is the composition root of the application. The code in the composition root is single threaded, so it is safe to use properties like DefaultRequestHeaders to configure the HttpClient instances.
Do you see any problem in creating one instance of HttpClient per host to be called ?
I know that instantiating one HttpClient per request can lead to socket exhaustion and must be avoided, but I guess that having one instance per host is safe with regard to this problem (because the same instance is used for all the requests to the same host and I do not expect that a single application needs to talk with a large number of different hosts).
Do you agree ? Am I missing anything ?
I know that, when using the Microsoft dependency injection container, the best practice to handle HttpClient instances is using the IHttpClientFactory interface provided by the Microsoft.Extensions.Http nuget package.
Correct.
Unfortunately the classes implementing the IHttpClientFactory interface are not public (as you can verify here), so the only way to exploit this pattern is using the Microsoft dependency injection container (at least it's the only one that I know). Sometimes I need to maintain old applications using a different container, so I need to figure out a best practice even when the IHttpClientFactory approach cannot be used.
Microsoft.Extensions.DependencyInjection ("MEDI") should be thought of a (simplistic) abstraction over multiple DI systems - it just so happens to come with its own basic DI container. You can use MEDI as a front for Unity, SimpleInject, Ninject, and others.
As explained in this famous article and confirmed in the Microsoft docs too the HttpClient class is designed to be instantiated once per application lifetime and reused across multiple HTTP calls.
Not exactly.
You don't want a singleton HttpClient used by all consumers of HttpClient in your application because different consumers might have different assumptions about (as you later point out) DefaultRequestHeaders and other HttpClient state. Some code may also assume that HttpClient is not using any DelegatingHandler instances either.
You also don't want any instances of HttpClient (created using its own parameterless constructor) with an unlimited lifetime because of how its default internal HttpClientHandler handles (or rather, doesn't handle) DNS changes. Hence why the default IHttpClientFactory imposes a lifetime limit of 2 minutes for each HttpClientHandler instance.
This opens a question: what happens if I have a singleton HttpClient instance and somewhere in my code I use the property DefaultRequestHeaders to set some common HTTP request headers useful to call one of the host my application needs to communicate with?
What happens? What happens is what you can expect: different consumers of the same HttpClient instance acting on wrong information - such as sending the wrong Authorization header to the wrong BaseAddress. This is why HttpClient instances should not be shared.
This is potentially dangerous, because different hosts could require different values for the same request header (think of authentication as an example of that). Furthermore, modifying DefaultRequestHeaders concurrently from two threads could potentially mess up the internal state of the HttpClient instance, because of the lack of thread safety guarantees.
This isn't necessarily a "Thread safety" issue - you can have a single-threaded application that abuses a singleton HttpClient this way and still have the same issue. The real issue is that different objects (the consumers of HttpClient) are assuming that they are the owner of the HttpClient when they aren't.
Unfortunately C# and .NET do not have a built-in way to declare and assert ownership or object lifetimes (hence why IDisposable is a bit of a mess today) - so we need to resort to different alternatives.
create one instace of HttpClient for each host the application needs to communicate with. Every call to one specific host will then use the same instance of HttpClient. Concurrent calls to the same host are safe, because of the documented thread safety of methods used to perform calls.
(By "host" I assume you mean HTTP "origin"). This is naive and won't work if you make different requests to the same service with different access-tokens (if the access-tokens are stored in DefaultRequestHeaders).
create one service for each host the application needs to communicate with. The HttpClient instance is injected inside this service and the service itself is used as a singleton in the application. This service is used to abstract away the access to the host it is coupled with. Classes like this are fully testable as illustrated here.
Again, don't think of HTTP services in terms of "hosts" - otherwise this has the same problem as above.
the only point where instances of HttpClient are created and configured is the composition root of the application. The code in the composition root is single threaded, so it is safe to use properties like DefaultRequestHeaders to configure the HttpClient instances.
I'm not sure how this helps either. Your consumers might be stateful.
Anyway, the real solution, imo, is to implement your own IHttpClientFactory (it can also be your own interface!). To simplify things, your consumers' constructors won't accept a HttpClient instance, but instead accept the IHttpClientFactory and call its CreateClient method in order to get their own privately-owned and stateful instance of HttpClient which then uses the pool of shared and stateless HttpClientHandler instances.
Using this approach:
Each consumer gets its own private instance of HttpClient that they can alter as they like - no worries about objects modifying instances that they don't own.
Each consumer's HttpClient instance does not need to be disposed - you can safely disregard the fact they implement IDisposable.
Without pooled handlers, each HttpClient instance owns its own handler, which must be disposed.
But with pooled handlers, as with this approach, the pool manages handler lifetime and clean-up, not the HttpClient instances.
Your code can call HttpClient.Dispose() if it really wants to (or you just want to make FxCop shut-up) but it wont do anything: the underlying HttpMessageHandler (PooledHttpClientHandler) has a NOOP dispose method.
Managing the lifetime of HttpClient is irrelevant because each HttpClient only owns its own mutable state like DefaultRequestHeaders and BaseAddress - so you can have transient, scoped, long-life'd or singleton HttpClient instances and it's okay because they all dip into the pool of HttpClientHandler instances only when they actually send a request.
Like so:
/// <summary>This service should be registered as a singleton, or otherwise have an unbounded lifetime.</summary>
public QuickAndDirtyHttpClientFactory : IHttpClientFactory // `IHttpClientFactory ` can be your own interface. You do NOT need to use `Microsoft.Extensions.Http`.
{
private readonly HttpClientHandlerPool pool = new HttpClientHandlerPool();
public HttpClient CreateClient( String name )
{
PooledHttpClientHandler pooledHandler = new PooledHttpClientHandler( name, this.pool );
return new HttpClient( pooledHandler );
}
// Alternative, which allows consumers to set up their own DelegatingHandler chains without needing to configure them during DI setup.
public HttpClient CreateClient( String name, Func<HttpMessageHandler, DelegatingHandler> createHandlerChain )
{
PooledHttpClientHandler pooledHandler = new PooledHttpClientHandler( name, this.pool );
DelegatingHandler chain = createHandlerChain( pooledHandler );
return new HttpClient( chain );
}
}
internal class HttpClientHandlerPool
{
public HttpClientHandler BorrowHandler( String name )
{
// Implementing this is an exercise for the reader.
// Alternatively, I'm available as a consultant for a very high hourly rate :D
}
public void ReleaseHandler( String name, HttpClientHandler handler )
{
// Implementing this is an exercise for the reader.
}
}
internal class PooledHttpClientHandler : HttpMessageHandler
{
private readonly String name;
private readonly HttpClientHandlerPool pool;
public PooledHttpClientHandler( String name, HttpClientHandlerPool pool )
{
this.name = name;
this.pool = pool ?? throw new ArgumentNullException(nameof(pool));
}
protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken )
{
HttpClientHandler handler = this.pool.BorrowHandler( this.name );
try
{
return await handler.SendAsync( request, cancellationToken ).ConfigureAwait(false);
}
finally
{
this.pool.ReleaseHandler( this.name, handler );
}
}
// Don't override `Dispose(Bool)` - don't need to.
}
Then each consuimer can use it like so:
public class Turboencabulator : IEncabulator
{
private readonly HttpClient httpClient;
public Turboencabulator( IHttpClientFactory hcf )
{
this.httpClient = hcf.CreateClient();
this.httpClient.DefaultRequestHeaders.Add( "Authorization", "my-secret-bearer-token" );
this.httpClient.BaseAddress = "https://api1.example.com";
}
public async InverseReactiveCurrent( UnilateralPhaseDetractor upd )
{
await this.httpClient.GetAsync( etc )
}
}
public class SecretelyDivertDataToTheNsaEncabulator : IEncabulator
{
private readonly HttpClient httpClientReal;
private readonly HttpClient httpClientNsa;
public SecretNsaClientService( IHttpClientFactory hcf )
{
this.httpClientReal = hcf.CreateClient();
this.httpClientReal.DefaultRequestHeaders.Add( "Authorization", "a-different-secret-bearer-token" );
this.httpClientReal.BaseAddress = "https://api1.example.com";
this.httpClientNsa = hcf.CreateClient();
this.httpClientNsa.DefaultRequestHeaders.Add( "Authorization", "TODO: it's on a postit note on my desk viewable from outside the building" );
this.httpClientNsa.BaseAddress = "https://totallylegit.nsa.gov";
}
public async InverseReactiveCurrent( UnilateralPhaseDetractor upd )
{
await this.httpClientNsa.GetAsync( etc )
await this.httpClientReal.GetAsync( etc )
}
}
I just started to learn C# for a school project but I'm stuck on something.
I have a solution with 2 projects (and each project has a class), something like this:
Solution:
Server (project) (...) MyServerClass.cs, Program.cs
App (project) (...) MyAppClass.cs, Program.cs
In my "MyServerClass.cs", I have this:
class MyServerClass
{
...
public void SomeMethod()
{
Process.Start("App.exe", "MyAppClass");
}
}
How can I properly send, for example, an IP address and port? Would something like this work?
class MyServerClass
{
....
public void SomeMethod()
{
string ip = "127.0.0.1";
int port = 8888;
Process.Start("App.exe", "MyAppClass " + ip + " " + port);
}
}
Then in my "MyAppClass.cs", how can I receive that IP address and port?
EDIT:
The objective of this work is to practice processes/threads/sockets. The idea is having a server that receives emails and filter them, to know if they're spam or not. We got to have 4 or 5 filters. The idea was having them as separated projects (ex: Filter1.exe, Filter2.exe, ...), but I was trying to have only 1 project (ex: Filters.exe) and have the filters as classes (Filter1.cs, Filter2.cs, ...), and then create a new process for each different filter.
I guess I'll stick to a project for each filter!
Thanks!
There are a number of ways to achieve this, each with their own pros/cons.
Some possible solutions:
Pass the values in on the command line. Pros: Easy. Cons: Can only be passed in once on launch. Unidirectional (child process can't send info back). Doesn't scale well for complex structured data.
Create a webservice (either in the server or client). Connect to it and either pull/push the appropriate settings. Pros: Flexible, ongoing, potentially bi-directional with some form of polling and works if client/server are on different hosts. Cons: A little bit more complex, requires one app to be able to locate the web address of the other which is trivial locally and more involved over a network.
Use shared memory via a memory mapped file. This approach allows multiple processes to access the same chunk of memory. One process can write the required data and the others can read it. Pros: Efficient, bi-directional, can be disk-backed to persist state through restarts. Cons: Requires pointers and an understanding of how they work. Requires a little more manipulation of data to perform a read/write.
There are dozens more ways. Without knowing your situation in detail, it's hard to recommend one over another.
Edit Re: Updated requirements
Ok, command line is definitely a good choice here. A quick detour into some architecture...
There's no reason you can't do this with a single project.
First up, use an interface to make sure all your filters are interchangeable. Something like this...
public interface IFilter {
FilterResult Filter(string email);
void SetConfig(string config);
}
SetConfig() is optional but potentially useful to reconfigure a filter without a recompile.
You also need to decide what your IFilter's FilterResult is going to be. Is it a pass/fail? Or a score? Maybe some flags and other metrics.
If you wanted to do multiple projects, you'd put that interface in a "shared" or "common" project on its own and reference it from every other project. This also makes it easy for third parties to develop a filter.
Anyway, next up. Let's look at how the filter is hosted. You want something that's going to listen on the network but that's not the responsibility of the filter itself, so we need a network client. What you use here is up to you. WCF in one flavour or another seems to be a prime candidate. Your network client class should take in its constructor a network port to listen on and an instance of the filter...
public class NetworkClient {
private string endpoint;
private IFilter filter;
public NetworkClient(string Endpoint, IFilter Filter) {
this.filter = Filter;
this.endpoint = Endpoint;
this.Setup();
}
void Setup() {
// Set up your network client to listen on endpoint.
// When it receives a message, pass it to filter.Filter(msg);
}
}
Finally, we need an application to host everything. It's up to you whether you go for a console app or winforms/wpf. Depends if you want the process to have a GUI. If it's running as a service, the UI won't be visible on a user desktop anyway.
So, we'll have a process that takes the endpoint for the NetworkClient to listen on, a class name for the filter to use, and (optionally) a configuration string to be passed in to the filter before first use.
So, in your app's Main(), do something like this...
static void Main() {
try {
const string usage = "Usage: Filter.exe Endpoint FilterType [Config]";
var args = Environment.GetCommandLineArgs();
Type filterType;
IFilter filter;
string endpoint;
string config = null;
NetworkClient networkClient;
switch (args.Length) {
case 0:
throw new InvalidOperationException(String.Format("{0}. An endpoint and filter type are required", usage));
case 1:
throw new InvalidOperationException(String.Format("{0}. A filter type is required", usage));
case 2:
// We've been given an endpoint and type
break;
case 3:
// We've been given an endpoint, type and config.
config = args[3];
break;
default:
throw new InvalidOperationException(String.Format("{0}. Max three parameters supported. If your config contains spaces, ensure you are quoting/escaping as required.", usage));
}
endpoint = args[1];
filterType = Type.GetType(args[2]); //Look at the overloads here to control where you're searching
// Now actually create an instance of the filter
filter = (IFilter)Activator.CreateInstance(filterType);
if (config != null) {
// If required, set config
filter.SetConfig(config);
}
// Make a new NetworkClient and tell it where to listen and what to host.
networkClient = new NetworkClient(endpoint, filter);
// In a console, loop here until shutdown is requested, however you've implemented that.
// In winforms, the main UI loop will keep you alive.
} catch (Exception e) {
Console.WriteLine(e.ToString()); // Or display a dialog
}
}
You should then be able to invoke your process like this...
Filter.exe "127.0.0.1:8000" MyNamespace.MyFilterClass
or
Filter.exe "127.0.0.1:8000" MyNamespace.MyFilterClass "dictionary=en-gb;cutoff=0.5"
Of course, you can use a helper class to convert the config string into something your filter can use (like a dictionary).
When the network client gets a FilterResult back from the filter, it can pass the data back to the server / act accordingly.
I'd also suggest a little reading on Dependency Injection / Inversion of control and Unity. It makes a pluggable architecture much, much simpler. Instead of instantiating everything manually and tracking concrete instances, you can just do something like...
container.Resolve<IFilter>(filterType);
And the container will make sure that you get the appropriate instance for your thread/context.
Hope that helps
I am developing server side logic to process requests and respond with data to front-end server as well as to mobile app direct connections.
I have implemented SessionContext class that basically ensures, that there is matching session record in DB for every service that is called (with exception for forgot password cases, etc).
I am now trying to implement event logging. I want to have common logic so I may log all requests, exceptions, data, etc.
I have come up with this code, but somehow I don't feel good about it - too much code for each service method. Are there any clever tricks that I might implement to make it shorter and easier to read/code?
Current implementation would use EventLogic class to log event to event table. At some point some events might be related to session so I am passing eventLog as parameter to SessionContext (to create link between event and session). SessionContext saves entity data on successful dispose... i have a gut feeling that something is wrong with my design.
public Session CreateUser(string email, string password, System.Net.IPAddress ipAddress)
{
using (var eventLog = new EventLogic())
{
try
{
eventLog.LogCreateUser(email, password, ipAddress);
using (var context = SessionContext.CreateUser(eventLog, email, password, ipAddress))
{
return new Session()
{
Id = context.Session.UId,
HasExpired = context.Session.IsClosed,
IsEmailVerified = context.Session.User.IsEmailVerified,
TimeCreated = context.Session.TimeCreated,
PublicUserId = CryptoHelper.GuidFromId(context.Session, context.Session.UserId, CryptoHelper.TargetTypeEnum.PublicUser),
ServerTime = context.Time
};
}
}
catch (Exception e)
{
eventLog.Exception(e);
}
}
}
You should consider using something like SLF4J + LogBack (for example) for logging.
If your classes follow SRP, you should have not more than one call type like LogCreateUser per your application. And that means, there is no need to extract logging logic into a new class.
A WCF service will consume another Wcf service. Now, i want to create channel factory object and cache it manually. I know performance will be good but concern any other issue will be raised or not.
I have found info as follows:
"Using ChannelFactory you can still achieve channel factory caching with your own custom MRU cache. This still implies an important restriction: calls to the same service endpoint that share the channel factory must also share the same credentials. That means you can t pass different credentials for each thread calling application services from the Web server tier. One scenario where this is not an issue is if you use the same certificate or Windows credential to authenticate to downstream services. In this case, if you need to pass information about the authenticated user, you can use custom headers rather than a security token."
Link: http://devproconnections.com/net-framework/wcf-proxies-cache-or-not-cache
I have found a sample code in Google as follows.
internal delegate void UseServiceDelegate<in T>(T proxy);
internal static class Service<T>
{
private static readonly IDictionary<Type, string>
cachedEndpointNames = new Dictionary<Type, string>();
private static readonly IDictionary<string, ChannelFactory<T>>
cachedFactories =
new Dictionary<string, ChannelFactory<T>>();
internal static void Use(UseServiceDelegate<T> codeBlock)
{
var factory = GetChannelFactory();
var proxy = (IClientChannel)factory.CreateChannel();
var success = false;
try
{
using (proxy)
{
codeBlock((T)proxy);
}
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
private static ChannelFactory<T> GetChannelFactory()
{
lock (cachedFactories)
{
var endpointName = GetEndpointName();
if (cachedFactories.ContainsKey(endpointName))
{
return cachedFactories[endpointName];
}
var factory = new ChannelFactory<T>(endpointName);
cachedFactories.Add(endpointName, factory);
return factory;
}
}
private static string GetEndpointName()
{
var type = typeof(T);
var fullName = type.FullName;
lock (cachedFactories)
{
if (cachedEndpointNames.ContainsKey(type))
{
return cachedEndpointNames[type];
}
var serviceModel =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
.SectionGroups["system.serviceModel"] as ServiceModelSectionGroup;
if ((serviceModel != null) && !string.IsNullOrEmpty(fullName))
{
foreach (var endpointName in
serviceModel.Client.Endpoints.Cast<ChannelEndpointElement>()
.Where(endpoint => fullName.EndsWith(endpoint.Contract)).Select(endpoint
=> endpoint.Name))
{
cachedEndpointNames.Add(type, endpointName);
return endpointName;
}
}
}
throw new InvalidOperationException("Could not find endpoint element
for type '" + fullName + "' in the ServiceModel client
configuration section. This might be because no configuration file
was found for your application, or because no endpoint element
matching this name could be found in the client element.");
}
}
I am totally confused what should i do. Can anyone give me a best practice guideline?
This is a complex topic with a lot of details to go over, but here it goes.
First, as a general rule you should be caching a ChannelFactory and not an individual Channel. A ChannelFactory is expensive to construct as well as thread-safe so it is a great candidate for caching. A Channel is cheap to construct and it is generally recommended to only create channels on an as-needed basis and to close them as early as possible. Additionally, when you cache a Channel then you have to worry about it timing out which will cause it to fault which invalidates the entire benefit of caching it in the first place.
The article you linked to by Michele Leroux Bustamante is one of the best resources out there. As she states, there are differences to consider between Windows clients and server-side clients. Mostly only Windows clients benefit from caching as typically the credentials differ from thread to thread on server-side clients. For your typical Windows clients, there are two main options: Caching the references yourself or leveraging the MRU cache.
Leveraging the MRU cache: Essentially this means that you are letting Microsoft take the wheel. The ClientBase class will use an MRU cache for the internal ChannelFactory instance. The caching behavior is controlled via a CacheSetting property and by default caching will be disabled if any of the "security-sensitive" properties are accessed. ClientBase properties which will invalidate and remove a ChannelFactory from the MRU cache when accessed include the Endpoint, ClientCredentials or the ChannelFactory itself. There is a way to override this behavior by setting the CacheSettings property to CacheSettings.AlwaysOn. Additionally, if the Binding is run-time defined then the ChannelFactory is no longer a candidate for the MRU cache. See more details here.
Caching the references yourself: This means that you are going to keep a collection of ChannelFactory references yourself. The snippet you provide in your question uses this approach. The best approach I have ever seen and admittedly use a modified version of at work is by Darin Dimitrov via this related SO question. For those of us who like to have more fine-grained control over the caching mechanism then this is the approach to use. This is typically used when credentials must be set at run-time like is often required by internet services.
Quite similarly, client proxies can be cached to improve performance - Wenlong Dong has an article about this topic.
(Update) Server-side clients as noted before are quite limited in their options when it comes to ChannelFactory caching. For this brief discussion, we will assume that our deployment scenario looks like this:
Client -> Service A -> Service B
The most likely method to use in order to leverage ChannelFactory caching in this scenario is to cache the references yourself for the session between the Client and Service A. This way Service A does not have to construct a different ChannelFactory instance every time Service A needs to call into Service B. However, if the properties of the ChannelFactory need change for each call, then this is no longer going to be appropriate.
Of course this also holds if Service A is a Singleton and each call to the downstream service (Service B) does not require new credentials, but Singleton services have their own set of performance problems.