IAuthenticationService throws an object not set exception

Dec 12, 2011 at 4:06 PM
Edited Jan 23, 2012 at 5:43 PM

Hello,

I'm trying to use the AuthenticationService to authenticate against Asp.net SQL server infrastructure.... I've defined my own edmx called SecurityEdmx ,

created the view/viewmodel (here's the model)

 

 

  [Export]
    public class LoginViewModel : Screen
    {
        private readonly IAuthenticationService _authenticationService;

        private string _userName;
        private string _password;

        public string UserName
        {
            get
            {
                return _userName;
            }
            set
            {
                _userName = value;

                NotifyOfPropertyChange(() => UserName);
            }

        }

        public string Password
        {
            get
            {
                return _password;
            }
            set
            {
                _password = value;

                NotifyOfPropertyChange(() => Password);
            }

        }

        [ImportingConstructor]
        public LoginViewModel(IAuthenticationService authenticationService)
        {
            this.DisplayName = "Login";
            _authenticationService = authenticationService;
        }

        public void Login()
        {

            var loginCredential = new LoginCredential(_userName, _password, null);

//exception here
            _authenticationService.LoginAsync(loginCredential, () =>
                {
                    int i = 0;
                    //close the loginform
                }, e =>
                    {
                        int i = 0;
                        //display error message
                    });
        }

        public void PasswordChanged(System.Windows.Controls.PasswordBox source)
        {
            Password = source.Password;
        }

 

In the bootstrapper I've set this :

 

 

    protected override void Configure()
        {
            var catalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

            container = new CompositionContainer(catalog);

            var batch = new CompositionBatch();

            IWindowManager windowManager = new WindowManager();

            var provider = new EntityManagerProvider();


            var r = new myRepository(provider);

            ConventionManager.AddElementConvention<RadMenuItem>(ItemsControl.ItemsSourceProperty, "DataContext", "Click");

            EventAggregator.Subscribe(windowManager);
            batch.AddExportedValue<IEventAggregator>(EventAggregator);
            batch.AddExportedValue<IWindowManager>(windowManager);
            batch.AddExportedValue<IApplicationRepository>(r);
            batch.AddExportedValue<IAuthenticationService>(new AuthenticationService<SecurityEntities>());
            batch.AddExportedValue(container);
          
            container.Compose(batch);
            container.SatisfyImportsOnce(this);
        }

 

I've tried to put in the InitializeCompositeBatch override but I get an exception that my LoginViewModel is not defined (I think since it can't do IoC properly, since it wont' find an export valut for IAuthenticationService)

Any suggestion?

My Edmx has EntityManager named = SecurityEntities and it point to the aspnet db created with aspnet_regsql.exe

 

Thanks

Coordinator
Dec 14, 2011 at 1:45 PM

Sorry about the delay. I'm currently travelling overseas. First of all why did you create a security edmx when you want to authenticate against ASP.NET? ASP.NET will do all the security related stuff. You just have to configure the membership provider, role provider etc. in the web.config and than enable ASP.NET authentication for DevForce, which will use the ASP.NET out-of-the-box LoginManager.

http://drc.ideablade.com/xwiki/bin/view/Documentation/security-aspnet

Second, what the heck are you trying to do there in your bootstrapper? That's all wrong. First of all if you do override a method, call the base method for god's sake unless you have a very good reason not to! Here everything falls apart because you are not calling base.Configure() and instead try to initialize the MEF catalog and container yourself, which is not going to work at all. Also, I'm already putting the EventAggregator and WindowManager into the container for example. The only thing you should need in the Configure method is that CM convention you are adding. Injecting the AuthenticationService should go into InitializeCompositionBatch just like documented here:

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

The repository should be properly exported and have an ImportingConstructor for the EntityManager provider. Just like documented here:

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

Dec 14, 2011 at 2:26 PM
Edited Jan 23, 2012 at 5:44 PM

Hello Marcel,

sorry for my mistakes but I'm new... and the documentation I've found till now hasn't permit me to develop a good code :(.....

by the way I've tried moving all the batch.AssExportedValue to the InitializeCompositionBatch but I got a strange problem...

if I do

 

    protected override void Configure()
        {
            base.Configure();
            ConventionManager.AddElementConvention<RadMenuItem>(ItemsControl.ItemsSourceProperty, "DataContext", "Click");
        }

        protected override void InitializeCompositionBatch(CompositionBatch batch)
        {
            base.InitializeCompositionBatch(batch);
            var catalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());
            container = new CompositionContainer(catalog);

            IWindowManager windowManager = new WindowManager();

            var provider = new EntityManagerProvider();

            var myRepository = new myRepository(provider);

            EventAggregator.Subscribe(windowManager);
            batch.AddExportedValue<IEventAggregator>(EventAggregator);
            batch.AddExportedValue<IWindowManager>(windowManager);
            batch.AddExportedValue<IApplicationRepository>(myRepository);

            ////var securityRepository = new SecurityEntities();


            ////var admin = new AuthenticationService<SecurityEntities>(securityRepository);
            ////batch.AddExportedValue<IAuthenticationService>(admin);
            batch.AddExportedValue(container);

            container.Compose(batch);
            container.SatisfyImportsOnce(this);

            //base.InitializeCompositionBatch(batch);
        }

I got the following exception :

 

{"The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.\r\n\r\n1) More than one export was found that matches the constraint '((exportDefinition.ContractName == \"Caliburn.Micro.IEventAggregator\") AndAlso (exportDefinition.Metadata.ContainsKey(\"ExportTypeIdentity\") AndAlso \"Caliburn.Micro.IEventAggregator\".Equals(exportDefinition.Metadata.get_Item(\"ExportTypeIdentity\"))))'.\r\n\r\nResulting in: Cannot set import ''"}

 

if I call the base.InitializeCompositionBatch(batch); at the end it seems ok.... why is this happening?

Thanks

Coordinator
Dec 14, 2011 at 2:50 PM

Because as I already said, the following two things are already done by the framework for you. You are getting duplicates and there can only be one EventAggregator and one WindowManager application wide. If you call base.InitializationCompositionBatch() I'm detecting the duplicates and that's why it works, but you shouldn't put them in the batch in the first place.

            batch.AddExportedValue<IEventAggregator>(EventAggregator);
            batch.AddExportedValue<IWindowManager>(windowManager);

Also, why are you subscribing the WindowManager to the EventAggregator? That doesn't make much sense.

The method should look like this:

        protected override void InitializeCompositionBatch(CompositionBatch batch)
        {
            base.InitializeCompositionBatch(batch);
            batch.AddExportedValue<IAuthenticationService>(new AuthenticationService<YourApplicationsEntityManagerType>());
        }

That's all you need. Then to make your repository available, decorate it properly with an [Export] attribute.

[Export(typeof(IApplicationRepository)]
public class DOMRepository : IApplicationRepository
{

   [ImportingConstructor]
   public DOMRepository(IEntityManagerProvider<YourApplicationsEntityManagerType> entityManagerProvider)  // EMP will be injected for you
   {
   }

}

Dec 14, 2011 at 3:06 PM

Thanks Marcel,

It helped out, but I still have another question for you.

I would like to detect a failed login possibly intercepting a false result from the _authenticationService.Login(loginCredential) calling.

Is there a way to force the method to return false?

public void Login()
        {
            var loginCredential = new LoginCredential(_userName, _password, null);
            try
            {
                bool isLogged = _authenticationService.Login(loginCredential);

                int res = 0;
            }
            catch (LoginException ex)
            {
                int i = 0;
            }
        }

Here's my EntityLoginManager

  public class myLoginManage : IEntityLoginManager
    {
        public System.Security.Principal.IPrincipal Login(ILoginCredential credential, EntityManager entityManager)
        {
            if (credential.UserName == "test" && credential.Password == "test")
            {
                var identity = new UserIdentity(credential.UserName, "test", true);
                var principal = new UserBase(identity);
                return principal;
            }
            else
            {
                //I would like the _authenticationService.Login to return false... here it returns me true
                throw new LoginException(LoginExceptionType.InvalidUserName, "Invalid username"); //this raise an exception... doesn't set to false
            }
        }

        public void Logout(System.Security.Principal.IPrincipal principal, EntityManager entityManager)
        {
            int i = 0;
        }
    }

Thanks

Coordinator
Dec 14, 2011 at 7:38 PM

The return value comes from DevForce and I have no control over it in DAF. The return value is actually pretty useless, because it can only ever return true. If the login fails it throws an exception. If at any time you need to find out if the user is logged in or not, you would check the IsLoggedIn property of the authentication service.

Dec 15, 2011 at 11:31 AM

Hello Marcel,

perfect, I was baffled by the return value... that's ok... I've another question for you  (I've found no solution on the samples)

I've got a SP I use to authenticate and it's present in my edmx how do I specify myEntity in the login function?

He'res my code

 

  public class myLoginManager : IEntityLoginManager
    {
        public System.Security.Principal.IPrincipal Login(ILoginCredential credential, EntityManager entityManager)
        {
            entityManager = new myEntities(); //tried this but didn't work.. when I call the SP_LOGIN it returns here with credential=null

            if (credential == null)
                throw new LoginException(LoginExceptionType.NoCredentials, "Fornire credenziali");

            int idOffice= ConfigurationHelper.GetValueFromSettings<Int32>("IDOffice");

            var op = myEntity.SP_LOGIN(idOffice, credential.UserName, credential.Password);

            return null;
           
        }

        public void Logout(System.Security.Principal.IPrincipal principal, EntityManager entityManager)
        {
          
        }
    }

I think I'm missing something really stupid... but can't figure out what

Thanks

Coordinator
Dec 15, 2011 at 2:30 PM

You clone the provided entityManager into your concrete type.

var myEntity = new myEntities(entityManager);

var op = myEntity.SP_LOGIN(...)