Combining two modules without making the factory a god object - c#

We have an application that contacts several diffrent remote services(SOAP, HTTPREQUEST). We then do different actions(import, export, update, delete).
Today we have two client classes and four action classes.
QUESTION!
How can I decouple these two modules so that I have to do the least changes. IE only add a new action/new client. Nothing more.
Client class
Authorizes our client against the remote service, it handles logging in and out.
Action class
Holds the url, method to invoke against the client. Aswell as the ExecuteActionMethod
Usage
Client class get's decorated with an action and then performs the action with the client.
Fears
I dont want to: - create a new action class everytime I add a new client class - create a new client class everytime I add a new action class - No god object factory that needs to know everything
Problem
The problem with this approach is that when talking to different clients, I need diffrent information in this case different URLS, talking to the Soap service needs invoking of the correct method. The action itself is the keeper of this information. But as I dig deeper this certainly is something that will change.
Scenario 1#
I end up creating classes that combine both action and result. So I have classes like "HttpImport"(based on HttpClient and ImportAction). Which results in X(Clients) * Y(Actions) which now would total at 8 classes, which is really bad.
Scenario 2#
Time for some code! In this scenario the implementation binds my classes together even though I use abstractions.
Problem here is that every action need to have a property for each of the clients(remember they visit different endpoints). So if i were to add one more client I would have to go through all the actions and add another property for that clients endpoint, aswell as add another deocrator to delegete all calls to the correct endpoint(remember i have three properties now in every action). If I were to create another action, it would just be that action. So N*times actions + 1(the action), in this case 5 changes. A little bit better but still not there.
Scenario 3#
This is the God object factory. Here we get rid of the properties holding the endpoints, and we supply the enpoint via the constructor. This will result in methods for creating all sorts of clients and actions. Same as above X(Clients) * Y(Actions) if something were to be added, these accumulate into 8 new methods inside the factory. The factory must also hold endpoint information.
Code
My code has evolved to the 2:nd scenario. I dont want to build the factory, and I'm looking to you guys.
Something tells me that the client classes does to much and should somehow be decoupled, from the classes they instansiate inside.
Main
static void Main(string[] args)
{
IAction iact = new ImportAction();
IDecorator idec = new HttpDecorator(iact);
IClient icli = new HttpClient(idec);
Console.Write(icli.connect().ToString());
Console.ReadKey();
}
IAction
public interface IAction
{
string[] Execute();
string HttpString { get; }
string SoapMethod { get; }
}
ImportAction
class ImportAction : IAction
{
private string soapmethod;
private string httpUrl;
public ImportAction()
{
this.HttpString = #"http://www.hereiswereactionsgo.com";
}
public string[] Execute()
{ //Execute the action!
return null;
}
public string HttpString { get; set; }
public string SoapMethod { get; set; }
}
IDecorator
public interface IDecorator
{
string GetActionString();
}
HttpDecorator
class HttpDecorator : IDecorator
{
private IAction _action;
public HttpDecorator(IAction action)
{
this._action = action;
}
public string GetActionString()
{
return _action.HttpString;
}
public string[] Execute()
{
throw new NotImplementedException();
}
}
IClient
public interface IClient
{
bool connect();
}
HttpClient
class HttpClient : IClient
{
private string _username;
private string _password;
private IDecorator _myaction;
private HttpWebRequest webReq;
public HttpClient(IDecorator action)
{
this._username = "myusername";
this._password = "mypassword";
this._myaction = action;
}
public bool connect()
{
bool result = false;
webReq = (HttpWebRequest)WebRequest.Create(_myaction.GetActionString());
webReq.Credentials = new NetworkCredential(_username, _password);
HttpWebResponse myHttpWebResponse = (HttpWebResponse)webReq.GetResponse();
if (myHttpWebResponse.StatusCode == HttpStatusCode.OK)
{
result = true;
}
return result;
}
}

Visitor pattern seems suitable for this (Visitor) .
Treat Actions as the Visitors and Clients as Elements to visit. Keeping Action as an abstract class rather than interface may help by providing the boilerplate code.
To add a new Action extend BaseAction. Implement methods such as getHttpUrl(), getHttpBody() etc.
To add a new Client will require changes to existing classes. You have to implement corresponding methods in each Action class. I assume adding a new Client will happen less frequently.
The sample code below follows Java syntax.
public static void main() {
new HttpClient().performAction(new ImportAction());
}
public interface Client {
performAction(Action);
}
public class HttpClient implements Client {
public void accept(IAction a) {
a.visitHttp(this);
}
}
public abstract class Action {
public visitHttp(HttpClient c) {
getHttpUrl();
c.connect(getHttpUrl());
c.send(getHttpBody());
c.close;
}
public visitSoap(SoapClient c) {
}
public abstract String getHttpUrl();
public abstract String getHttpBody();
}
ImportAction extends Action {
#Override
getHttpUrl() {
}
#Override
getHttpBody() {
}
}

Related

How to resolve Autofac dependency in runtime using parameter from request?

I have a .NET project with 50+ WebAPI Controllers. Dependencies injected in constructor:
// One of this APIs
public class ProductAController : ApiController
{
private readonly IProductDataProvider _productDataProvider;
// Constructor usually requires a lot of dependencies (for user sesisons, calculations, crud operations, some external integrations etc)
public ProductAController(IProductDataProvider productDataProvider)
{
this._productDataProvider = productDataProvider;
}
}
public interface IProductDataProvider
{
public bool ProductExists(string productName);
}
public class ProductDataProvider
{
private readonly IDbConnectionProvider _dbConnectionProvider;
public ProductDataProvider(IDbConnectionProvider dbConnectionProvider)
{
this._dbConnectionProvider = dbConnectionProvider;
}
public bool ProductExists(string productName)
{
//...
}
}
public interface IDbConnectionProvider
{
public string GetConnectionString();
}
public class DbConnectionProvider
{
// This is the config that I need to set up for several APIs
private readonly ModuleConfig _moduleConfig;
public DbConnectionProvider(ModuleConfig moduleConfig)
{
this._moduleConfig = moduleConfig;
}
public string GetConnectionString();
{
//...
}
}
IDbConnectionProvider, ModuleConfig, and IProductDataProvider registered in Autofac (all 50 API use single ModuleConfig):
builder.RegisterInstance(pdkProvidersSettings).AsSelf().SingleInstance();
builder.RegisterType<DbConnectionProvider>().As<IDbConnectionProvider>().InstancePerDependency();
builder.RegisterType<ProductDataProvider>().As<IProductDataProvider>().InstancePerDependency();
Now I need to use different instances of ModuleConfig for some APIs.
I'll have to make IConfigProvider.GetModuleConfig(string key, string ownerKey) (and call it with "ModuleConfig" and "productA123" params)
I was thinking about Named and Keyed Services feature or IIndex in Autofac, I also tried to pass moduleConfig across all that chain (but it required too much changes).
It seems like I have a design problem here but I can't figure it out.
The expected result is something like that:
API gets a request containing the key ("productA1" or "productA2". Thats why I can't hardcode "productA" in metadata attribute of ProductAController)
When it comes to IDbConnectionProvider it gets specific ModuleConfig and is able to get specific connection string
The result is that different API modules can use different DBs (that is the main requirement)

Provide user information from signalr request in business logic layer using autofac

I have an ASP.NET MVC 5 Application with a SignalR 2 hub and using autofac for the DI.
The entire business logic is encapsulated in manager classes in their own layer. Some manager methods need informations about the current logged in user (UserId, TenantId, ..).
I solved this problem by injecting an AuthorizationProvider into each manager class that needs the user information.
public interface IAuthorizationProvider
{
long? GetUserId();
long? GteTenantId();
}
public class MyManager : IMyManager
{
private IAuthorizationProvider _authorizationProvider;
public MyManager(IAuthorizationProvider authorizationProvider)
{
_authorizationProvider = authorizationProvider;
}
public void MyMethod()
{
// Getting the User information here is pretty simple
long userId = _authorizationProvider.GetUserId();
}
}
Normally I can get the user information from the HttpContext and from the session. So I wrote a SessionAuthorizationProvider:
public class SessionAuthorizationProvider{
public long? GetUserId()
{
HttpContext.Current?.Session?[SessionKeys.User]?.Id;
}
public long? GteTenantId() { ... }
}
But now I have a new method in the SignalR hub that use the same mechanism.
[HubName("myHub")]
public class MyHub : Hub
{
private IMyManager _myManager;
public MyHub(IMyManager myManager)
{
_myManager = myManager;
}
[HubMethodName("myHubMethod")]
public void MyHubMethod(long userId, long tenantId)
{
_myManager.MyMethod();
}
}
The problem is that a SignalR request doesn't have a session. Therefore I have also set the required user information in the hub method as parameters postet from the client.
So I thought it is the best solution for this problem to write a new AuthorizationProvider for SignalR and adapt the depdendency resolver. But I can't get the current user in the new SignalrAuthorizationProvider.
public class SignalrAuthorizationProvider{
public long? GetUserId()
{
// How to get the user information here???
}
public long? GteTenantId() { /* and here??? */ }
}
Is there a recommended solution to this problem?
Of course, I can extend MyMethod to accept the user information as a parameter. But MyMethod calls another method from another manager and that manager also calls another method. The user information is only needed for the last method call. So I had to change at least 3 methods and many more in the future.
Here is a sketch of the problem
This is a potential solution. But it's very bad
Session is not supported by SignalR by default and you should avoid using it. See No access to the Session information through SignalR Hub. Is my design is wrong?. But you still can use cookie or querystring to get the desired value.
In both case you need to have access to the HubCallerContext of the underlying hub, the one that is accessible through the Context property of the Hub.
In a ideal word you should just have to had the dependency to the SignalAuthorizationProvider
ie :
public class SignalrAuthorizationProvider {
public SignalrAuthorizationProvider(HubCallerContext context){
this._context = context;
}
private readonly HubCallerContext _context;
public long? GetUserId() {
return this._context.Request.QueryString["UserId"]
}
}
But due to SignalR design it is not possible. Context property is assigned after construction of the Hub and AFAIK there is no way to change it.
Source code here : HubDispatcher.cs
One possible solution would be to inject a mutable dependency inside the Hub and alter the object in the OnConnected, OnReconnected methods.
public class SignalrAuthorizationProvider : IAuthorizationProvider
{
private Boolean _isInitialized;
private String _userId;
public String UserId
{
get
{
if (!_isInitialized)
{
throw new Exception("SignalR hack not initialized");
}
return this._userId;
}
}
public void OnConnected(HubCallerContext context)
{
this.Initialize(context);
}
public void OnReconnected(HubCallerContext context)
{
this.Initialize(context);
}
private void Initialize(HubCallerContext context) {
this._userId = context.QueryString["UserId"];
this._isInitialized = true;
}
}
and the Hub
public abstract class CustomHub : Hub
{
public CustomHub(IAuthorizationProvider authorizationProvider)
{
this._authorizationProvider = authorizationProvider;
}
private readonly IAuthorizationProvider _authorizationProvider;
public override Task OnConnected()
{
this._authorizationProvider.OnConnected(this.Context);
return base.OnConnected();
}
public override Task OnReconnected()
{
this._authorizationProvider.OnReconnected(this.Context);
return base.OnReconnected();
}
}
Having a mutable dependency is not the best design but I can't see any other way to have access to IRequest or HubCallerContext.
Instead of having an abstract Hub class which is not a perfect solution. You can change the RegisterHubs autofac method to use AOP with Castle.Core and let the interceptor calls the methods for you.

How to create Polly's policy for several WCF Clients

There is a 3rd-party service I need to integrate with. It has 3 endpoints for 3 calls. That's it, each API call has its own endpoint with its own wsdl. So there isn't any connection (like inheritance) between POCOs of different calls. BUT results of these calls are very similar. Particularly every result has 'Errors' property that contains errors in a pipe-delimited string. Some errors are shared among calls (have the same name) and have to be handled in a similar manner.
My goal is to retry calls when either an exception was raised or there is an error named 'EXCEPTION' in Errors. I'm trying to use Polly to reach this goal. The only way I see now is to create separate Policy for each call. Is there any way to create a single policy for all calls?
Sample code is below (in real project *Result classes and *Service interfaces are autogenerated by VisualStudio when importing wsdl):
public partial class CreateResult
{
public string Errors {get;set;}
}
public interface ICreateService
{
Task<CreateResult> CreateAsync();
}
public partial class UpdateResult
{
public string Errors {get;set;}
}
public interface IUpdateService
{
Task<UpdateResult> UpdateAsync();
}
public partial class DeleteResult
{
public string Errors {get;set;}
}
public interface IDeleteService
{
Task<DeleteResult> DeleteAsync();
}
public class Connector
{
private readonly ICreateService _createService;
private readonly IUpdateService _updateService;
private readonly IDeleteService _deleteService;
private readonly Policy _policy = ???;
public Connector(ICreateService createService, IUpdateService updateService, IDeleteService deleteService)
{
_createService = createService;
_updateService = updateService;
_deleteService = deleteService;
}
public async Task<CreateResult> CreateAsync()
{
// sample policy: var policy = Policy.Handle<Exception>()
// .OrResult<CreateResult>(r => r.Errors.Contains("EXCEPTION"))
// .Retry();
// Now I need to create such a policy for every call. How can I create a single policy or a factory method to enforce DRY principle?
return _policy.ExecuteAsync(() => _createService.CreateAsync());
}
public async Task<UpdateAsync> UpdateAsync()
{
return _policy.ExecuteAsync(() => _updateService.UpdateAsync());
}
public async Task<DeleteResult> DeleteAsync()
{
return _policy.ExecuteAsync(() => _deleteService.DeleteAsync());
}
}
Assuming that each return type has a property containing a collection of Error objects you can eliminate some code duplication by having each policy reuse the method that inspects that collection.
For example:
public static class PolicyExtensions
{
public static bool ContainsExceptionMessage(this IEnumerable<Error> errors)
{
return errors.Any(error => error.Name.Contains("EXCEPTION"));
}
}
You'll need multiple policies but each can reuse this method:
var policy = Policy.HandleResult<MyResultClass>(
result => result.Errors.ContainsExceptionMessage())
.Or<Exception>()
.Retry();
If each of your classes implements an interface indicating that it contains a collection of errors, you can also create a generic function to return a policy:
public interface IHasErrors
{
List<Error> Errors { get; }
}
Policy<THasErrors> CreateExceptionPolicy<THasErrors>() where THasErrors:IHasErrors
{
return Policy.HandleResult<THasErrors>(
result => result.Errors.ContainsExceptionMessage())
.Or<Exception>()
.Retry();
}
Now you're still creating multiple policies, but they're much easier to create, and none of their code is duplicated.
var policy = CreateExceptionPolicy<UpdateResult>();

How to use IRequiresRequest to inject IRequest in ServiceStack?

I need to access request context, specifically the Items inside my custom class and I don't want to do have it either inheriting from ServiceStack Service or having the set it up inside the my Service.
So if I have a class like below which the implementer class (ContextItemsGetter) also implements IRequiresRequest, I would expect the Request property to be populated.
public interface IGetContextItems
{
string Get(string key);
}
public class ContextItemsGetter : IGetContextItems, IRequiresRequest
{
public string Get(string key)
{
//someway to access http context items
//im RequestContext.Instance.Items[key] e.g. Prop1 Prop2
//or Request.blah but Request is always null
}
public IRequest Request { get; set; }
}
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Web/IRequiresRequest.cs
However the Request is always null for both when SessionIdGetter is called from a genuine HTTP request or a redis message request. Am I doing something wrong? The purpose is to decouple and use Items to pass information between http request and redis message request.
I've also tried to use RequestContext.Instance.Items, where this worked for HTTP request, but during redis message request, the items are not there, the keys where I populated just before calling ExecuteMessage are not there.
var req = new BasicRequest { Verb = HttpMethods.Get };
req.Items.Add("Prop1", m.GetBody().Prop1);
req.Items.Add("Prop2", m.GetBody().Prop2);
var result = HostContext.ServiceController.ExecuteMessage(m, req);
I'm using version 4.0.50.
Also, this page Access HTTP specific features in services where mentions
Note: ServiceStack's Service base class already implements IRequiresRequestContext which allows you to access the IRequestContext with base.RequestContext and the HTTP Request and Response with base.Request and base.Response.
I believe IRequiresRequestContext is now called IRequiresRequest, so I think the doc should be updated.
Updated: full code to demo my original intention:
[Route("/test", Verbs = "GET")]
public class Dto : IReturnVoid
{ }
public class DtoService : Service
{
//So that IGetContextItems is taken care of by IDependencyThatUsesIGetContextItems
public IDependencyThatUsesIGetContextItems DependencyThatUsesIGetContextItems { get; set; }
public void Get(Dto req)
{
DependencyThatUsesIGetContextItems.SomeMethod();
}
}
public interface IGetContextItems
{
string Get(string key);
}
//since ContextItemsGetter implmeents IRequiresRequest
//I can still easily test any service that uses IGetContextItems by mocking IGetContextItems
public class ContextItemsGetter : IGetContextItems, IRequiresRequest
{
public IRequest Request { get; set; }
public string Get(string key)
{
//either through injection
//return Request.Items[key].ToString();
//or some static class
//return RequestContext.RequestItems.Items[key].ToString();
return RequestContext.Instance.Items[key].ToString();
}
}
public interface IDependencyThatUsesIGetContextItems
{
string SomeMethod();
}
public class DependencyThatUsesIGetContextItems : IDependencyThatUsesIGetContextItems
{
//this will be inejcted
public IGetContextItems ContextItemsGetter { get; set; }
public string SomeMethod()
{
var a = ContextItemsGetter.Get("SomeKey");
return "blah";
}
}
IRequiresRequest only injects the current IRequest to your Service classes and Validation Filters, it doesn't inject the IRequest into your dependencies which are resolved directly from the IOC and who doesn't have access to current IRequest to be able to inject.
Also ServiceStack's convenient Service and AbstractValidator<T> base classes already implement IRequiresRequest so in most cases the places where IRequiresRequest applies has already been implemented so you shouldn't need to implement it yourself.
The recommended approach to passing the IRequest into your dependencies is to pass them as a parameter from your Service, e.g:
public class MyServices : Service
{
public IGetContextItems ContextItems { get; set; }
public object Get(Request request)
{
return ContextItems.Get(base.Request, request.Id);
}
}
You do have an opportunity to inspect and modify your Service instance before it executes your Service by overriding OnPreExecuteServiceFilter() in your AppHost to go through and inject the IRequest in each of your Services dependencies that implement IRequiresRequest with:
public override object OnPreExecuteServiceFilter(IService service,
object request, IRequest req, IResponse res)
{
service.InjectRequestIntoDependencies(req);
return request;
}
Which calls the below extension method will recursively populate your Services dependency graph as long as each parent implements IRequiresRequest:
public static class ServiceExtensions
{
public static void InjectRequestIntoDependencies(this object instance, IRequest req)
{
foreach (var pi in instance.GetType().GetPublicProperties())
{
var mi = pi.GetGetMethod();
if (mi == null)
continue;
var dep = mi.Invoke(instance, new object[0]);
var requiresRequest = dep as IRequiresRequest;
if (requiresRequest != null)
{
requiresRequest.Request = req;
requiresRequest.InjectRequestIntoDependencies(req);
}
}
}
}
But you need to be careful to not implement IRequiresRequest on any of your Singleton dependencies (the default scope) as it's not ThreadSafe whereas passing IRequest as a parameter would be.
Also to avoid coupling your logic classes to ServiceStack I'd consider only passing in what your dependencies needs from IRequest instead of the IRequest instance itself which will also make it easier to test.

wcf callback reference

I have a desktop app with a duplex WCF service, but I have some troubles using the callback.
The service is started as following in main of program.cs:
ServiceHost svcHost = new ServiceHost(typeof(PeriodicService));
svcHost.Open();
Console.WriteLine("Available Endpoints :\n");
svcHost.Description.Endpoints.ToList().ForEach(endpoint => Console.WriteLine(endpoint.Address.ToString() + " -- " + endpoint.Name));
For the service I created a subscribe function where the callbackchannel is saved in a global variable, then the callback uses that global variable to talk back to the client (there will be only one client connecting).
IPeriodicCallback callbackClient;
public IPeriodicCallback Proxy
{
get
{
return this.callbackClient;
}
}
public void joinPeriodicService()
{
Console.WriteLine("Client subscribe");
this.callbackClient = OperationContext.Current.GetCallbackChannel<IPeriodicCallback>();
}
The thing I want to do now is call the callbackclient from an other class.
In the other class I created the service as:
private PeriodicService periodicService = new PeriodicService();
And I try to write data to it using:
if(this.periodicService.Proxy != null)
{
this.periodicService.Proxy.On1MinuteDataAvailable(tmpPeriod);
}
However the proxy stays null, I also tried to move the proxy part to the class but this also results in it staying null.
When the client connects I nicely get the message "Client Subscribe" but it seems there are two instances running of the periodicservice.
But my problem is I don't see an other way to access the periodicservice then creating it in my class, or is it also already created by the svcHost?
Can ayone point me in the right direction?
This repository shows the a duplex WCF imeplementation I made to answer a similar question a while ago, its a full working example with as little extra stuff as possible.
https://github.com/Aelphaeis/MyWcfDuplexPipeExample
Lets say we have a Service Contract like this :
[ServiceContract(CallbackContract = typeof(IMyServiceCallback),SessionMode = SessionMode.Required)]
public interface IMyService
{
[OperationContract(IsOneWay=true)]
void DoWork();
}
Note that I specified a CallbackContract.
If you want to make a duplex, you would want to perhaps make your Service Behavior implementation of the above contract like this :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService
{
public void DoWork()
{
Console.WriteLine("Hello World");
Callback.WorkComplete();
}
IMyServiceCallback Callback
{
get
{
return OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
}
}
}
The important thing here is the Callback. This is how your service would allow you to access specified to you by the Client.
You also need to define the callback interface, In my case its quite simple :
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract(IsOneWay = true)]
void WorkComplete();
}
Now I want to create a client to use this Duplex Service. The first thing I need to do is implement the IMyServiceCallback. I need to do this on the client. In this case the implementation is this:
class Callback : IMyServiceCallback
{
public void WorkComplete()
{
Console.WriteLine("Work Complete");
}
}
Now when I want to open my duplex connection with the services I would create a proxy class like this something like this:
public class MyServiceClient: IMyService, IDisposable
{
DuplexChannelFactory<IMyService> myServiceFactory { get; set; }
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
}
public void DoWork()
{
myServiceFactory.CreateChannel().DoWork();
}
public void Dispose()
{
myServiceFactory.Close();
}
}
Notice that I specified an InstanceContext. That Instance Context will be an instance of the object I created that implements IMyServiceCallback.
That's all you need to do! Simple as that!
Update :
Callback objects are just like any other object. You can store them into a collection and iterate through them and based on some condition.
One way is to create a property in the IMyServiceCallback that can uniquely identify it. When a client connects to the service it can call a method which specifies a callback object which can then be cached or saved for later use. You can then iterate the callbacks and based on some condition you can call a method for a specific client.
This is certainly more complicated; however, it is certainly manageable. I will add an example in a bit.
Update 2
This is a working example of exactly what you want; however, its a lot more complicated. I'll try to explain as simply as I can : https://github.com/Aelphaeis/MyWcfDuplexPipeExample/tree/MultiClient
Here is a list of the changes:
I've modified the client proxy (and service) so that when initialized it calls the init Method
I've also modified the Service implementation so that now it is a single instance dealing with all requests (for convenience).
I added a new OperationContract in the Service interface called Msg
I've added a new Method in the IMyServiceCallback called RecieveMessage.
I've added a way to identify the client.
In the proxy class I have the following :
public MyServiceClient(IMyServiceCallback Callback)
{
InstanceContext site = new InstanceContext(Callback);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
EndpointAddress endpointAddress = new EndpointAddress(Constants.myPipeService + #"/" + Constants.myPipeServiceName);
myServiceFactory = new DuplexChannelFactory<IMyService>(site, binding, endpointAddress);
Init();
}
public void Init()
{
myServiceFactory.CreateChannel().Init();
}
In my service I have the following :
public class MyService : IMyService
{
public List<IMyServiceCallback> Callbacks { get; private set; }
public MyService(){
Callbacks = new List<IMyServiceCallback>();
}
public void Init()
{
Callbacks.Add(Callback);
}
// and so on
My IMyServiceCallback has been redefined to :
[ServiceContract]
public interface IMyServiceCallback
{
[OperationContract]
int GetClientId();
[OperationContract(IsOneWay = true)]
void WorkComplete();
[OperationContract(IsOneWay = true)]
void RecieveMessage(String msg);
}
By specifying a number, you can contact the client that corresponds with that number. If two clients have the same Id, both clients will be contacted.

Categories