Entity Framework Custom Profile Provider

The standard aspnetdb based System.Web.Profile provider stores profile properties in a way that doesn't allow you to efficiently query users by their profile properties.

Here's an example of how the data is stored in the standard aspnet_Profile table:

UserId PropertyNames PropertyValuesString
5BAE09D2-B5B0-45A1-8A2D-FFCBC75E5DCA ExampleA:S:0:4:ExampleB:S:4:8:ExampleC:S:12:6: TE52TE5212Q5TE5212
E32EDEF0-D07E-417E-B68E-FF70E2B1D9E5 ExampleA:S:0:4:ExampleB:S:4:8:ExampleC:S:12:6: TJQGZJQG4MT5ZJQG4M

To get around this limitation I've created a brand new table and my own custom profile provider which uses Entity Framework for data access and includes an extra method for querying against profile properties.

Here's what my aspnet_Profile2 table looks like:

Column Name Data Type Allow Nulls
(PK)Id int
(FK)UserId uniqueidentifier
ExampleA nvarchar(4) X
ExampleB nvarchar(6) X
LastUpdatedTime datetime

The Id, UserId and LastUpdatedTime are mandatory columns which are required by my custom profile provider. ExampleA and ExampleB are my profile property columns.

Here's what my web.config looks like, first of all don't forget your connection string:

<connectionStrings>
    <add name="ApplicationServices" connectionString="data source=server;Initial Catalog=aspnetdb;User Id=sa;Password=password;MultipleActiveResultSets=true" providerName="System.Data.SqlClient" />
</connectionStrings>

Next inside the system.web section here is the modified profile declaration. Important things to note hear are that we need to set a defaultProvider and to inherit from our assembly so that strongly named profile properties can be used. It's also important to give your application a name to avoid deployment issues further down the line. I've commented out the original default profile provider.

    <profile inherits="Acme.MyApplication.ProfileCommon,MyApplication" defaultProvider="EfTableProfileProvider">
      <providers>
        <clear />        
        <add name="EfTableProfileProvider" type="Acme.MyApplication.EfTableProfileProvider,Facade" connectionStringName="ApplicationServices" applicationName="/MyApplication"/>
        <!--<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/MyApplication" />-->        
      </providers>
    </profile>

Here is the ProfileCommon class that inherits form ProfileBase and defines our strongly named profile properties. It also has an expression builder so that we can query profiles by properties. This is the class that should be added to the 'inherits' property in the web.config.

public class ProfileCommon : ProfileBase
    {
        public static ProfileCommon GetProfile()
        {
            return Create(HttpContext.Current.Request.IsAuthenticated ?
                   HttpContext.Current.User.Identity.Name : HttpContext.Current.Request.AnonymousID,
                   HttpContext.Current.Request.IsAuthenticated) as ProfileCommon;
        }

        public static ProfileCommon GetUserProfile(string username)
        {
            return Create(username) as ProfileCommon;
        }

        public static ProfileCommon GetUserProfile()
        {
            return Create(Membership.GetUser().UserName) as ProfileCommon;
        }

        [CustomProviderData("ExampleA;nvarchar")]
        public virtual string WinDSSId
        {
            get
            {
                return ((string)(this.GetPropertyValue("ExampleA")));
            }
            set
            {
                this.SetPropertyValue("ExampleA", value);
            }
        }

        [CustomProviderData("ExampleB;nvarchar")]
        public virtual string SAPId
        {
            get
            {
                return ((string)(this.GetPropertyValue("ExampleB")));
            }
            set
            {
                this.SetPropertyValue("ExampleB", value);
            }
        }

        static IQueryable<T> ETForStartsWith<T>(IQueryable<T> query, string propertyValue, PropertyInfo propertyInfo)
        {
            ParameterExpression e = Expression.Parameter(typeof(T), "e");
            MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
            ConstantExpression c = Expression.Constant(propertyValue, typeof(string));
            MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
            Expression call = Expression.Call(m, mi, c);

            Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
            return query.Where(lambda);
        }
    }

Here is the Entity Framework class that maps to my aspnetdb_Profile2 table. The User object can be found in my previous blog post about creating a custom membership provider, which also contains the data context class.

    [Table("aspnet_Profile2")]
    public class Profile2
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        [ForeignKey("UserId")]
        public virtual User User { get; set; }
        public Guid UserId { get; set; }
        public string ExmapleA { get; set; }
        public string ExampleB { get; set; }
        public DateTime LastUpdatedDate { get; set; }
    }

Finally here's the class that overrides the default profile provider. It has an extra method called FindProfilesByPropertyValue which takes a string representing the name of the property to search and another string containing the value to match.

    public class EfTableProfileProvider : ProfileProvider
    {
        private string _appName;
        private Guid _appId;
        private bool _appIdSet;
        private string _sqlConnectionString;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");
            if (String.IsNullOrEmpty(name))
                name = "EfTableProfileProvider";
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Entity Framework Table Profile Provider");
            }
            base.Initialize(name, config);

            string temp = config["connectionStringName"];
            if (String.IsNullOrEmpty(temp))
                throw new ProviderException("connectionStringName not specified");
            _sqlConnectionString = GetConnectionString(temp);
            if (String.IsNullOrEmpty(_sqlConnectionString))
            {
                throw new ProviderException("connectionStringName not specified");
            }

            _appName = config["applicationName"];
            if (string.IsNullOrEmpty(_appName))
                _appName = GetDefaultAppName();

            if (_appName.Length > 256)
            {
                throw new ProviderException("Application name too long");
            }

            config.Remove("connectionStringName");
            config.Remove("applicationName");
            if (config.Count > 0)
            {
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized))
                    throw new ProviderException("Unrecognized config attribute:" + attribUnrecognized);
            }
        }

        internal static string GetConnectionString(string specifiedConnectionString)
        {
            if (String.IsNullOrEmpty(specifiedConnectionString))
                return null;

            ConnectionStringSettings connObj = ConfigurationManager.ConnectionStrings[specifiedConnectionString];
            if (connObj != null)
                return connObj.ConnectionString;

            return null;
        }

        internal static string GetDefaultAppName()
        {
            try
            {
                string appName = HostingEnvironment.ApplicationVirtualPath;
                if (String.IsNullOrEmpty(appName))
                {
                    appName = System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName;
                    int indexOfDot = appName.IndexOf('.');
                    if (indexOfDot != -1)
                    {
                        appName = appName.Remove(indexOfDot);
                    }
                }

                if (String.IsNullOrEmpty(appName))
                {
                    return "/";
                }
                else
                {
                    return appName;
                }
            }
            catch (SecurityException)
            {
                return "/";
            }
        }

        public override string ApplicationName
        {
            get { return _appName; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("ApplicationName");
                if (value.Length > 256)
                {
                    throw new ProviderException("Application name too long");
                }
                _appName = value;
                _appIdSet = false;
            }
        }

        private Guid AppId
        {
            get
            {
                if (!_appIdSet)
                {
                    try
                    {
                        using (Data.MembershipContext context = new Data.MembershipContext())
                        {
                            Application application = null;
                            application = context.ApplicationData
                                .Where(a => a.LoweredApplicationName.Equals(ApplicationName.ToLower()))
                                .FirstOrDefault();

                            if (application == null)
                            {
                                application = new Application()
                                    {
                                        ApplicationName = ApplicationName,
                                        ApplicationId = Guid.NewGuid(),
                                        Description = "Entity Framework Table Profile Provider",
                                        LoweredApplicationName = ApplicationName.ToLower()
                                    };
                                context.ApplicationData.Add(application);
                                context.SaveChanges();
                            }
                            _appId = application.ApplicationId;
                            _appIdSet = true;
                        }

                    }
                    catch (Exception ex)
                    {
                        Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
                    }

                }
                return _appId;
            }
        }

        private static string s_legalChars = "_@#$";
        private static void EnsureValidTableOrColumnName(string name)
        {
            for (int i = 0; i < name.Length; ++i)
            {
                if (!Char.IsLetterOrDigit(name[i]) && s_legalChars.IndexOf(name[i]) == -1)
                    throw new ProviderException("Table and column names cannot contain: " + name[i]);
            }
        }

        private void GetProfileDataFromTable(SettingsPropertyCollection properties, SettingsPropertyValueCollection svc, string username)
        {
            List<ProfileColumnData> columnData = new List<ProfileColumnData>(properties.Count);

            int columnCount = 0;
            foreach (SettingsProperty prop in properties)
            {
                SettingsPropertyValue value = new SettingsPropertyValue(prop);
                svc.Add(value);

                string persistenceData = prop.Attributes["CustomProviderData"] as string;
                if (String.IsNullOrEmpty(persistenceData))
                {
                    continue;
                }
                string[] chunk = persistenceData.Split(new char[] { ';' });
                if (chunk.Length != 2)
                {
                    continue;
                }
                string columnName = chunk[0];

                SqlDbType datatype = (SqlDbType)Enum.Parse(typeof(SqlDbType), chunk[1], true);

                columnData.Add(new ProfileColumnData(columnName, value, null, datatype));

                ++columnCount;
            }

            try
            {
                Profile2 profile;
                Guid userId;
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    profile = context.TableProfileData
                        .Where(p => p.User.UserName.ToLower().Equals(username))
                        .FirstOrDefault();
                    userId = context.UserData
                        .Where(u => u.UserName.Equals(username))
                        .Select(u => u.UserId)
                        .FirstOrDefault();
                }
                if (profile != null)
                {
                    for (int i = 0; i < columnData.Count; ++i)
                    {
                        ProfileColumnData colData = columnData[i];
                        colData.PropertyValue.PropertyValue = profile.GetType().GetProperty(columnData[i].ColumnName).GetValue(profile, null);
                        colData.Value = colData.PropertyValue.PropertyValue;
                        SettingsPropertyValue propValue = colData.PropertyValue;
                    }
                    UpdateLastActivityDate(userId);
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
        }

        private static void UpdateLastActivityDate(Guid userId)
        {
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    var user = context.UserData
                        .Where(u => u.UserId.Equals(userId))
                        .FirstOrDefault();
                    if (user != null)
                    {
                        user.LastActivityDate = DateTime.Now;
                        context.Entry(user).State = EntityState.Modified;
                        context.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
        }

        public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
        {
            SettingsPropertyValueCollection svc = new SettingsPropertyValueCollection();

            if (collection == null || collection.Count < 1 || context == null)
                return svc;

            string username = (string)context["UserName"];
            if (String.IsNullOrEmpty(username))
                return svc;

            GetProfileDataFromTable(collection, svc, username);

            return svc;
        }

        private struct ProfileColumnData
        {
            public string ColumnName;
            public SettingsPropertyValue PropertyValue;
            public object Value;
            public SqlDbType DataType;

            public ProfileColumnData(string col, SettingsPropertyValue pv, object val, SqlDbType type)
            {
                EnsureValidTableOrColumnName(col);
                ColumnName = col;
                PropertyValue = pv;
                Value = val;
                DataType = type;
            }
        }

        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            string username = (string)context["UserName"];
            bool userIsAuthenticated = (bool)context["IsAuthenticated"];

            if (username == null || username.Length < 1 || collection.Count < 1)
                return;

            try
            {
                bool anyItemsToSave = false;

                foreach (SettingsPropertyValue pp in collection)
                {
                    if (pp.IsDirty)
                    {
                        if (!userIsAuthenticated)
                        {
                            bool allowAnonymous = (bool)pp.Property.Attributes["AllowAnonymous"];
                            if (!allowAnonymous)
                                continue;
                        }
                        anyItemsToSave = true;
                        break;
                    }
                }

                if (!anyItemsToSave)
                    return;

                List<ProfileColumnData> columnData = new List<ProfileColumnData>(collection.Count);

                foreach (SettingsPropertyValue pp in collection)
                {
                    if (!userIsAuthenticated)
                    {
                        bool allowAnonymous = (bool)pp.Property.Attributes["AllowAnonymous"];
                        if (!allowAnonymous)
                            continue;
                    }

                    if (!pp.IsDirty)
                        continue;

                    string persistenceData = pp.Property.Attributes["CustomProviderData"] as string;

                    if (String.IsNullOrEmpty(persistenceData))
                    {
                        continue;
                    }
                    string[] chunk = persistenceData.Split(new char[] { ';' });
                    if (chunk.Length != 2)
                    {
                        continue;
                    }
                    string columnName = chunk[0];

                    SqlDbType datatype = (SqlDbType)Enum.Parse(typeof(SqlDbType), chunk[1], true);

                    object value = null;


                    if (pp.Deserialized && pp.PropertyValue == null)
                    {
                        value = DBNull.Value;
                    }
                    else
                    {
                        value = pp.PropertyValue;
                    }

                    columnData.Add(new ProfileColumnData(columnName, pp, value, datatype));
                }

                Guid userId = Guid.Empty;

                try
                {
                    using (Data.MembershipContext dataContext = new Data.MembershipContext())
                    {
                        Profile2 profile = null;
                        userId = dataContext.UserData
                            .Where(u => u.LoweredUserName.Equals(username.ToLower())
                                && u.ApplicationId.Equals(AppId))
                            .Select(u => u.UserId)
                            .FirstOrDefault();
                        if (userId != null)
                        {
                            profile = dataContext.TableProfileData
                                    .Where(p => p.UserId.Equals(userId))
                                    .FirstOrDefault();
                            if (profile != null)
                            {
                                profile.LastUpdatedDate = DateTime.Now;
                                foreach (ProfileColumnData data in columnData)
                                {
                                    switch (data.ColumnName)
                                    {
                                        case "SAPId":
                                            profile.SAPId = data.Value.ToString();
                                            break;
                                        case "WinDSSId":
                                            profile.WinDSSId = data.Value.ToString();
                                            break;
                                    }
                                }
                                dataContext.Entry(profile).State = EntityState.Modified;
                            }
                            else
                            {
                                profile = new Profile2();
                                profile.UserId = userId;
                                profile.LastUpdatedDate = DateTime.Now;
                                foreach (ProfileColumnData data in columnData)
                                {
                                    switch (data.ColumnName)
                                    {
                                        case "SAPId":
                                            profile.SAPId = data.Value.ToString();
                                            break;
                                        case "WinDSSId":
                                            profile.WinDSSId = data.Value.ToString();
                                            break;
                                    }
                                }
                                dataContext.TableProfileData.Add(profile);
                            }
                        }
                        dataContext.SaveChanges();
                    }
                }
                catch (Exception ex)
                {
                    Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
        }

        public override int DeleteProfiles(ProfileInfoCollection profiles)
        {
            if (profiles == null)
            {
                throw new ArgumentNullException("profiles");
            }

            if (profiles.Count < 1)
            {
                throw new ArgumentException("Profiles collection is empty");
            }

            string[] usernames = new string[profiles.Count];

            int iter = 0;
            foreach (ProfileInfo profile in profiles)
            {
                usernames[iter++] = profile.UserName;
            }

            return DeleteProfiles(usernames);
        }

        public override int DeleteProfiles(string[] usernames)
        {
            if (usernames == null || usernames.Length < 1)
            {
                return 0;
            }
            int numProfilesDeleted = 0;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    var profiles = context.TableProfileData
                        .Where(p => usernames.Contains(p.User.UserName))
                        .ToList();
                    numProfilesDeleted = profiles.Count;
                    profiles.ForEach(p => context.TableProfileData.Remove(p));
                    context.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            return numProfilesDeleted;
        }

        private List<Profile2> GetInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
        {
            List<Profile2> profiles = null;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    switch (authenticationOption)
                    {
                        case ProfileAuthenticationOption.Anonymous:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.LastActivityDate <= userInactiveSinceDate
                                    && p.User.IsAnonymous == true)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.Authenticated:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.LastActivityDate <= userInactiveSinceDate
                                    && p.User.IsAnonymous == false)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.All:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .ToList();
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            return profiles;
        }

        public override int DeleteInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
        {
            int numProfilesDeleted = 0;
            List<Profile2> profiles = null;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    profiles = GetInactiveProfiles(authenticationOption, userInactiveSinceDate);
                    if (profiles != null)
                    {
                        numProfilesDeleted = profiles.Count();
                        profiles.ForEach(p => context.TableProfileData.Remove(p));
                        context.SaveChanges();
                    }
                }
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            return numProfilesDeleted;
        }

        public override int GetNumberOfInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
        {
            int numProfiles = 0;
            List<Profile2> profiles = null;
            try
            {
                profiles = GetInactiveProfiles(authenticationOption, userInactiveSinceDate);
                numProfiles = profiles.Count();
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            return numProfiles;
        }

        public override ProfileInfoCollection GetAllProfiles(ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
        {
            List<Profile2> profiles = new List<Profile2>();
            ProfileInfoCollection profileCollection = new ProfileInfoCollection();
            if (pageIndex < 0)
                throw new ArgumentException("pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("pageSize");
            int lowerBound = pageIndex * pageSize;

            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    switch (authenticationOption)
                    {
                        case ProfileAuthenticationOption.Anonymous:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == true)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.Authenticated:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == false)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.All:
                            profiles = context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId))
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                    }
                }
                profiles.ForEach(p => profileCollection.Add(new ProfileInfo(p.User.UserName, p.User.IsAnonymous, p.User.LastActivityDate, p.LastUpdatedDate, 0)));
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            totalRecords = profiles.Count;
            return profileCollection;
        }

        public override ProfileInfoCollection GetAllInactiveProfiles(ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
        {
            List<Profile2> profiles = new List<Profile2>();
            ProfileInfoCollection profileCollection = new ProfileInfoCollection();
            if (pageIndex < 0)
                throw new ArgumentException("pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("pageSize");
            int lowerBound = pageIndex * pageSize;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    switch (authenticationOption)
                    {
                        case ProfileAuthenticationOption.Anonymous:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == true
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.Authenticated:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == false
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.All:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                    }
                }
                profiles.ForEach(p => profileCollection.Add(new ProfileInfo(p.User.UserName, p.User.IsAnonymous, p.User.LastActivityDate, p.LastUpdatedDate, 0)));
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            totalRecords = profiles.Count;
            return profileCollection;
        }

        public override ProfileInfoCollection FindProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            List<Profile2> profiles = new List<Profile2>();
            ProfileInfoCollection profileCollection = new ProfileInfoCollection();
            if (pageIndex < 0)
                throw new ArgumentException("pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("pageSize");
            int lowerBound = pageIndex * pageSize;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    switch (authenticationOption)
                    {
                        case ProfileAuthenticationOption.Anonymous:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == true
                                    && p.User.UserName.Equals(usernameToMatch))
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.Authenticated:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == false
                                    && p.User.UserName.Equals(usernameToMatch))
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.All:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.UserName.Equals(usernameToMatch))
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                    }
                }
                profiles.ForEach(p => profileCollection.Add(new ProfileInfo(p.User.UserName, p.User.IsAnonymous, p.User.LastActivityDate, p.LastUpdatedDate, 0)));
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            totalRecords = profiles.Count;
            return profileCollection;
        }

        public override ProfileInfoCollection FindInactiveProfilesByUserName(ProfileAuthenticationOption authenticationOption, string usernameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
        {
            List<Profile2> profiles = new List<Profile2>();
            ProfileInfoCollection profileCollection = new ProfileInfoCollection();
            if (pageIndex < 0)
                throw new ArgumentException("pageIndex");
            if (pageSize < 1)
                throw new ArgumentException("pageSize");
            int lowerBound = pageIndex * pageSize;
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    switch (authenticationOption)
                    {
                        case ProfileAuthenticationOption.Anonymous:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == true
                                    && p.User.UserName.Equals(usernameToMatch)
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.Authenticated:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.IsAnonymous == false
                                    && p.User.UserName.Equals(usernameToMatch)
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                        case ProfileAuthenticationOption.All:
                            context.TableProfileData
                                .Where(p => p.User.ApplicationId.Equals(AppId)
                                    && p.User.UserName.Equals(usernameToMatch)
                                    && p.User.LastActivityDate <= userInactiveSinceDate)
                                .OrderBy(p => p.User.UserName)
                                .Skip(lowerBound)
                                .Take(pageSize)
                                .ToList();
                            break;
                    }
                }
                profiles.ForEach(p => profileCollection.Add(new ProfileInfo(p.User.UserName, p.User.IsAnonymous, p.User.LastActivityDate, p.LastUpdatedDate, 0)));
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            totalRecords = profiles.Count;
            return profileCollection;
        }        

        public ProfileInfoCollection FindProfilesByPropertyValue(string PropertyName, object PropertyValue)
        {
            List<Profile2> profiles = new List<Profile2>();
            ProfileInfoCollection profileCollection = new ProfileInfoCollection();
            try
            {
                using (Data.MembershipContext context = new Data.MembershipContext())
                {
                    PropertyInfo propertyInfo = typeof(Profile2).GetProperty(PropertyName); 
                    ParameterExpression e = Expression.Parameter(typeof(Profile2), "e");
                    MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
                    ConstantExpression c = Expression.Constant(PropertyValue, typeof(string));
                    MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
                    Expression call = Expression.Call(m, mi, c);
                    Expression<Func<Profile2, bool>> lambda = Expression.Lambda<Func<Profile2, bool>>(call, e);

                    profiles = context.TableProfileData
                        .Where(p => p.User.ApplicationId.Equals(AppId))
                        .Where(lambda)
                        .OrderBy(p => p.User.UserName)
                        .ToList();

                    profiles.ForEach(p => profileCollection.Add(new ProfileInfo(p.User.UserName, p.User.IsAnonymous, p.User.LastActivityDate, p.LastUpdatedDate, 0)));
                }                
            }
            catch (Exception ex)
            {
                Log.LogException(ex.Message, ex, LogCategory.Membership, TraceEventType.Error);
            }
            return profileCollection;
        }
    }

Here are a couple of Test Methods that demonstrate how to use the custom profile provider. The tests also use the custom membership provider that I blogged about in a previous post.

 [TestMethod]
        public void CreateProfile()
        {
            EfMembershipProvider membershipProvider = (EfMembershipProvider)Membership.Provider;
            MembershipCreateStatus status = new MembershipCreateStatus();
            EfMembershipUser user = (EfMembershipUser)membershipProvider.CreateUser(
                "testcreateprofile",
                "password",
                "",
                "",
                "",
                true,
                Guid.NewGuid(),
                out status);
            ProfileCommon profile = ProfileCommon.GetUserProfile("testcreateprofile");            
            profile.ExampleA = "1234";
            profile.Save();
            profile = ProfileCommon.GetUserProfile("testcreateprofile");
            string ExampleA = profile.ExampleA;
            membershipProvider.DeleteUser("testcreateprofile", true);
            Assert.AreEqual("1234", profile.ExampleA);
        }

        [TestMethod]
        public void FindProfilesByPropertyValue()
        {
            int count = 0;
            EfMembershipProvider membershipProvider = (EfMembershipProvider)Membership.Provider;
            EfTableProfileProvider profileProvider = (EfTableProfileProvider)ProfileManager.Provider;
            MembershipCreateStatus status = new MembershipCreateStatus();
            EfMembershipUser user = (EfMembershipUser)membershipProvider.CreateUser(
                "testfindprofilebypropertyvalue",
                "password",
                "",
                "",
                "",
                true,
                Guid.NewGuid(),
                out status);
            ProfileCommon profile = ProfileCommon.GetUserProfile("testfindprofilebypropertyvalue");
            profile.ExampleA = "4321";
            profile.Save();
            ProfileInfoCollection profilePropertyCollection = profileProvider.FindProfilesByPropertyValue("ExampleA", "4321");
            count = profilePropertyCollection.Count;
            membershipProvider.DeleteUser("testcreateprofile", true);
            Assert.IsTrue(count > 0);
        }

0 comments: