SignalR
Printed From: IdeaBlade
Category: DevForce
Forum Name: DevForce 2012
Forum Discription: For .NET 4.5
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=4139
Printed Date: 06-Sep-2025 at 3:18pm
Topic: SignalR
Posted By: kdev
Subject: SignalR
Date Posted: 06-May-2013 at 5:28am
Hi,
I want to use SignalR in order to notify client users from the server and wanted to know if someone has succeded in getting a server side EntityManager connected with the same login credentials as it would be if it was called through an RPC call.
In my case, I have customized the login process in order to use my own authentication logic rather than asp.net login process http://drc.ideablade.com/devforce-2012/bin/view/Documentation/authentication-details-custom - I've followed that , and didn't find how I can create, when I use SignalR Invoke method, a correctly authenticated EntityManager. I need to have that EntityManager using the same credentials as it would have if it was called in a DevForce RPC method call.
I would be very interesting if you could give some advise on how to inegrate SignalR with DevForce.
Regards,
|
Replies:
Posted By: sbelini
Date Posted: 09-May-2013 at 10:39am
kdev,
If you pass the sessionKey, you will be able to authenticate the server EM:
on the client:
var sessionKey = Authenticator.Instance.DefaultAuthenticationContext.SessionKey;
myIHubProxy.Invoke("MyPubSubService", sessionKey).Wait();
|
on the server:
public void MyPubSubService(Guid sessionKey) {
var em = new EntityManager();
Authenticator.Instance.Login(sessionKey);
// do something
}
|
|
Posted By: kdev
Date Posted: 09-May-2013 at 2:20pm
Thank you Silvio,
On the client side I had to oper a little differently since I'm using Cocktail :
var authenticationService = Composition.GetInstance<IAuthenticationService>();
var sessionKey = authenticationService.AuthenticationContext.SessionKey; |
and then pass sessionKey through the Invoke.
But on the server side the EntityManager try to login with no credentials (= null)
Please note that I have implemented a custom IEntityLoginManager...
Regards.
|
Posted By: kdev
Date Posted: 09-May-2013 at 3:04pm
By adding the following code to my LoginManager, it worked, but I'm not very happy with this workaround...
Is there any other better solution that avoids the call of my custom Login() method ???
public class LoginManager : IEntityLoginManager
{
public virtual IPrincipal Login(ILoginCredential credential, EntityManager entityManager)
{
if (entityManager.IsLoggedIn && entityManager.AuthenticationContext.Principal is IMyOwnPrincipal)
return entityManager.AuthenticationContext.Principal;
... |
|
Posted By: sbelini
Date Posted: 13-May-2013 at 10:42am
kdev,
Unlike Push in DevForce 2010, the SignalR request is not a DevForce request and a principal is not automatically passed nor an authenticated EM will exist.
Because of that, you will need to pass that information explicitly and manually login so to authenticate your EM.
I find the most secure way to do this by passing the sessionKey. This way you are not at risk of sending sensitive information across the wire. You will still have to login though.
Note that in my test, I used the default ASP.NET authentication and it worked fine. Since you are using a custom Login Manager, you should adapt your customization for such. (i.e. as you have done in your post above)
|
Posted By: kdev
Date Posted: 05-Jul-2013 at 10:54am
Hi,
I come back to you about my problem, after some tests I found an issue with your proposition.
The first time Authenticator.Instance.Login is called on the server it generates a specific sessionkey and store it along the CURRENT Principal in the property DefaultAuthenticationContext.
Then every time I start a new work on the server the Authenticator has these informations, even though another user did it.
It results that, if I check the Principal on the newly created entityManger, the Identity.Name might not have MY name but the name of the first person who did "initialize" the Authenticator.Instance.DefaultAuthenticator.
Should we use the Authenticator singleton when working on a server ?
I tried to Logout the DefaultAuthenticationContext just before to Logging it again, but it did raise and exception ("Implicit login is not allowed when the AuthenticationContext is logged out")
But once I save my changes, the EntityServerInterceptor get an EntityManager with the correct Principal.
What should I do to have the correct Principal in the manager I created ?
|
Posted By: kdev
Date Posted: 12-Jul-2013 at 5:57am
Any info about this issue ?
|
Posted By: sbelini
Date Posted: 12-Jul-2013 at 9:22am
kdev,
Sorry for the delay. I'm checking this issue and will follow up soon.
|
Posted By: sbelini
Date Posted: 15-Jul-2013 at 2:41pm
kdev,
I was partially able to reproduce the scenario you described.
While I see that the server holds the information of the user who made the first call, I don't get any exception while trying to logout/login on the second call.
Since you are using the DefaultAuthenticationContext, "remembering" the login information is actually expected.
If you don't want to keep the login information, you might want use an EM's specific AuthenticationContext instead:
var em = new EntityManager();
em.AuthenticationContext = Authenticator.Instance.Login(sessionKey); |
|
Posted By: kdev
Date Posted: 02-Aug-2013 at 8:42am
Hello,
I come back with a side effect, and I think there is a bug in DF but I'm not sure...
After using successfully the sessionKey obtained by this method :
Composition.GetInstance<IAuthenticationService>().AuthenticationContext.SessionKey
I leave my (SL) application launched without activity for a while.
After that I successfully query some entities (I think that the client EM reconnects after a timeout)
But when I try to use a new EM on the server with the session key authenticationService.AuthenticationContext.SessionKey I have an exeption saying : SessionKey was not found. You must re-login.
Can you reproduce the issue ?
Regards.
|
Posted By: kdev
Date Posted: 12-Aug-2013 at 5:47am
Posted By: sbelini
Date Posted: 12-Aug-2013 at 9:34pm
kdev,
I was not able to reproduce the problem. Can you provide a small solution showing the issue?
|
Posted By: kdev
Date Posted: 24-Oct-2013 at 3:52am
Hi Silvio,
Here is a test project which reproduce the sessionKey issue with SignalR 2.0 and Cocktail.
uploads/1623/TestSignalRSessionKey.zip - TestSignalRSessionKey.zip
I have set the userSessionTimeout to 2 min.
If you click on the button right after starting the application, no exception will be thrown in the hub. But if you wait 2 or more minutes to click on it, it will raise an exception « SessionKey not found – you must re login » (put a break point on the hub to catch the exact message because the error is not handled on Silverlight).
Do you think the fix could be made for the next release of DF ?
Regards
|
Posted By: sbelini
Date Posted: 24-Oct-2013 at 12:41pm
Thanks, kdev.
We will look into your sample and determine what the issue is.
|
Posted By: sbelini
Date Posted: 29-Oct-2013 at 5:15pm
kdev,
It turns out that this is not a bug. If after the session timeout the client performs a query or save, it will still work because DevForce silently logs the client back in because the “session bundle” contains enough information to do the login. You shouldn't expect it to happen on a SignalR call.
In this situation you can either log back in or perform an operation that would trigger the silent login.
|
Posted By: kdev
Date Posted: 30-Oct-2013 at 4:42am
Hi,
Executing a query before a SignalR call indeed reconnects the client's EntityManager but doesn't fix the signalr issue as the SessionKey provided by the AuthtenticationContext never changes.
However, if the "query trick" worked, I find it very annoying to have to do a dummy query before every signalr call. I currently have many signalr calls and plan to have a lot more.
Below the updated project using a query prior to the signalr call uploads/1623/TestSignalRSessionKey2.zip - uploads/1623/TestSignalRSessionKey2.zip
You said "you can log back in", how would you do that and based on which event/information ? Currently I have no way to know if the client is disconnected, the information on the AuthenticationContext always show "LoggedIn".
Regards
|
Posted By: sbelini
Date Posted: 30-Oct-2013 at 9:13am
kdev,
Executing a query fixes the issue - the client is silently logged back in using the information in the 'session bundle'. In this case, the sessionKey remains the same.
I tested calling a query before suggesting it to you and it worked fine. You can check my test solution here http://www.ideablade.com/forum/uploads/892/SignalR.zip - uploads/892/SignalR.zip . Note that since you stated this is a DevForce/SignalR issue, I removed Cocktail as it is not pertinent to the problem and only adds unnecessary complexity.
As for logging back in, you'd need the user to enter the credentials again. You are correct about not knowing if the session has timed out on the client side - specially because if a query/save is performed, the silent login occurs. Unless you want to implement some monitoring on the client that would require a manual login after a certain amount of time (time that would match the sessionTimeout in web.config), the easiest approach is to use the silent login by running a dummy query.
|
Posted By: kdev
Date Posted: 30-Oct-2013 at 9:29am
I tried your solution and it also has the issue :
|
Posted By: sbelini
Date Posted: 30-Oct-2013 at 9:34am
Interesting.
I'll check why it works here, but not there.
|
Posted By: kdev
Date Posted: 30-Oct-2013 at 9:50am
I should note it didn't throw the exception on the first timeout but the second.
Here is what I did :
- Click the button - Wait more than 1 minute - Click the button => Success - Wait more than 1 minute - Click the button => Fail
|
Posted By: sbelini
Date Posted: 30-Oct-2013 at 2:14pm
Thanks for the additional information. That certainly helped.
The problem is that the same query executed a second time tend to run against the cache. To solve the problem set the query to explicitly run against the DataSource:
i.e.
await dummyQuery.With(QueryStrategy.DataSourceOnly).ExecuteAsync();
|
Posted By: kdev
Date Posted: 19-Nov-2013 at 9:48am
Running the dummy query on the original EmtityManager works but I would like to be able to reconnect with another EntityManager.
why ? because I find to have to execute a query dummy before every call very annoying so I want to encapsulate the Invoke method frrom the SignalR.IHubProxy with this query call inside.
if I create a new EntityManager with the same ConnectionOption (cocktail) as the EntityManager I used to login in the application it doesn't work although they have the same sessionKey !
I noticed if I create an entityManager from the disconnected EntityManager it works (with new EntityManager(em)). but if I create it from an EntityManagerContext using the same values as the one in the disconnected em it doesn't.
var context = new EntityManagerContext( dataSourceExtension : disconnectedEm.DataSourceExtension, entityServiceOption: disconnectedEm.EntityServiceOption, compositionContextName: disconnectedEm.CompositionContext.Name, options: disconnectedEm.Options, serviceKey: disconnectedEm.ServiceKey);
I don't really understand why in some case it works and not in others. what is the use of the sessionkey if a newly entitymanager using the same sessionkey of the disconnected one doesn't do the job.
is it possible to achieve my scenario with cocktail ?
|
Posted By: kdev
Date Posted: 20-Nov-2013 at 2:38am
I have a case where a ViewModel has no UnitOfwork or EntityManager at all, but I am doing SignalR call providing the Authenticationcontext.SessionKey as the signalr method will create entities with Audit information.
How, in this case, can I reconnect "something" with a dummy query without any EntityManager ? Is there a way to create a new EntityManager (with which parameters) which will allow the reconnection ?
|
Posted By: kdev
Date Posted: 20-Nov-2013 at 6:45am
Edit : repost of the post due to a delete by error
This SessionKey issue is making me crazy !
When I implement the Dummy Query in my application it doesn't work at all, I still get the "Relogin error" while it does in the test project.
I updated my first project test to implement IEntityManagerProvider<T> from Cocktail and it appears the relogin process doesn't happen completly when I use the manager coming from this service. To resume, prior to the Invoke code I don't get the same result depending of the way the entitymanager been created. Exemple :
// THIS VERSION WORKS var _em = new EntityManager(); var orders = await new EntityQuery<Order>().With(_em).With(QueryStrategy.DataSourceOnly).ExecuteAsync(); // THIS VERSION DOESN'T WORK var orders = await new EntityQuery<Order>().With(_entityManagerProvider.Manager).With(QueryStrategy.DataSourceOnly).ExecuteAsync();
|
project test : uploads/1623/TestSignalRSessionKey4.zip - uploads/1623/TestSignalRSessionKey4.zip
|
Posted By: kdev
Date Posted: 25-Nov-2013 at 5:58am
Posted By: sbelini
Date Posted: 26-Nov-2013 at 9:55am
kdev.
I'm checking what in Cocktail could be causing this behavior. I'll follow up shortly.
|
Posted By: kdev
Date Posted: 02-Dec-2013 at 6:41am
Posted By: sbelini
Date Posted: 02-Dec-2013 at 5:39pm
kdev,
You are using compositionContext, so you must specify what context you are using when you login:
var loginOptions = new LoginOptions(compositionContextName: "SignalRDefault");
var em = new EntityManager();
em.AuthenticationContext = Authenticator.Instance.Login(sessionKey, loginOptions);
|
Posted By: kdev
Date Posted: 04-Dec-2013 at 10:07am
|