locked
Custom authentication and dynamic connection string RRS feed

  • Question

  • Hi!

    I'm trying to build few RIA forms over old application running on Firebird database.

    There are several challenges I'm struggling with:

    • custom authentication - it doesn't do any table checking, but instead uses provided credentials to login on to database (users are defined in database security system). So, each user, basically needs different connection string and if connection was successful it means user is authenticated
    • connection strings built at runtime - this is required for both authentication and for later database access because user can work with multiple databases and chooses one at application startup.

    I'm looking custom auth examples, but they use model to authenticate user over a table and I need to build custom connection string, try to connect to database and if successful mark user as authenticated.

    For changing connection string at runtime, I found CreateObjectContext() method which I can override and change connection string BUT that happens on MyContext ctx = MyContext.Create() on client side and I couldn't figure out how to send my connection string from client when creating context.
    I figured creating context is equivalent of connecting to database, right?

    My questions:

    • does AuthenticationService needs to inherit from LinqToEntitiesDomainService<Entities> which means it needs model of some kind? If no, how to check database connectivity with provided information? Can I simply use my main model and it's entities?
    • how to pass connection information entered by user on client to DomainService so I can fill that information in CreateObjectContext()?

    Could you please provide code snippets if you have ideas about this since sollutions like "write your own class" doesn't help me much since I'm still new with .net techonolgies and strugling with it (lots of stuff to learn in short time).

    Thanks a lot,

    Mario

     

    Monday, August 16, 2010 5:12 AM

Answers

  •     return new MyEntities("connection string"); //this will be stored in this.ObjectContext
    Yes, but I want "connection string" to be passed from client. 
    I found one solution using Session, but that got me stuck again. 
    Since I need to authenticate towards Firebird database (not against table in database, but I need to supply information to connect to database - these are login credentials as well) I wrote my own FormsAuthenticationService which I implemented like here http://www.astaticstate.com/2010/04/silverlight-custom-authentication.html
    Then I wrote CustomAuthenticationService which looks like this:
        [EnableClientAccess()]
        public class CustomAuthenticationService : FormsAuthenticationService<User>
        {
            protected override User GetCurrentUser(string name, string userData)
            {
                string[] userDataParts = userData.Split(':');
                return new User()
                {
                    Name = name,
                };
            }
            protected override User ValidateCredentials(string name, string password, string customData, out string userData)
            {
                User user = null;
                userData = null;
                using (LinkEntities ctx = new LinkEntities())
                {
                    Korisnik korisnik = ctx.Korisnici.Where(k => k.ID == name).FirstOrDefault();
                    if (korisnik != null || name == "SYSDBA") 
                    {
                        user = new User()
                        {
                            Name = name,
                        };
                    }
                }
                if (user != null)
                {
                    userData = user.Name;
                }
                return user;
            }
            public IEnumerable<DBFile> GetDatabaseList()
            {
                string linkIniLocation = ConfigurationManager.AppSettings["LinkIniLocation"].ToString();
                IniFile ini = new IniFile(linkIniLocation);
                Dictionary<string,string> _dbFiles = ini.Sections["DBFiles"].Parameters;
                var dbFiles = 
                    from entry in _dbFiles
                    select new DBFile { DBName = entry.Key, DBPath = entry.Value };
                
                return dbFiles;
            }
            public void SetDatabase(string user, string pass, string connString)
            {            
                HttpContext.Current.Session["connectionString"] = connString;
                HttpContext.Current.Session["username"] = user;
                HttpContext.Current.Session["password"] = pass;
            }
        }
        [DataContract]
        public class DBFile
        {
            [DataMember]
            [Key]
            public string DBName { get; set; }
            [DataMember]
            public string DBPath { get; set; }
        }
        public class User : UserBase
        {
        }
    Also, I created partial class for my main service:
            protected override LinkEntities CreateObjectContext()
            {
                LinkEntities ctx = null;
                if (HttpContext.Current.Session["connectionString"] != null)
                {
                    string[] dbParam = HttpContext.Current.Session["connectionString"].ToString().Split(':');
                    string connString = EntityConnectionStringHelper.Build(
                        "FirebirdSql.Data.FirebirdClient",
                        dbParam[0], 
                        dbParam[1], 
                        HttpContext.Current.Session["username"].ToString(),
                        HttpContext.Current.Session["password"].ToString(), 
                        "Models.LinkModel");
                    ctx = new LinkEntities(connString);
                }
                if (ctx == null) throw new Exception("No connection information");
                return ctx;
            }
     Finally, my login procedure on client-side looks like this:
     
                LoginParameters lp = new LoginParameters(
                    txtUsername.Text,
                    txtPassword.Text,
                    true,
                    ((DBFile)cbxDBFiles.SelectedItem).DBPath);
                CustomAuthenticationContext ctx = new CustomAuthenticationContext();
                ctx.SetDatabase(lp.UserName, lp.Password, lp.CustomData, (op) =>
                {
                    if (!op.HasError)
                    {
                        WebContext.Current.Authentication.Login(lp, (lo) =>
                        {
                            if (!lo.HasError)
                            {
                                if (WebContext.Current.User.IsAuthenticated) this.DialogResult = true;
                                else (new LgnMessageBox()).Show("Login", "User and password are not correct!");
                            }
                            else
                            {
                                (new LgnMessageBox()).Show("Login error", lo.Error.Message);
                                lo.MarkErrorAsHandled();
                            }
                        }, null);
                    }
                }, null);
    So, what's my idea with this:
    when application starts, program creates instance off CustomAuthenticationContext and calls GetDatabaseList
    then login window is created showing supplied list of databases and allowing user to choose one and to enter username and password
    when user clicks "Login", I create instance of CustomAuthenticationService and call SetDatabase function which sets Session information on server for selected database and supplied user and pass. 
    when SetDatabase is complete, I want to call Login which should authenticate user by trying to create LinkEntities with supplied information. If everything was fine, connection was made, user should be authenticated - but what if everything is not fine? What is the best way to catch errors when creating LinkEntities that will be thrown if connection string is wrong, if user and password are wrong or if database to which it tries connecting is invalid?
    Also, what really happens when you call WebContext.Current.Authentication.Login on client side? Because my first attempt was to only call Login (not calling SetDatabase from client) but then Session information was null. 
    Current code actually works if supplied information is correct. It connects to database and user gets authenticated, but if supplied informations are wrong, then CreateObjectContext is never called (how?!) and I get

         return new MyEntities("connection string"); //this will be stored in this.ObjectContext

     

    Yes, but I want "connection string" to be passed from client. 

    Anyway, I got this sorted in the mean time and would like to share this code if anyone else will have similar learning path. 

    Since I needed to authenticate towards Firebird database system (so, not reading users and passwords from table) I had to write custom authentication and needed to dynamically change connection string of entities. Also, user has ability to connect to different databases which one needs to choose on login form.

    I started with this http://www.astaticstate.com/2010/04/silverlight-custom-authentication.html example to write FormsAuthenticationService. 

    Then I wrote CustomAuthenticationService which extends FormsAuthenticationService and I added this two additional functions inside:

            public IEnumerable<DBFile> GetDatabaseList()
            {
                string linkIniLocation = ConfigurationManager.AppSettings["LinkIniLocation"].ToString();
                IniFile ini = new IniFile(linkIniLocation);
                Dictionary<string,string> _dbFiles = ini.Sections["DBFiles"].Parameters;
                var dbFiles = 
                    from entry in _dbFiles
                    select new DBFile { DBName = entry.Key, DBPath = entry.Value };
                
                return dbFiles;
            }
    
            public void SetDatabase(string user, string pass, string connString)
            {            
                HttpContext.Current.Session["connectionString"] = connString;
                HttpContext.Current.Session["username"] = user;
                HttpContext.Current.Session["password"] = pass;
    
            }
    


    In App.xaml.cs I wrote following code:

            private void Application_Startup(object sender, StartupEventArgs e)
            {
                this.RootVisual = new LayoutContainer();
                CustomAuthenticationContext authCtx = new CustomAuthenticationContext();
                authCtx.Load(authCtx.GetDatabaseListQuery(), (lo) =>
                {
                    if (!lo.HasError)
                    {
                        LoginWnd login = new LoginWnd();
                        login.cbxDBFiles.ItemsSource = lo.Entities;
                        login.Closed += (s, arg) =>
                        {
                            bool? result = login.DialogResult;
                            if (result.HasValue && result.Value)
                            {
                                ((LayoutContainer)this.RootVisual).SwitchControl(new MainPage());
                            }
                        };
                        login.Show();
                    }
                    else
                    {
                        // Provide some error visual here                                
                    }
                }, null);
            }

    I use LayoutContainer (empty UserControl) trick which enables me to "switch" RootVisual fairly easy with this code: ((LayoutContainer)this.RootVisual).SwitchControl(new MainPage()); I got INIFile class from here http://www.c-sharpcorner.com/uploadfile/killermonkey99/inifileclass02072008232619pm/inifileclass.aspx which I liked more than importing Kernel32 functions.
     
    So, I load database list and if success I display login window which has combobox with databases I downloaded and boxes to enter user and pass. When user clicks login, I execute following code:

                LoginParameters lp = new LoginParameters(
                    txtUsername.Text,
                    txtPassword.Text,
                    true,
                    ((DBFile)cbxDBFiles.SelectedItem).DBPath);
    
                CustomAuthenticationContext ctx = new CustomAuthenticationContext();
                ctx.SetDatabase(lp.UserName, lp.Password, lp.CustomData, (op) =>
                {
                    if (!op.HasError)
                    {
                        WebContext.Current.Authentication.Login(lp, (lo) =>
                        {
                            if (lo.LoginSuccess)
                            {
                                if (WebContext.Current.User.IsAuthenticated) this.DialogResult = true;
                                else (new LgnMessageBox()).Show("Login", "User was not authenticated");
                            }
                            else if (lo.HasError)
                            {
                                (new LgnMessageBox()).Show("Error", lo.Error.Message);
                                lo.MarkErrorAsHandled();
                            }
                            else if (!lo.IsCanceled)
                            {
                                (new LgnMessageBox()).Show("Error", "Incorrect user and password");
                            }
                        }, null);
                    }
                }, null);


    As you can see, I first instantiate CustomAuthenticationContext and call SetDatabase which set information that user entered into session. I tried calling SetDatabase from ValidateCredentials in CustomAuthenticationService, but that didn't work - session parameteres were empty when code reached CreateObjectContext. So, I call SetDatabase from client instead and when call is successfuly returned I call Login. In CustomAuthenticationService I overrided only ValidateCredentials which looks like this:

            protected override User ValidateCredentials(string name, string password, string customData, out string userData)
            {
                User user = null;
                userData = null;
    
                // this.SetDatabase(name, password, customData); --> this didn't work, session did not persist into LinkService class
                Korisnik korisnik = null; // Korisnik is entity from EF model
                LinkService svc = new LinkService(); // DomainService created from EF model
                try
                {
                    if (name.ToUpper() == "SYSDBA") korisnik = svc.GetKorisnici().FirstOrDefault();
                    else korisnik = svc.GetKorisnici().Where(k => k.ID == name).FirstOrDefault();
                }
                catch (System.Data.EntityException exc)
                {
                    if (exc.InnerException.Message.Contains("Your user name and password are not defined"))
                    {
                        korisnik = null; name = "";
                    }
                    else throw exc;
                }            
    
                if (korisnik != null || name == "SYSDBA")
                {
                    user = new User()
                    {
                        Name = name,
                    };
                }
                
                // userData is used in FormsAuthenticationService to create auth cookie
                if (user != null)
                {
                    userData = user.Name;
                }
    
                return user;

    With this part I'm still not very happy. I would only want to check if EF can connect to dababase or not, but I don't know how to do that yet, so instead I'm querying table Korisnik which is table holding additional users information, but it is not involved in authentication (which is done by Firebird itself). Problem with that table is that it never holds SYSDBA (root) user, so I need to handle him in code and simply get any user from Korisnik table just to see whether I can do it (correct user and pass) or not (incorrect user and pass). If anyone knows how to check if EF can connect to database without actually getting any data, please tell.

    And last thing for all this to work is overriden CreateObjectContext which I put in LinkService.partial.cs and looks like this:

        public partial class LinkService
        {
            protected override LinkEntities CreateObjectContext()
            {
                LinkEntities ctx = null;
                if (HttpContext.Current.Session["connectionString"] != null)
                {
                    string[] dbParam = HttpContext.Current.Session["connectionString"].ToString().Split(':');
                    string connString = EntityConnectionStringHelper.Build(
                        "FirebirdSql.Data.FirebirdClient",
                        dbParam[0],
                        dbParam[1],
                        HttpContext.Current.Session["username"].ToString(),
                        HttpContext.Current.Session["password"].ToString(),
                        "Models.LinkModel");
                    ctx = new LinkEntities(connString);
                }
    
                if (ctx == null) throw new Exception("No connection information");
    
                return ctx;
    
            }
    


    This is the part that actually sets custom connection information. EntityConnectionHelper is a class I got from here http://mattduffield.wordpress.com/2010/05/20/dynamically-changing-the-connection-string-for-wcf-ria-services-linq2entities/.

    At the end, there are still few questions:

    • what exactly happens when you call WebContext.Current.Authentication.Login?
    • is it possible to check if RIA can connect to database without actually retrieving any entities? Something like: LinkService svc = new LinkService(); svc.CheckDBConnection(); -> calling this kind of method would invoke CreateObjectContext() to setup connection string and would try to connect.

    Anyway, this was one great experience with RIA Services, Silverlight and EF, I learned A LOT about how things works and loved the technology even more. I'm off to earn some money now :)

    Wednesday, August 18, 2010 4:51 AM

All replies

  • Hi,

       Authentication Service does not required to derive from LinqToEntitiesDomainService<Entities>.

       You can write something like:

        public class AuthenticationService:DomainService,IAuthentication<User>
        {

        
        }

        As to let domainservice use use;s input:

        My idea is that when your user click button to change the connectiong string. You can send it back to server and store it in some place. When your domainservice need to create object you use that variable.

    Best Regards

    Tuesday, August 17, 2010 5:05 AM
  • So, you're saying it's impossible to send connection string data from client, instead I need to call a custom service function which accepts connection information and save information somewhere on server? 

    Isn't that a bit problematic when multiple users are logging in? I guess I could create files with some unique user identifier (session information probably) or save information to database, but I would be much happier if I could just send that information directly from client upon creating domain context.

    EDIT:
    Ok, never mind connection string. Let's say connection strings are saved on server. Server sends list of possible connection strings descriptive names, user selects one from the list provided and sends back selected descriptive connection string name. Based on that name, server loads proper connection string and supplies it to CreateObjectContext - how?

    Tuesday, August 17, 2010 7:15 AM
  • Hi,

        Override the CreateObjectContext method.

        And remove that return line, instead add your own one.

        Something like

        return new MyEntities("connection string"); //this will be stored in this.ObjectContext

    Best Regards

    Tuesday, August 17, 2010 10:54 PM
  •     return new MyEntities("connection string"); //this will be stored in this.ObjectContext
    Yes, but I want "connection string" to be passed from client. 
    I found one solution using Session, but that got me stuck again. 
    Since I need to authenticate towards Firebird database (not against table in database, but I need to supply information to connect to database - these are login credentials as well) I wrote my own FormsAuthenticationService which I implemented like here http://www.astaticstate.com/2010/04/silverlight-custom-authentication.html
    Then I wrote CustomAuthenticationService which looks like this:
        [EnableClientAccess()]
        public class CustomAuthenticationService : FormsAuthenticationService<User>
        {
            protected override User GetCurrentUser(string name, string userData)
            {
                string[] userDataParts = userData.Split(':');
                return new User()
                {
                    Name = name,
                };
            }
            protected override User ValidateCredentials(string name, string password, string customData, out string userData)
            {
                User user = null;
                userData = null;
                using (LinkEntities ctx = new LinkEntities())
                {
                    Korisnik korisnik = ctx.Korisnici.Where(k => k.ID == name).FirstOrDefault();
                    if (korisnik != null || name == "SYSDBA") 
                    {
                        user = new User()
                        {
                            Name = name,
                        };
                    }
                }
                if (user != null)
                {
                    userData = user.Name;
                }
                return user;
            }
            public IEnumerable<DBFile> GetDatabaseList()
            {
                string linkIniLocation = ConfigurationManager.AppSettings["LinkIniLocation"].ToString();
                IniFile ini = new IniFile(linkIniLocation);
                Dictionary<string,string> _dbFiles = ini.Sections["DBFiles"].Parameters;
                var dbFiles = 
                    from entry in _dbFiles
                    select new DBFile { DBName = entry.Key, DBPath = entry.Value };
                
                return dbFiles;
            }
            public void SetDatabase(string user, string pass, string connString)
            {            
                HttpContext.Current.Session["connectionString"] = connString;
                HttpContext.Current.Session["username"] = user;
                HttpContext.Current.Session["password"] = pass;
            }
        }
        [DataContract]
        public class DBFile
        {
            [DataMember]
            [Key]
            public string DBName { get; set; }
            [DataMember]
            public string DBPath { get; set; }
        }
        public class User : UserBase
        {
        }
    Also, I created partial class for my main service:
            protected override LinkEntities CreateObjectContext()
            {
                LinkEntities ctx = null;
                if (HttpContext.Current.Session["connectionString"] != null)
                {
                    string[] dbParam = HttpContext.Current.Session["connectionString"].ToString().Split(':');
                    string connString = EntityConnectionStringHelper.Build(
                        "FirebirdSql.Data.FirebirdClient",
                        dbParam[0], 
                        dbParam[1], 
                        HttpContext.Current.Session["username"].ToString(),
                        HttpContext.Current.Session["password"].ToString(), 
                        "Models.LinkModel");
                    ctx = new LinkEntities(connString);
                }
                if (ctx == null) throw new Exception("No connection information");
                return ctx;
            }
     Finally, my login procedure on client-side looks like this:
     
                LoginParameters lp = new LoginParameters(
                    txtUsername.Text,
                    txtPassword.Text,
                    true,
                    ((DBFile)cbxDBFiles.SelectedItem).DBPath);
                CustomAuthenticationContext ctx = new CustomAuthenticationContext();
                ctx.SetDatabase(lp.UserName, lp.Password, lp.CustomData, (op) =>
                {
                    if (!op.HasError)
                    {
                        WebContext.Current.Authentication.Login(lp, (lo) =>
                        {
                            if (!lo.HasError)
                            {
                                if (WebContext.Current.User.IsAuthenticated) this.DialogResult = true;
                                else (new LgnMessageBox()).Show("Login", "User and password are not correct!");
                            }
                            else
                            {
                                (new LgnMessageBox()).Show("Login error", lo.Error.Message);
                                lo.MarkErrorAsHandled();
                            }
                        }, null);
                    }
                }, null);
    So, what's my idea with this:
    when application starts, program creates instance off CustomAuthenticationContext and calls GetDatabaseList
    then login window is created showing supplied list of databases and allowing user to choose one and to enter username and password
    when user clicks "Login", I create instance of CustomAuthenticationService and call SetDatabase function which sets Session information on server for selected database and supplied user and pass. 
    when SetDatabase is complete, I want to call Login which should authenticate user by trying to create LinkEntities with supplied information. If everything was fine, connection was made, user should be authenticated - but what if everything is not fine? What is the best way to catch errors when creating LinkEntities that will be thrown if connection string is wrong, if user and password are wrong or if database to which it tries connecting is invalid?
    Also, what really happens when you call WebContext.Current.Authentication.Login on client side? Because my first attempt was to only call Login (not calling SetDatabase from client) but then Session information was null. 
    Current code actually works if supplied information is correct. It connects to database and user gets authenticated, but if supplied informations are wrong, then CreateObjectContext is never called (how?!) and I get

         return new MyEntities("connection string"); //this will be stored in this.ObjectContext

     

    Yes, but I want "connection string" to be passed from client. 

    Anyway, I got this sorted in the mean time and would like to share this code if anyone else will have similar learning path. 

    Since I needed to authenticate towards Firebird database system (so, not reading users and passwords from table) I had to write custom authentication and needed to dynamically change connection string of entities. Also, user has ability to connect to different databases which one needs to choose on login form.

    I started with this http://www.astaticstate.com/2010/04/silverlight-custom-authentication.html example to write FormsAuthenticationService. 

    Then I wrote CustomAuthenticationService which extends FormsAuthenticationService and I added this two additional functions inside:

            public IEnumerable<DBFile> GetDatabaseList()
            {
                string linkIniLocation = ConfigurationManager.AppSettings["LinkIniLocation"].ToString();
                IniFile ini = new IniFile(linkIniLocation);
                Dictionary<string,string> _dbFiles = ini.Sections["DBFiles"].Parameters;
                var dbFiles = 
                    from entry in _dbFiles
                    select new DBFile { DBName = entry.Key, DBPath = entry.Value };
                
                return dbFiles;
            }
    
            public void SetDatabase(string user, string pass, string connString)
            {            
                HttpContext.Current.Session["connectionString"] = connString;
                HttpContext.Current.Session["username"] = user;
                HttpContext.Current.Session["password"] = pass;
    
            }
    


    In App.xaml.cs I wrote following code:

            private void Application_Startup(object sender, StartupEventArgs e)
            {
                this.RootVisual = new LayoutContainer();
                CustomAuthenticationContext authCtx = new CustomAuthenticationContext();
                authCtx.Load(authCtx.GetDatabaseListQuery(), (lo) =>
                {
                    if (!lo.HasError)
                    {
                        LoginWnd login = new LoginWnd();
                        login.cbxDBFiles.ItemsSource = lo.Entities;
                        login.Closed += (s, arg) =>
                        {
                            bool? result = login.DialogResult;
                            if (result.HasValue && result.Value)
                            {
                                ((LayoutContainer)this.RootVisual).SwitchControl(new MainPage());
                            }
                        };
                        login.Show();
                    }
                    else
                    {
                        // Provide some error visual here                                
                    }
                }, null);
            }

    I use LayoutContainer (empty UserControl) trick which enables me to "switch" RootVisual fairly easy with this code: ((LayoutContainer)this.RootVisual).SwitchControl(new MainPage()); I got INIFile class from here http://www.c-sharpcorner.com/uploadfile/killermonkey99/inifileclass02072008232619pm/inifileclass.aspx which I liked more than importing Kernel32 functions.
     
    So, I load database list and if success I display login window which has combobox with databases I downloaded and boxes to enter user and pass. When user clicks login, I execute following code:

                LoginParameters lp = new LoginParameters(
                    txtUsername.Text,
                    txtPassword.Text,
                    true,
                    ((DBFile)cbxDBFiles.SelectedItem).DBPath);
    
                CustomAuthenticationContext ctx = new CustomAuthenticationContext();
                ctx.SetDatabase(lp.UserName, lp.Password, lp.CustomData, (op) =>
                {
                    if (!op.HasError)
                    {
                        WebContext.Current.Authentication.Login(lp, (lo) =>
                        {
                            if (lo.LoginSuccess)
                            {
                                if (WebContext.Current.User.IsAuthenticated) this.DialogResult = true;
                                else (new LgnMessageBox()).Show("Login", "User was not authenticated");
                            }
                            else if (lo.HasError)
                            {
                                (new LgnMessageBox()).Show("Error", lo.Error.Message);
                                lo.MarkErrorAsHandled();
                            }
                            else if (!lo.IsCanceled)
                            {
                                (new LgnMessageBox()).Show("Error", "Incorrect user and password");
                            }
                        }, null);
                    }
                }, null);


    As you can see, I first instantiate CustomAuthenticationContext and call SetDatabase which set information that user entered into session. I tried calling SetDatabase from ValidateCredentials in CustomAuthenticationService, but that didn't work - session parameteres were empty when code reached CreateObjectContext. So, I call SetDatabase from client instead and when call is successfuly returned I call Login. In CustomAuthenticationService I overrided only ValidateCredentials which looks like this:

            protected override User ValidateCredentials(string name, string password, string customData, out string userData)
            {
                User user = null;
                userData = null;
    
                // this.SetDatabase(name, password, customData); --> this didn't work, session did not persist into LinkService class
                Korisnik korisnik = null; // Korisnik is entity from EF model
                LinkService svc = new LinkService(); // DomainService created from EF model
                try
                {
                    if (name.ToUpper() == "SYSDBA") korisnik = svc.GetKorisnici().FirstOrDefault();
                    else korisnik = svc.GetKorisnici().Where(k => k.ID == name).FirstOrDefault();
                }
                catch (System.Data.EntityException exc)
                {
                    if (exc.InnerException.Message.Contains("Your user name and password are not defined"))
                    {
                        korisnik = null; name = "";
                    }
                    else throw exc;
                }            
    
                if (korisnik != null || name == "SYSDBA")
                {
                    user = new User()
                    {
                        Name = name,
                    };
                }
                
                // userData is used in FormsAuthenticationService to create auth cookie
                if (user != null)
                {
                    userData = user.Name;
                }
    
                return user;

    With this part I'm still not very happy. I would only want to check if EF can connect to dababase or not, but I don't know how to do that yet, so instead I'm querying table Korisnik which is table holding additional users information, but it is not involved in authentication (which is done by Firebird itself). Problem with that table is that it never holds SYSDBA (root) user, so I need to handle him in code and simply get any user from Korisnik table just to see whether I can do it (correct user and pass) or not (incorrect user and pass). If anyone knows how to check if EF can connect to database without actually getting any data, please tell.

    And last thing for all this to work is overriden CreateObjectContext which I put in LinkService.partial.cs and looks like this:

        public partial class LinkService
        {
            protected override LinkEntities CreateObjectContext()
            {
                LinkEntities ctx = null;
                if (HttpContext.Current.Session["connectionString"] != null)
                {
                    string[] dbParam = HttpContext.Current.Session["connectionString"].ToString().Split(':');
                    string connString = EntityConnectionStringHelper.Build(
                        "FirebirdSql.Data.FirebirdClient",
                        dbParam[0],
                        dbParam[1],
                        HttpContext.Current.Session["username"].ToString(),
                        HttpContext.Current.Session["password"].ToString(),
                        "Models.LinkModel");
                    ctx = new LinkEntities(connString);
                }
    
                if (ctx == null) throw new Exception("No connection information");
    
                return ctx;
    
            }
    


    This is the part that actually sets custom connection information. EntityConnectionHelper is a class I got from here http://mattduffield.wordpress.com/2010/05/20/dynamically-changing-the-connection-string-for-wcf-ria-services-linq2entities/.

    At the end, there are still few questions:

    • what exactly happens when you call WebContext.Current.Authentication.Login?
    • is it possible to check if RIA can connect to database without actually retrieving any entities? Something like: LinkService svc = new LinkService(); svc.CheckDBConnection(); -> calling this kind of method would invoke CreateObjectContext() to setup connection string and would try to connect.

    Anyway, this was one great experience with RIA Services, Silverlight and EF, I learned A LOT about how things works and loved the technology even more. I'm off to earn some money now :)

    Wednesday, August 18, 2010 4:51 AM
  • Great information.  However I am still not able to build the connection string I need.

    I have a need to allow the client to select which database and server to access.  There are hundreds of databases and I don't want to have to add connection strings in web.config each time something changes.  I also don't want the user context to have to keep track of all these databases and servers.

    I need a way to create a connection string for the DomainService based on data selected by the user.  Does anyone have any idea how I could use data from the client to dynamically create a connection string within the DomainService?

    Thanks,

    -Scott

    Saturday, December 4, 2010 7:02 PM
  • I realize you may not be interested in what I am proposing ...however if you are interested in windows integrated authentication

    their is another way and you can use it with windows azure and silverlight or in a silverlight wcf ria service

    It works with iis 7.5 and it was mentioned in silverlight firestarter. It is windows idnetity foundation    http://blogs.msdn.com/b/endpoint/archive/2009/11/21/claims-based-security-with-windows-identity-foundation-wif-and-wcf.aspx 

       http://blogs.msdn.com/b/eugeniop/archive/2009/10/09/ria-services-and-windows-identity-foundation-claims-enabling-a-ria-application.aspx

    Saturday, December 4, 2010 7:27 PM
  • Hi!

    Well, that's exactly what I'm doing in my example. You can't construct and send Entity Framework connection string from client, but there is workaround which I used.

    Perhaps my example was a bit confusing since I used connectionString session variable which supposedly is coming from server, but that connectionString is not entity framework connection string, but firebird specific in form server:database. That string is then used to dynamically construct entity connection string in overridden CreateObjectContext(). 

    So, basically, I'm presenting user an option to select one of many databases he can connects to, then I send his choice to server and server dynamically constructs entity framework connection string. 

    Please examine my example once more, having in mind that connectionString is Firebird specific and not EF one. EF one is constructed in CreateObjectContext();
    Also, this could work the same if I asked the user to enter server name and database, but I'm making user's life easier by getting all available databases on server and then sending that list to user so he can choose from it instead having to remember server and database name for each database.  

    Sunday, December 5, 2010 3:39 AM