Friday, October 27, 2006

ASP.NET 2.0 Profile object from web service

You have started to use ASP.NET 2.0 Profile Provider and you are very happy with it. All your .aspx pages are full of "Profile.Something". You also introduced a lot of new properties on the profile object via web.config. Then you added a new web service. There you want to access the Profile object. You realize, you are doomed.

You cannot access the Profile object from Web Service.

At runtime, ASP.NET generates a class looking at the specification provided in web.config, which becomes the "Profile" object in .aspx pages. But this object is not available in Web service (.asmx.cs) and you cannot see the custom properties you have added in the profile object.

Although HttpContext.Current.Profile will give you reference to Profile object, but it's type is ProfileBase which does not show your custom properties. Makes sense, because the class is generated at runtime. But if it can be made available in .aspx.cs, then it should also be available in .asmx.cs.

In order to overcome this problem, you have to hand code that profile class in your App_Code folder and then configure web.config so that it does not auto generate the class instead use your one.

Here's what you do in web.config:





I have added a new attribute UserProfile.

Now go to App_Code and make a UserProfile class like this:

public class UserProfile : System.Web.Profile.ProfileBase{
---[SettingsAllowAnonymousAttribute(true)]
---public virtual int Timezone{
------get{return ((int)(this.GetPropertyValue("Timezone")));}
------set{this.SetPropertyValue("Timezone", value);}
---}
}


Declare all the properties like this.

Don't forget to add the [SettingsAllowAnonymousAttribute(true)] on the properties which you want to be made available to anonymous users.

At the end of the class, add this method:

public virtual ProfileCommon GetProfile(string username){
---return ((ProfileCommon) (ProfileBase.Create(username)));
}


Here's an easy way to avoid hand coding this class and generating it automatically. Before you make the changes in web.config and create the UserProfile class, run your web project as it was before. But before running it, turn off SQL Server. This will make Asp.net execution to break on first call to some Profile object's property. For ex, if you have a custom property TimeZone in the Profile object, execution will break on this line:

public virtual int Timezone{
get{ return ((int)(this.GetPropertyValue("Timezone")));


It will fail to load the profile object values from database because database is down. If you scroll up, you will see this is the class that ASP.NET generates at run time. You will see all the properties are declared on this class already. So, you can just copy & paste it in your own class easily!

But after copying, you will realize there's no [SettingsAllowAnonymousAttribute(true)] attribute. So, you will have to put them manually. Also after making your own custom class, you will have to remove all the custom properties declared inside node in the web.config.

Now that you have your own Profile class, inside web service, you can cast (HttpContext.Current.Profile as UserProfile) and then you can use all the custom properties.

If you don't want to enjoy strongly typed coding on web service, then you can always use the old way of accessing Profile properties via: Profile.GetPropertyValue("TimeZone"). But it's no fun.

No comments: