custom implementation of asp.net ProfileProvider

Latest post 05-28-2008 5:32 PM by Pat. 6 replies.
  • 05-24-2008 5:19 AM

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    custom implementation of asp.net ProfileProvider

    here's a custom implementation of the asp.net profile provider working with the system.web.profile namespace

     public class AsteryxProfileProvider : ProfileProvider
        {
            public AsteryxProfileProvider () { }

            public string UserName { get; set; }

            public override string ApplicationName
            {
                get { return AppSettings.ApplicationName(); }
                set { }
            }

            public override void Initialize(string name, NameValueCollection config)
            {
                base.Initialize(name, config);
            }

            private void GetUsername(SettingsContext context)
            {
                UserName = ((string)context["UserName"]).Remove(0, ((string)context["UserName"]).IndexOf(@"\") + 1);
            }

            private List<ProfileProperty> GetPropertyValues()
            {
                DataSet ds = ProfileDA.GetProfileData(UserName, ApplicationName);
                List<ProfileProperty> props = new List<ProfileProperty>();
                foreach (DataRow row in ds.Tables[0].Rows)
                    props.Add(new ProfileProperty((String)row["PropertyName"], (String)row["StringValue"], (Byte[])row["BinaryValue"]));
                return props;
            }

            public override System.Configuration.SettingsPropertyValueCollection GetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyCollection collection)
            {
                GetUsername(context);
                List<ProfileProperty> Propertys = GetPropertyValues();
                System.Configuration.SettingsPropertyValue propValue = null;
                System.Configuration.SettingsPropertyValueCollection Values = new System.Configuration.SettingsPropertyValueCollection();

                foreach (System.Configuration.SettingsProperty property in collection)
                {
                    propValue = new System.Configuration.SettingsPropertyValue(property);

                    ProfileProperty ProfileProp = Propertys.Find(delegate(ProfileProperty Predicate) { return Predicate.PropertyName == property.Name; });
                    if (ProfileProp == null)
                    {
                        if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(String))
                        {
                            propValue.PropertyValue = null;
                            propValue.SerializedValue = null;
                            propValue.Deserialized = false;
                        }
                    }
                    else
                    {
                        propValue.Deserialized = false;
                        if (property.SerializeAs == SettingsSerializeAs.String)
                        {
                            propValue.SerializedValue = ProfileProp.StringValue;
                        }
                        else if (property.SerializeAs == SettingsSerializeAs.Binary)
                        {
                            propValue.SerializedValue = ProfileProp.BinaryValue;
                        }
                    }
                    Values.Add(propValue);
                }

                return Values;
            }

            /// <summary>
            ///
            /// </summary>
            /// <param name="context"></param>
            /// <param name="collection"></param>
            public override void SetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyValueCollection collection)
            {
                GetUsername(context);
                ProfileProperty NewProfileProp = null;

                foreach (System.Configuration.SettingsPropertyValue PropertyValue in collection)
                {
                    if (PropertyValue.IsDirty && !PropertyValue.UsingDefaultValue)
                    {                {
                        NewProfileProp = new ProfileProperty();
                        NewProfileProp.PropertyName = PropertyValue.Name;

                        if (PropertyValue.Deserialized && PropertyValue.Property.SerializeAs != SettingsSerializeAs.Binary)
                        {
                            NewProfileProp.StringValue = PropertyValue.PropertyValue.ToString();
                            NewProfileProp.BinaryValue = new Byte[0];
                        }
                        else if (!PropertyValue.Deserialized && PropertyValue.Property.SerializeAs != SettingsSerializeAs.Binary)
                        {
                            NewProfileProp.StringValue = PropertyValue.PropertyValue.ToString();
                            NewProfileProp.BinaryValue = new Byte[0];
                        }
                        else if (PropertyValue.Deserialized && PropertyValue.Property.SerializeAs == SettingsSerializeAs.Binary)
                        {
                            NewProfileProp.BinaryValue = (Byte[])PropertyValue.SerializedValue;
                            NewProfileProp.StringValue = String.Empty;
                        }
                        else if (!PropertyValue.Deserialized && PropertyValue.Property.SerializeAs == SettingsSerializeAs.Binary)
                        {
                            MemoryStream strm = new System.IO.MemoryStream();
                            BinaryFormatter bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                            bin.Serialize(strm, PropertyValue.PropertyValue);
                            Byte[] PropertyData = new Byte[strm.Length];
                            strm.Seek(0, System.IO.SeekOrigin.Begin);
                            strm.Read(PropertyData, 0, PropertyData.Length);
                            NewProfileProp.BinaryValue = PropertyData;
                            NewProfileProp.StringValue = String.Empty;
                        }

                        ProfileDA.SetProfileData(UserName, NewProfileProp.PropertyName, NewProfileProp.StringValue, NewProfileProp.BinaryValue, ApplicationName);
                    }
                }
            }
     
     //Other functions not implemented
        }


        public class ProfileProperty
        {
            public int SystemID { get; set; }
            public int EmployeeID { get; set; }
            public String Ntlogin { get; set; }
            public String PropertyName { get; set; }
            public String StringValue { get; set; }
            public Byte[] BinaryValue { get; set; }
            public DateTime LastUpdated { get; set; }
            public ProfileProperty(string propertyName, string strValue, Byte[] binaryValue)
            {
                PropertyName = propertyName;
                StringValue = strValue;
                BinaryValue = binaryValue;
            }
            public ProfileProperty() { }
        }


        public static class ActiveUserCounter
        {
            private static DateTime LastTime = Convert.ToDateTime("1/1/1900");
            private static int UserCount = 0;

            public static int GetUserCount(string ntlogin)
            {
                if (UserCount == 0
                    || DateTime.Now.AddMinutes(-1) > LastTime)
                {
                    UserCount = ProfileDA.CountApplicationUsers(AppSettings.ApplicationName(), ntlogin);
                    LastTime = DateTime.Now;
                }
                return UserCount;
            }
        }

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

  • 05-24-2008 5:22 AM In reply to

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    Re: custom implementation of asp.net ProfileProvider

     Here's the data access class to go along with this...

     public static class ProfileDA
        {
            public static DataSet GetProfileData(string ntlogin, string systemName)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT p.PropertyName, p.StringValue, p.BinaryValue ");
                sb.Append("FROM [dbo].[tbl_Profiles] p WITH(NOLOCK) INNER JOIN [dbo].[tbl_Systems] s ON p.SystemID = s.SystemID ");
                sb.Append("WHERE p.Ntlogin = @Ntlogin AND s.SystemName = @SystemName");
                List<DBManager.DBParameters> paramList = new List<DBManager.DBParameters>();
                paramList.Add(new DBManager.DBParameters("@Ntlogin", ntlogin));
                paramList.Add(new DBManager.DBParameters("@SystemName", systemName));
                return DBManager.ExplicitQuery(Properties.Settings.Default.MainDB, sb.ToString(), paramList);
            }

            public static void SetProfileData(string ntlogin, string propertyName, string strValue, Byte[] binaryValue, string systemName)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("UPDATE [dbo].[tbl_Profiles] SET ");
                sb.Append("StringValue = @StringValue, BinaryValue = @BinaryValue, LastUpdated = GETDATE() ");
                sb.Append("WHERE EXISTS (SELECT [SystemID] FROM [dbo].[tbl_Systems] WITH(NOLOCK) WHERE SystemName = @SystemName) ");
                sb.Append("AND Ntlogin = @Ntlogin AND PropertyName = @PropertyName ");
                sb.Append("if @@ROWCOUNT = 0 BEGIN DECLARE @sysID int ");
                sb.Append("SELECT @SysID = [SystemID] FROM [dbo].[tbl_Systems] WITH(NOLOCK) WHERE SystemName = @SystemName ");
                sb.Append("INSERT INTO [dbo].[tbl_Profiles] ([SystemID],[Ntlogin],[PropertyName],[StringValue],[BinaryValue],LastUpdated) ");
                sb.Append("VALUES (@SysID, @Ntlogin, @PropertyName, @StringValue, @BinaryValue, GETDATE()) END ");

                List<DBManager.DBParameters> paramList = new List<DBManager.DBParameters>();
                paramList.Add(new DBManager.DBParameters("@SystemName", systemName));
                paramList.Add(new DBManager.DBParameters("@Ntlogin", ntlogin));
                paramList.Add(new DBManager.DBParameters("@PropertyName", propertyName));
                paramList.Add(new DBManager.DBParameters("@StringValue", strValue));
                paramList.Add(new DBManager.DBParameters("@BinaryValue", binaryValue));

                DBManager.DbNonQuery(Properties.Settings.Default.MainDB, sb.ToString(), paramList);
            }

            public static int CountApplicationUsers(string applicationName, string ntlogin)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT COUNT(DISTINCT NTLogin)+1 FROM ]dbo].[tbl_Profiles] WITH(NOLOCK) ");
                sb.Append("WHERE EXISTS (SELECT [SystemID] FROM [dbo].[tbl_Systems] WITH(NOLOCK) WHERE SystemName = @SystemName) ");
                sb.Append("AND Ntlogin <> @Ntlogin AND (DateLastAccessed > DATEADD(n, - 10, GETDATE())) ");
                List<DBManager.DBParameters> paramList = new List<DBManager.DBParameters>();
                paramList.Add(new DBManager.DBParameters("@SystemName", applicationName));
                paramList.Add(new DBManager.DBParameters("@Ntlogin", ntlogin));
                return DBManager.ExecuteSQL(Properties.Settings.Default.MainDB, sb.ToString(), paramList);
            }
        }

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

  • 05-24-2008 5:27 AM In reply to

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    Re: custom implementation of asp.net ProfileProvider

     here's the sql script to create the table...

    Only EmployeeID field or the Ntlogin field is needed and should be a foreign key to the employee table

    CREATE TABLE [dbo].[tbl_Profiles](
     [ProfileID] [int] IDENTITY(1,1) NOT NULL,
     [SystemID] [int] NULL,
     [EmployeeID] [int] NULL,
     [Ntlogin] [varchar](8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
     [PropertyName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
     [StringValue] [nvarchar](3000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
     [BinaryValue] [image] NULL,
     [LastUpdated] [datetime] NULL,
     CONSTRAINT [PK_tbl_DOPQ_Profiles] PRIMARY KEY CLUSTERED
    (
     [ProfileID] ASC
    ) ON [PRIMARY]
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

    ALTER TABLE [dbo].[tbl_Profiles]  WITH CHECK ADD  CONSTRAINT [FK_tbl_Profiles_tbl_Systems] FOREIGN KEY([SystemID])
    REFERENCES [dbo].[tbl_Systems] ([SystemID])

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

  • 05-26-2008 5:35 PM In reply to

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    Re: custom implementation of asp.net ProfileProvider

    Here are better SetPropertyValues and SetProfileData methods that only make 1 database call by buildinding a single sql statement to execute instead of executing a statement for each property.

       public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            GetUsername(context);
            UpdatePropertiesWrapper upw = new UpdatePropertiesWrapper(UserName, ApplicationName);

            foreach (SettingsPropertyValue PropertyValue in collection)
            {
                if (PropertyValue.IsDirty && !PropertyValue.UsingDefaultValue)
                {
                    upw.PropertyNames.Add(PropertyValue.Name);
                    if (PropertyValue.Deserialized && PropertyValue.Property.SerializeAs != SettingsSerializeAs.Binary)
                    {
                        upw.StringValues.Add(PropertyValue.PropertyValue.ToString());
                        upw.BinaryValues.Add(new Byte[0]);
                    }
                    else if (!PropertyValue.Deserialized && PropertyValue.Property.SerializeAs != SettingsSerializeAs.Binary)
                    {
                        upw.StringValues.Add(PropertyValue.PropertyValue.ToString());
                        upw.BinaryValues.Add(new Byte[0]);
                    }
                    else if (PropertyValue.Deserialized && PropertyValue.Property.SerializeAs == SettingsSerializeAs.Binary)
                    {
                        upw.BinaryValues.Add((Byte[])PropertyValue.SerializedValue);
                        upw.StringValues.Add(String.Empty);
                    }
                    else if (!PropertyValue.Deserialized && PropertyValue.Property.SerializeAs == SettingsSerializeAs.Binary)
                    {
                        MemoryStream strm = new System.IO.MemoryStream();
                        BinaryFormatter bin = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        bin.Serialize(strm, PropertyValue.PropertyValue);
                        Byte[] PropertyData = new Byte[strm.Length];
                        strm.Seek(0, System.IO.SeekOrigin.Begin);
                        strm.Read(PropertyData, 0, PropertyData.Length);
                        upw.BinaryValues.Add(PropertyData);
                        upw.StringValues.Add(String.Empty);
                    }
                }
            }
            if (upw.PropertyNames.Count > 0)
                AsteryxProfileDA.SetProfileData(upw);
        }

     

    This wrapper class is used to collect property values to send to the data layer

    public class UpdatePropertiesWrapper
    {
        public String UserName { get;set;}
        public String ApplicationName { get;set;}
        public List<String> PropertyNames { get;set;}
        public List<String> StringValues { get;set;}
        public List<Byte[]> BinaryValues { get;set;}

        public UpdatePropertiesWrapper(String userName, String applicationName)
        {
            UserName = userName;
            ApplicationName = applicationName;
            PropertyNames = new List<String>();
            StringValues = new List<String>();
            BinaryValues = new List<Byte[]>();
        }

     

    Here's the data access method...

     

     public static void SetProfileData(UpdatePropertiesWrapper upw)
        {
            List<DBManager.DBParameters> paramList = new List<DBManager.DBParameters>();
            paramList.Add(new DBManager.DBParameters("@ApplicationName", upw.ApplicationName));
            paramList.Add(new DBManager.DBParameters("@UserName", upw.UserName));

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < upw.PropertyNames.Count; i++)
            {
                sb.Append("UPDATE [tbl_Asteryx_Profiles] SET ");
                sb.Append("StringValue = @StringValue" + i.ToString() + ", BinaryValue = @BinaryValue" + i.ToString() + ", LastUpdated = GETDATE() ");
                sb.Append("WHERE SystemName = @ApplicationName ");
                sb.Append("AND UserName = @UserName AND PropertyName = @PropertyName" + i.ToString() + " ");
                sb.Append("if @@ROWCOUNT = 0 Begin ");
                sb.Append("INSERT INTO [tbl_Asteryx_Profiles] ([SystemName],[UserName],[PropertyName],[StringValue],[BinaryValue],LastUpdated) ");
                sb.Append("VALUES (@ApplicationName, @UserName, @PropertyName" + i.ToString() + ", @StringValue" + i.ToString() + ", @BinaryValue" + i.ToString() + ", GETDATE()) END ");

                paramList.Add(new DBManager.DBParameters("@PropertyName" + i.ToString(), upw.PropertyNames[i]));
                paramList.Add(new DBManager.DBParameters("@StringValue" + i.ToString(), upw.StringValues[i]));
                paramList.Add(new DBManager.DBParameters("@BinaryValue" + i.ToString(), upw.BinaryValues[i]));
            }
            DBManager.DbNonQuery("", sb.ToString(), paramList);
        }

     

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

  • 05-26-2008 5:50 PM In reply to

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    Re: custom implementation of asp.net ProfileProvider

     Here's a custom class that works with the asp.net profileprovider that will return the number of active users for an application.

     

    • It is a static class that will only make 1 database call based on the last time interval.

        public static class ActiveUserCounter
        {
            private static DateTime LastTime = Convert.ToDateTime("1/1/1900");
            private static int UserCount = 0;

            public static int GetUserCount(string ntlogin)
            {
                if (UserCount == 0
                    || DateTime.Now.AddMinutes(-1) > LastTime)
                {
                    UserCount = ProfileDA.CountApplicationUsers(AppSettings.ApplicationName(), ntlogin);
                    LastTime = DateTime.Now;
                }
                return UserCount;
            }
        }

     

    • Here is the data access method that will retrieve a count of distinct users.  This is set to grab users active in the last 10 mins.

            public static int CountApplicationUsers(string applicationName, string ntlogin)
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT COUNT(DISTINCT NTLogin)+1 FROM ]dbo].[tbl_Profiles] WITH(NOLOCK) ");
                sb.Append("WHERE EXISTS (SELECT [SystemID] FROM [dbo].[tbl_Systems] WITH(NOLOCK) WHERE SystemName = @SystemName) ");
                sb.Append("AND Ntlogin <> @Ntlogin AND (DateLastAccessed > DATEADD(n, - 10, GETDATE())) ");
                List<DBManager.DBParameters> paramList = new List<DBManager.DBParameters>();
                paramList.Add(new DBManager.DBParameters("@SystemName", applicationName));
                paramList.Add(new DBManager.DBParameters("@Ntlogin", ntlogin));
                return DBManager.ExecuteSQL(Properties.Settings.Default.MainDB, sb.ToString(), paramList);
            }
        }

     

     

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

  • 05-28-2008 1:59 PM In reply to

    • Nick
    • Top 10 Contributor
    • Joined on 04-28-2008
    • Posts 27

    Re: custom implementation of asp.net ProfileProvider

    Why don't you use the UserIsOnlineTimeWindow as the time variable instead of hard coding it as 10?
    What is this? Amateur hour?

     

     

  • 05-28-2008 5:32 PM In reply to

    • Pat
    • Top 10 Contributor
      Male
    • Joined on 04-27-2008
    • Tempe, AZ
    • Posts 45

    Re: custom implementation of asp.net ProfileProvider

     You should write that up and post a follow-up.  Always room for improvement

    Patrick McNamara, BS-IS/CS, MBA, MAED
    ASP.NET Web Application Developer
    Asteryx, LLC.
    http://asteryx.com
    pat@asteryx.com

    Filed under:
Page 1 of 1 (7 items) | RSS
Forums to discuss Microsoft ASP.Net Development and SQL
Powered by Community Server (Non-Commercial Edition), by Telligent Systems