I am using Instancing mode as PerSession - If a client makes multiple request for a given method - o/p should be incremented as per below code snippet b/c Instancing mode is
PerSession,
However I am always getting value as 1 for every call, ideally it should be incremented.
Let me know what I am missing
Thanks in advance...
Server
[ServiceContract]
public interface IServer
{
[OperationContract]
int GetData();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class Service1 : IServer
{
int count = 0;
public int GetData()
{
count++;
return count;
}
}
Client
ServiceReference1.IServer obj = new ServiceReference1.ServerClient();
Console.WriteLine(obj.GetData());
Console.WriteLine(obj.GetData());
what is the binding you have ? basicHttpBinding does not support PerSession instance mode it defaults to PerCall.
If you have basicHttpBinding change that to wsHttpBinding and try.
Related
(I am writing a processor that handles requests in a queue (console app).
I would like to use the .NET Core DI.
So far my code looks like this:
...
var connectionString = exportConfiguration.ConnectionString;
using (var scope = _serviceProvider.CreateScope())
{
var provider = scope.ServiceProvider;
var service = provider.GetRequiredService<MyContext>();
service.SqlConnectionString = sqlConnectionString; // I don't think property injection on a dbcontext will work, it takes its connection string in via the constructor
}
I have read how to assign parameters to the object as shown above, but how do I create a new context based on the connection string that is used in all the objects that the service uses (using constructor injection because thats why dbcontexts take - connection string in constructor)?
(I am not storing my connection string in the queue by the way, a code comes down the queue and my app then chooses the connection string to use).
I have managed to work this out. The key was that when you use CreateScope(), then GetRequiredService(), the DI system will provide new objects. So I just had to provide the correct information. This is now what my code looks like:
// Prior code gets information from a queue, which could be different every time.
// This needs passing as a constructor to the DbContext and possibly other information from the queue to other methods constructors
// (constructor injection not property injection)
var connectionString = queueItem.ConnectionString;
// save the connection string so the DI system (startup.cs) can pick it up
Startup.ConnectionString = connectionString;
using (var scope = _serviceProvider.CreateScope())
{
var provider = scope.ServiceProvider;
var service = provider.GetRequiredService<IMyService>();
// go off and get data from the correct dbcontext / connection string
var data = service.GetData();
// more processing
}
/// The Service has the DbContext in its constructor:
public class MyService : IMyService {
private DbContext _dbContext;
public MyService(DbContext dbContext) {
_dbContext = dbContext;
}
// more stuff that uses dbcontext
}
/// In startup.cs:
public static string ConnectionString {get;set;}
...
builder.Services.AddScoped<IMyService, MyService>();
builder.Services.AddScoped<DbContext>(options => options.UseSqlServer(Startup.ConnectionString));
// Also the following code will work if needed:
// Parameter1 is something that comes from the queue and could be different for each
// CreateScope()
build.Services.AddScoped<IMyOtherService>((_) =>
new MyOtherService(Startup.Parameter1));
I hope this helps somebody, because when I was googling around I couldn't find out how to do this.
SignalR does not have the ability to have client methods which returns a value. So I am trying to create a helper class to make this possible.
So this is what I am trying to do:
Server side: Call client method and provide unique request id Client(clientId).GetValue(requestId)
Server side: Save requestId and wait for answer using ManualResetEvent
Client side: Inside void GetValue(Guid requestId) call server method hubProxy.Invoke("GetValueFinished", requestId, 10)
Server side: find waiting method by requestId => set return value => set signal
Server side: Method not longer waiting vor ManualResetEvent and returns retrieved value.
I am able to get it work unfortunately. Here is my code:
public static class MethodHandler
{
private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>();
public static TResult GetValue<TResult>(Action<Guid> requestValue)
{
Guid key = Guid.NewGuid();
ReturnWaiter returnWaiter = new ReturnWaiter(key);
runningMethodWaiters.TryAdd(key, returnWaiter);
requestValue.Invoke(key);
returnWaiter.Signal.WaitOne();
return (TResult)returnWaiter.Value;
}
public static void GetValueResult(Guid key, object value)
{
ReturnWaiter waiter;
if (runningMethodWaiters.TryRemove(key, out waiter))
{
waiter.Value = value;
}
}
}
internal class ReturnWaiter
{
private ManualResetEvent _signal = new ManualResetEvent(false);
public ManualResetEvent Signal { get { return _signal; } }
public Guid Key {get; private set;}
public ReturnWaiter(Guid key)
{
Key = key;
}
private object _value;
public object Value
{
get { return _value; }
set
{
_value = value;
Signal.Set();
}
}
}
Using this MethodHandler class I need to have two method server side:
public int GetValue(string clientId)
{
return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key));
}
public void GetValueResult(Guid key, object value)
{
MethodHandler.GetValueResult(key, value);
}
Client side implementation is like this:
// Method registration
_hubProxy.On("GetValue", new Action<Guid>(GetValue));
public void GetValue(Guid requestId)
{
int result = 10;
_hubConnection.Invoke("GetValueResult", requestId, result);
}
PROBLEM:
if I call server side GetValue("clientid"). The client method will not be invoked. If I comment out returnWaiter.Signal.WaitOne();, client side GetValue is called and server side GetValueResult is called. But of course this time the method has already returned.
I thought is has to do with the ManualResetEvent but even using while(!returnWaiter.HasValue) Thread.Sleep(100); will not fix this issue.
Any ideas how to fix this issue?
Thanks in advance!
First, I think that, rather than asking for help in how to make it synchronous, it would be best if you just told us what it is you're trying to do so we could suggest a proper approach to do it.
You don't show your MethodHandler::Retrieve method, but I can guess pretty much what it looks like and it's not even the real problem. I have to tell you in the nicest possible way that this is a really bad idea. It will simply never scale. This would only work with a single SignalR server instance because you're relying on machine specific resources (e.g. kernel objects behind the ManualResetEvent) to provide the blocking. Maybe you don't need to scale beyond one server to meet your requirements, but this still a terrible waste of resources even on a single server.
You're actually on the right track with the client calling back with the requestId as a correlating identifier. Why can't you use that correlation to resume logical execution of whatever process you are in the middle of on the server side? That way no resources are held around while waiting for the message to be delivered to the client, processed and then the follow up message, GetValueResult in your sample, to be sent back a the server instance.
Problem solved:
The problem only occured in Hub.OnConnected and Hub.OnDisconnected. I don't have an exact explanation why, but probably these methods must be able to finish before it will handle your method call to the client.
So I changed code:
public override Task OnConnected()
{
// NOT WORKING
Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)));
// WORKING
new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start();
return base.OnConnected();
}
My app flow is as follows (simplified for clarity):
User GETs a page from "/page1"
User performs actions on the page (adds text, clicks, etc..), while Signalr communicates this data to the server, which performs heavy calculations in the background, and the results of those are returned to the page (lets call those "X").
When the user is finished with the page, he clicks a link to "/page2", that is returned by Nancy. This page is built using a Model that is dependent on X.
So, how do I build that Model based on X? How can signalr write to the user session in a way that Nancy can pick up on?
(I'm looking for a "clean" way)
Pending formal integration of Signalr & Nancy, this is what I came with. Basically, I share an IOC container between the two, and use an object (singleton lifetime) that maps users to state.
How to share an IOC container using the built in TinyIOC:
Extend Signalr's DefaultDependencyResolver
public class TinyIoCDependencyResolver : DefaultDependencyResolver
{
private readonly TinyIoCContainer m_Container;
public TinyIoCDependencyResolver(TinyIoCContainer container)
{
m_Container = container;
}
public override object GetService(Type serviceType)
{
return m_Container.CanResolve(serviceType) ? m_Container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = m_Container.CanResolve(serviceType) ? m_Container.ResolveAll(serviceType) : new object[] { };
return objects.Concat(base.GetServices(serviceType));
}
}
Replace Signalr's default DependencyResolver with our new one
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
CookieBasedSessions.Enable(pipelines);
// Replace UserToStateMap with your class of choice
container.Register<IUserToStateMap, UserToStateMap>();
GlobalHost.DependencyResolver = new TinyIoCDependencyResolver(container);
RouteTable.Routes.MapHubs();
}
}
Add IUserToStateMap as a dependency in your hubs and Nancy modules
public class MyModule : NancyModule
{
public MyModule(IUserToStateMap userToStateMap)
{
Get["/"] = o =>
{
var userId = Session["userId"];
var state = userToStateMap[userId];
return state.Foo;
};
}
}
public class MyHub : Hub
{
private readonly IUserToStateMap m_UserToStateMap;
public MyHub(IUserToStateMap userToStateMap)
{
m_UserToStateMap = userToStateMap;
}
public string MySignalrMethod(string userId)
{
var state = userToStateMap[userId];
return state.Bar;
}
}
What I would really want, is a way to easily share state between the two based on the connection ID or something like that, but in the meantime this solution works for me.
Did you arrive hear looking for a simple example of how to integrate Nancy and SignalR? I know I did.
Try this question instead (I self-answered it).
SignalR plus NancyFX : A simple but well worked example
I have a client (created using ASMX "Add Web Reference"). The service is WCF. The signature of the methods varies for the client and the Service. I get some unwanted parameteres to the method.
Note: I have used IsRequired = true for DataMember.
Service: [OperationContract]
int GetInt();
Client: proxy.GetInt(out requiredResult, out resultBool);
Could you please help me to make the schame non-varying in both WCF clinet and non-WCF client? Do we have any best practices for that?
using System.ServiceModel;
using System.Runtime.Serialization;
namespace SimpleLibraryService
{
[ServiceContract(Namespace = "http://Lijo.Samples")]
public interface IElementaryService
{
[OperationContract]
int GetInt();
[OperationContract]
int SecondTestInt();
}
public class NameDecorator : IElementaryService
{
[DataMember(IsRequired=true)]
int resultIntVal = 1;
int firstVal = 1;
public int GetInt()
{
return firstVal;
}
public int SecondTestInt()
{
return resultIntVal;
}
}
}
Binding = "basicHttpBinding"
using NonWCFClient.WebServiceTEST;
namespace NonWCFClient
{
class Program
{
static void Main(string[] args)
{
NonWCFClient.WebServiceTEST.NameDecorator proxy = new NameDecorator();
int requiredResult =0;
bool resultBool = false;
proxy.GetInt(out requiredResult, out resultBool);
Console.WriteLine("GetInt___"+requiredResult.ToString() +"__" + resultBool.ToString());
int secondResult =0;
bool secondBool = false;
proxy.SecondTestInt(out secondResult, out secondBool);
Console.WriteLine("SecondTestInt___" + secondResult.ToString() + "__" + secondBool.ToString());
Console.ReadLine();
}
}
}
Please help..
Thanks
Lijo
I don't think you can do much to make this "non-varying" - that's just the way the ASMX client side stuff gets generated from the WCF service. Each client-side stack is a bit different from the other, and might interpret the service contract in the WSDL in a slightly different manner. Not much you can do about that.....
If you don't want this - create a WCF client instead.
A remark on the side:
public class NameDecorator : IElementaryService
{
[DataMember(IsRequired=true)]
int resultIntVal = 1;
This is very strange how you're trying to put a DataMember (a field that should be serialized across for the service) into the class that implements the service.....
You should keep your service contract (interface IElementaryService), service implementation (class NameDecorator) and your data contracts (other classes) separate - do not mix data contract and service implementation - this is sure to backfire somehow....
I have this code up on my server here (Yes I known ASMX is a bad idea but WCF doesn't work at all for some reason):
<%# WebService Language="C#" Class="Test" %>
using System.Web;
using System.Web.Services;
[WebService(Namespace = "http://smplsite.com/smplAccess")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Test : System.Web.Services.WebService
{
State s;
public Test()
{
s = (Session["foo"] ?? (Session["foo"] = new State())) as State ;
}
[WebMethod(EnableSession = true)]
public void Set(int j) { i=j; }
[WebMethod(EnableSession = true)]
public int Get() { return i; }
}
class State
{
public int i = 5;
}
when I run the folloing code:
class Program
{
static void Main(string[] args)
{
var ser = new ServiceReference1.TestSoapClient();
Console.WriteLine(ser.Get());
ser.Set(3);
Console.WriteLine(ser.Get());
}
}
I expect to get back:
5
3
but I got back
5
5
My Solution
Usee wsdl.exe to generate a proxy class
Add references as needed to get it to compile
Use Martin's solution
This Seems related
Edit: Added State object.
Web services are stateless, so they do not store their state between multiple calls. Everytime you call a method, a new instance of the service will be created and its members will have the default values again.
What you can do, is to enable session state (as you have done) and store your state in the ASP.NET session.
Something like this:
[WebMethod(EnableSession = true)]
public void Set(int j) { Session["i"] = j; }
[WebMethod(EnableSession = true)]
public int Get() { return Session["i"] == null ? 5 : (int)Session["i"]; }
This was what is required on the server side. But you also have to take care on the client side:
Since an ASP.NET session is identified by a cookie, you have to make sure that you are passing the same cookie to the server with every web method call. To do so, you have to instantiate a CookieContainer and assign it to the web service proxy instance:
static void Main(string[] args)
{
var ser = new ServiceReference1.TestSoapClient();
ser.CookieContainer = new System.Net.CookieContainer();
// ...
}
You need to turn on sessions.
[WebMethod(EnableSession = true)]
It looks to me like its not persisting class state between session method calls - probably a new object is being called each time. I'm actually not sure that you can rely on getting the same object instance each time you call the service. Joshua's answer is correct, but you'll also need to write code to persist your service's internal field into that session.