views:

97

answers:

2

I'm trying to add the sample table profile provider from http://www.asp.net/downloads/sandbox/table-profile-provider-samples to a new MVC 2 site.

After a bit of research and fiddling around, I've arrived at a profile class that looks like this.

namespace MyNamespace.Models
{
    public class UserProfile : ProfileBase
    {
        [SettingsAllowAnonymous(false),CustomProviderData("FirstName;nvarchar")]
        public string FirstName
        {
            get { return base["FirstName"] as string; }
            set { base["FirstName"] = value; }
        }

        [SettingsAllowAnonymous(false),CustomProviderData("LastName;nvarchar")]
        public string LastName
        {
            get { return base["LastName"] as string; }
            set { base["LastName"] = value; }
        }

        public static UserProfile GetUserProfile(string username)
        {
            return Create(username,false) as UserProfile;
        }

        public static UserProfile GetUserProfile()
        {
            return Create(Membership.GetUser().UserName,true) as UserProfile;
        }
    }
}

And a web.config like

<profile enabled="true" defaultProvider="TableProfileProvider" inherits="MyNamespace.Models.UserProfile">
  <providers>
    <clear />
    <add name="TableProfileProvider" type="Microsoft.Samples.SqlTableProfileProvider" connectionStringName="ContentDB" table="aspnet_UserProfiles" applicationName="/"/>
  </providers>
</profile>

Things I think I've found out along the way are

  • Using a custom provider with MVC requires the "inherits" attribute on the <profile> element in web.config, which precludes the use of a <properties><add ....> construct with the same profile field name.
  • The sample SQL table profile provider needs the CustomProviderData attribute and, because of the above, it cannot appear in the web.config file, so needs to be added as an attribute to the properties in the profile class.

It all seems to work OK once a user is logged in. However, I want to capture some profile data as part of the new user registration process, and I cannot seem to access the profile object until the user has logged in.

I've tried adding a call to save profile data in the new user registration section of the MVC template code:

FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
UserProfile profile = UserProfile.GetUserProfile(Membership.GetUser().UserName);
profile.FirstName = "Doug";
Profile.Save();
return RedirectToAction("Index", "Home");

However it seems that Membership.GetUser() is null until the user actually logs in. I also tried using the user's name from the model.

FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
UserProfile profile = UserProfile.GetUserProfile(model.UserName);
profile.FirstName = "Doug";
profile.Save();
return RedirectToAction("Index", "Home");

This gets a bit further, but fails when trying to set the FirstName profile field, with an error message along the lines of "trying to set an attribute as an anonymous user, but this is not allowed" (sorry, don't have access to the exact message as I'm typing this).

Is there any way round this? It looks like the FormsServer.SignIn method does not actually log the user in as far as forms authentication is concerned, and it needs a round trip to be fully logged in, presumably needing the cookie to be submitted back to the server.

If there's no easy way round this I could populate the profile table directly using data access methods (insert into aspnet_UserProfiles ....). Is this a bodge too far, or is it a viable solution?

Thanks in advance.

Doug

A: 

Hasn't anyone got this problem? No? Just me then!

Just to update, I've tried the suggestion Franci Penov makes in his answer to this posting.

So, now my code looks like this.

            FormsService.SignIn(model.UserName, false /* createPersistentCookie */);

            GenericIdentity id = new GenericIdentity(model.UserName);
            HttpContext.User = new GenericPrincipal(id, null);

            UserProfile profile = UserProfile.GetUserProfile(Membership.GetUser().UserName) as UserProfile;
            profile.FirstName = "Doug";
            profile.Save();

            return RedirectToAction("Index", "Home");

Now, at least the call to Membership.GetUser() returns a valid MembershipUser object, but trying to set the FirstName profile property still results in the message This property cannot be set for anonymous users.

So, the user is logged on as far as Membership is concerned, but the Profile system still thinks not.

Any ideas?

Doug
+1  A: 

Hurrah!

Reading this posting even more closely, I thought I'd try calling the profile Initialize method explicitly, and it worked!

Final full code for the register action method is:

[HttpPost]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);

        if (createStatus == MembershipCreateStatus.Success)
        {
            FormsService.SignIn(model.UserName, false /* createPersistentCookie */);

            GenericIdentity id = new GenericIdentity(model.UserName);
            HttpContext.User = new GenericPrincipal(id, null);

            UserProfile profile = UserProfile.GetUserProfile(Membership.GetUser().UserName) as UserProfile;
            profile.Initialize(Membership.GetUser().UserName, true);
            profile.FirstName = "Doug";
            profile.Save();

            return RedirectToAction("Index", "Home");
        }
        else
        {
            ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
        }
    }

    // If we got this far, something failed, redisplay form
    ViewData["PasswordLength"] = MembershipService.MinPasswordLength;
    return View(model);
}

Hope this helps.

Doug

Doug

related questions