Search code examples
c#-4.0asp.net-profiles

How To Assign Complex Profile Values


Based on this article I was able to get the FullName to work rather easily.

I have the following class that has child classes as well:

[DataContract]
[Serializable]
public class SettingSection
{
    public SettingSection()
    {
        this.UserSettings = new List<UserSettingPair>();
    } // SettingSection - Constructor

    public SettingSection(List<UserSettingPair> UserSettings)
    {
        this.UserSettings = UserSettings;
    } // SettingSection - Constructor

    [DataMember(Name = "sectionName")]
    public string SectionName { get; set; }

    [DataMember(Name = "userSettings")]
    public List<UserSettingPair> UserSettings { get; set; }

} // SettingSection - Class

[DataContract]
[Serializable]
public class UserSettingPair
{
    [DataMember(Name = "key")]
    public string Key { get; set; }

    [DataMember(Name = "value")]
    public string Value { get; set; }
} // UserSettingPair - Class

I then have a way to serialize this into JSon with the following code:

public static string Serialize<T>(object input)
{
    string Result = "";
    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));

    using (MemoryStream ms = new MemoryStream())
    {
        ser.WriteObject(ms, input);
        Result = Encoding.Default.GetString(ms.ToArray());
    }

    return Result;
}

When I do what is in the above article, the following works:

UserProfileContract.CurrentUser.FullName = "Testing";

When I try it with my List/Complex object (now a json formatting string)...

base["sectionSettings"] = (Utilities.Serialize<List<SettingSection>>(Settings)).ToString();
Save();

I get the following error (note above I even double forced it to a string with the .ToString() but no luck:

The settings property 'sectionSettings' is of a non-compatible type.

I am clearly doing something wrong, I have to assume I am not the first out there who wants to save json data in the ASP.Net default Profile provider. Any help would be greatly appreciated.


Solution

  • After doing some tests where I put the Utilities.Serialize... code into the working FullName, that worked. Then I renamed FullName to FullNameXX and it failed. After a few more tests I came to the conclusion that all of the properties to be saved in the Profile need to be strings (or Binary I assume but I am not using the PropertyValuesBinary data field in SQL.) So when I had a complex field, like my List then I had a property for the programmers that gets and sets a List as well as a string version of the same property that gets stored. So I now have a SectionSettings and a SectionSettingsValue property. Another adjustment needed was to make it only a DataContract and NOT Serializable or you will get extra values like k__backingField which IMHO looks bad.

    The following should be the complete code...

    Here is my DataContract:

    [DataContract]
    public class UserProfileContract : ProfileBase
    {
    
        #region Constructors
    
        public UserProfileContract()
        {
        } // UserProfileContract - Constructor
    
        public UserProfileContract(List<SettingSection> SectionSettings)
        {
            this.SectionSettings = SectionSettings;
        } // UserProfileContract - Constructor
    
        #endregion Constructors
    
        public static UserProfileContract CurrentUser
        {
            get { return (UserProfileContract)(ProfileBase.Create(Membership.GetUser().UserName)); }
        }
    
        public string FullNameValue { get; set; }
        public string SectionSettingsValue { get; set; }
    
        [DataMember(Name = "FullName")]
        public string FullName
        {
            get { return ((string)(base["FullNameValue"])); }
            set {
                base["FullNameValue"] = value;
                Save();
            }
        } // FullName - Property
    
        [DataMember(Name = "SectionSettings")]
        public List<SettingSection> SectionSettings
        {
            get { return Utilities.Deserialize<List<SettingSection>>(base["SectionSettingsValue"].ToString()); }
            set
            {
                base["SectionSettingsValue"] = Utilities.Serialize<List<SettingSection>>(value);
                Save();
            }
        } // SectionSettings - Property
    
    } // UserProfileContract - Class
    
    [DataContract]
    public class SettingSection
    {
        public SettingSection()
        {
            this.UserSettings = new List<UserSettingPair>();
        } // SettingSection - Constructor
    
        public SettingSection(List<UserSettingPair> UserSettings)
        {
            this.UserSettings = UserSettings;
        } // SettingSection - Constructor
    
        [DataMember]
        public string SectionName { get; set; }
    
        [DataMember]
        public List<UserSettingPair> UserSettings { get; set; }
    
    } // SettingSection - Class
    
    [DataContract]
    public class UserSettingPair
    {
        [DataMember]
        public string Key { get; set; }
    
        [DataMember]
        public string Value { get; set; }
    } // UserSettingPair - Class
    

    Here is my static Utilities class:

    public static T Deserialize<T>(string json)
    {
        var obj = Activator.CreateInstance<T>();
    
        if (string.IsNullOrWhiteSpace(json))
            return obj;
    
        using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
        {
            var serializer = new DataContractJsonSerializer(obj.GetType());
            obj = (T)serializer.ReadObject(ms);
    
            return obj;
        } // using the memory stream
    } // Deserialize - Method
    
    public static string Serialize<T>(object input)
    {
        string Result = "";
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
    
        using (MemoryStream ms = new MemoryStream())
        {
            ser.WriteObject(ms, input);
            Result = Encoding.Default.GetString(ms.ToArray());
        }
    
        return Result;
    } // Serialize - Method
    

    Here is an example of how to Save data in multiple sections as well as the FullName property:

    // First Section
    SettingSection s = new SettingSection();
    s.SectionName = "ContractSearch";
    
    UserSettingPair usp = new UserSettingPair();
    usp.Key = "Default Control";
    usp.Value = "txtFirstTextBox";
    
    s.UserSettings.Add(usp);
    
    usp = new UserSettingPair();
    usp.Key = "Field1Choice";
    usp.Value = "SchoolName";
    
    s.UserSettings.Add(usp);
    
    List<SettingSection> ss = new List<SettingSection>();
    ss.Add(s);
    
    // Second Section
    s = new SettingSection();
    s.SectionName = "Workflow Settings";
    
    usp = new UserSettingPair();
    usp.Key = "Primart Thing";
    usp.Value = "Blabla bla";
    
    s.UserSettings.Add(usp);
    
    usp = new UserSettingPair();
    usp.Key = "Allowable Tries";
    usp.Value = "3";
    
    s.UserSettings.Add(usp);
    
    usp = new UserSettingPair();
    usp.Key = "Extra Value";
    usp.Value = "Gigity";
    
    s.UserSettings.Add(usp);
    ss.Add(s);
    
    UserProfileContract.CurrentUser.FullName = "Grigsby";
    UserProfileContract.CurrentUser.SectionSettings = ss;
    

    Here is an Extension Method I created to make extraction of SectionSetting values easier:

    public static T GetSectionValue<T>(this UserProfileContract up, string Section, string Property)
    {
        string value = (from ss in up.SectionSettings
                                            from us in ss.UserSettings
                                            where ss.SectionName == Section
                                            && us.Key == Property
                                            select us.Value).FirstOrDefault();
    
        try
        {
            return (T)Convert.ChangeType(value, typeof(T));
        }
        catch (InvalidCastException)
        {
            return default(T);
        }
    } // GetSectionValue - Extension Method
    

    And lastly, an example of the above Extension method:

    string k = x.GetSectionValue<string>("Workflow Settings", "Primary Thing");
    string g = x.GetSectionValue<string>("Workflow Settings", "Extra Value");
    int three = x.GetSectionValue<int>("Workflow Settings", "Allowable Tries");
    

    Here is the string version of the values I put in:

    [{"SectionName":"ContractSearch","UserSettings":[{"Key":"Default Control","Value":"txtFirstTextBox"},{"Key":"Field1Choice","Value":"SchoolName"}]},{"SectionName":"Workflow Settings","UserSettings":[{"Key":"Primart Thing","Value":"Blabla bla"},{"Key":"Allowable Tries","Value":"3"},{"Key":"Extra Value","Value":"Gigity"}]}]Grigsby
    

    In the above string k = ... example, note that it returns null because the data is "Primart Thing" and not "Primary Thing".

    Hope this helps someone out there.