Custom LoginManager (IEntityLoginManager) problem

Jul 14, 2011 at 8:37 PM
Edited Jul 14, 2011 at 8:38 PM

I'm having a problem with the server side custom LoginManager (IEntityLoginManager).
The basic code is from the DevForce Business Template, and the login framework that Devforce has converted from the WCF RIA template.

In a test/do nothing scenario all works fine. Add some code to  public IPrincipal Login(ILoginCredential credential, EntityManager entityManager){} and of course the method returns fine to the Login Prompt with an guest or fake user.

I'm now adding code to connect to a real database to validate the login. I'm not using ASP membership, just a user table.
I can write a CoRoutine.Start call appropriately which works fine. But of course the Login method returns to the login prompt before the callbacks have returned with the user from the database.
In the case of a Customer Repository it does not matter as the "grid" will be blank until the property notify binding updates the "grid" sometime later.

In the case of a Login prompt you only want to return when you have validated the login - clearly not a problem in WPF, but it is in Silverlight.
Well, that's the way the Template's written - an onSuccess callback which closes the prompt window.
So I can't see how the template's code would have ever worked for real - or there is something more I can do in the server code to fake a blocking action ( e.g while forever, doevents !!).

The DF business template is written to use a now obsolete call to Login. I've changed it to use LoginAsync, with a OnSuccess and OnFail handler.
Perhaps this is the reason, if I did not have an OnFail, the prompt would never respond to the initial null user return.
However, the OnSuccess handle does not receive any reuturn values so there is no way to inform user of a failed login and not close the prompt.
I can start modifying the signatures to use a OnSuccess with return values and control the way i dispose of the prompt.

I'm just wondering if there is a more accepted way of dealing with this situation?

I was also wondering whether you've a Caliburn Micro (no code behind) version of this login system that you're likely to contribute to your framework?

My loginManager code is below.
Thanks

John

   public class LoginManager : IEntityLoginManager
    {
        public IPrincipal Login(ILoginCredential credential, EntityManager entityManager)
        {
            _credential = credential;
            _entityManager = entityManager;

            if (_credential == null)
                return new UserBase(new UserIdentity("Anynoymous"));

            _manager = new CoProjectEntities(_entityManager);
            var coop = Coroutine.Start(ValidateUser);

            coop.Completed += (s,e) =>
            {
                var res = e.Result;
                //Do something....
            };
           
            return _user; //initially returns with a null user.
        }

        public IEnumerable<INotifyCompleted> ValidateUser()
        {
            IEntityQuery<User> query = _manager.Users.Where(a => a.UserName.Equals(_credential.UserName));
            EntityQueryOperation<User> op = query.ExecuteAsync();
            yield return op;
           
            var r = op.Results;
            //Do Something
           
            yield break;
        }
        //etc

Jul 14, 2011 at 10:14 PM

OK, I got a solution...
I need to access a UserRepository from the LoginViewModel and do the initial validation of the user in the client. This first call to the server is as a guest user at the server.
I can then call the server LoginAsync to update the login user.
I'm still thinking of my 2-tier initial example where a Generic Principle would apply to both the pseudo client and server both running inProcess.
Whereas a real WebServer would be running against a service account, perhaps using Windows authentication.
In my 2-tier I'd put all the code in the LoginManager where it would block.

Anyway, I'd be pleased if you could comment of the normal practise, as I've not concluded this from the DF documentation.

thanks

John

Coordinator
Jul 14, 2011 at 11:08 PM

I'm not following everything, but looking at your LoginManager I see the main issue. The world on the server is synchronous. You don't use Coroutines on the server. The Login method in the LoginManager is completely synchronous. You simply query the given EntityManager synchronously to retrieve any data you need in order to validate the user.

On the client side, the DevForce-Caliburn framework provides the AuthenticationService class to handle the login. It will by asynchronous and once the Login method on the server completes, the asynchronous login call will return on the client. See the HelloWorld solution for how the AuthenticationService is used. There's also a topic in the documentation:

http://devforcecaliburn.codeplex.com/wikipage?title=The%20Authentication%20Service&referringTitle=Documentation

If you are having issues with the Silverlight Business Template, please contact support.

Coordinator
Jul 14, 2011 at 11:11 PM

Just in case you are not clear about how to execute a query synchronously. Here's an example taking your query:

 

IEntityQuery<User> query = _manager.Users.Where(a => a.UserName.Equals(_credential.UserName));

var result = query.ToList();   // Synchronously executes the query and assigns the result

Coordinator
Jul 14, 2011 at 11:13 PM

Or:

IEntityQuery<User> query = _manager.Users.Where(a => a.UserName.Equals(_credential.UserName));

var user = query.FirstOrDefault()   // Synchronously executes the query and returns the first user or null.

The above query will result in a SELECT TOP 1 ...., so the database only returns the first record or an empty result set.

Jul 15, 2011 at 12:20 AM

Wow! Thanks! How embarrising....
I'd just copied "query.ExecuteAsync();" into my LoginManager (from my repository code) and thus caused myself the confusion!
First time trying anything in Silverlight.
Got the wrong idea from the code snippet at
http://drc.ideablade.com/ApiDocumentation/IdeaBlade.EntityModel~IdeaBlade.EntityModel.IEntityLoginManager.html

thanks again
John