In Valid Logic

Endlessly expanding technology

How to properly access ASP.NET profile defaults without a profile

Recently, we stumbled upon this indiscrete bug in Community Server with its handling of some things in a user's profile. While debugging, couldn't find much about it on Google, so I thought I'd write up a quick explanation so it might help spare some poor sole from losing hair over it.

In summary of it, there were some edge cases where a user record could exist, but they didn't have a profile record yet (created externally, or for me, created via REST API with none of the profile options set). When a profile record isn't found, then CS just reads the default values as defined in your web.config.

The documentation on how to do this is quite slim, so in our profile handling, we basically had this method:

public override object GetPropertyValue(string propertyKey)
{
  return ProfileBase.Properties[propertyKey].DefaultValue;
}

DefaultValue returns an object, and as the documentation states:

 

 

An object containing the default value of the SettingsProperty object.

 

So when you had a profile entry as so:

<properties>
  ...
  <add name = "timezone" type = "System.Double" defaultValue="0" />
  ...
</properties>

You might assume that when you get the timezone default value, you get a double back. Right? Wrong! Even though DefaultValue returns an object, the actual return value is always a string. The documentation isn't misleading, it is just rather bare. In our case, we expected it to be a double and would get an invalid casting exception (the bug in CS wasn't for the timezone setting, it was a different one).

So how do you properly read the default value and get the expected type? Documentation on this is bare as well, but after sifting through Reflector some, found that other places DefaultValue is accessed also make use of a SettingsPropertyValue class. Aha! The SettingsPropertyValue class is used to perform the serialization on the default value. So for the above, you could quickly fix it like so:

public override object GetPropertyValue(string propertyKey)
{
  return new SettingsPropertyValue(ProfileBase.Properties[propertyKey]).PropertyValue;
}

Wha-la! Magic! Also to help illustrate it better, can throw this snippet into a .aspx page and run it in your browser. It will spit out a quick list of all your profile properties and their values/type using both methods.

<%@ Page Language="C#" AutoEventWireup="true" %>
<script runat="server">
	public void Page_Load(object sender, EventArgs e)
	{
		Response.Write("<table border='1' cellpadding='3'><tr><th>Name</th><th>Type</th><th>Default Value</th><th>Default Value Type</th><th>SettingsPropertyValue</th><th>SettingsPropertyValue Type</tr>");
		foreach (SettingsProperty p in System.Web.Profile.ProfileBase.Properties)
		{
			SettingsPropertyValue spv = new SettingsPropertyValue(p);
			Response.Write( 				string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td></tr>", p.Name, p.PropertyType.FullName, 							p.DefaultValue, p.DefaultValue.GetType().FullName, 							spv.PropertyValue, spv.PropertyValue.GetType().FullName));
		}
		Response.Write("</table>");
	}
</script>

If you encounter something similar, may want to take into account how often you might need to access the value, since in the above, it would de-serialize the value each time. In CS, we actually changed it to lazy-load them and store the value in a dictionary. We also cleaned up some of the profile code so it was more streamlined and could handle the event it was a string (or something else, such as converting an int timezone into a double).

Friday, May 09, 2008

 
blog comments powered by Disqus