110

I don't know what I am missing, but I added Profile properties in the Web.config file but cannot access Profile.Item in the code or create a new profile.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
zsharp
  • 13,656
  • 29
  • 86
  • 152

10 Answers10

179

I had the same problem today, and learned a lot.

There are two kinds of project in Visual Studio -- "Web Site Projects" and "Web Application Projects." For reasons which are a complete mystery to me, Web Application Projects cannot use Profile. directly... the strongly-typed class is not magically generated for you from the Web.config file, so you have to roll your own.

The sample code in MSDN assumes you are using a Web Site Project, and they tell you just to add a <profile> section to your Web.config and party on with Profile.property, but that doesn't work in Web Application Projects.

You have two choices to roll your own:

(1) Use the Web Profile Builder. This is a custom tool you add to Visual Studio which automatically generates the Profile object you need from your definition in Web.config.

I chose not to do this, because I didn't want my code to depend on this extra tool to compile, which could have caused problems for someone else down the line when they tried to build my code without realizing that they needed this tool.

(2) Make your own class that derives from ProfileBase to represent your custom profile. This is easier than it seems. Here's a very very simple example that adds a "FullName" string profile field:

In your web.config:

<profile defaultProvider="SqlProvider" inherits="YourNamespace.AccountProfile">

<providers>
     <clear />
     <add name="SqlProvider"
          type="System.Web.Profile.SqlProfileProvider"
          connectionStringName="sqlServerMembership" />
</providers>

</profile>

In a file called AccountProfile.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Profile;
using System.Web.Security;

namespace YourNamespace
{
    public class AccountProfile : ProfileBase
    {
        static public AccountProfile CurrentUser
        {
            get { return (AccountProfile)
                         (ProfileBase.Create(Membership.GetUser().UserName)); }
        }

        public string FullName
        {
            get { return ((string)(base["FullName"])); }
            set { base["FullName"] = value; Save(); }
        }

        // add additional properties here
    }
}

To set a profile value:

AccountProfile.CurrentUser.FullName = "Snoopy";

To get a profile value

string x = AccountProfile.CurrentUser.FullName;
Sky Sanders
  • 36,396
  • 8
  • 69
  • 90
Joel Spolsky
  • 33,372
  • 17
  • 89
  • 105
  • @joel so can I assume SO is using Profiles, and has proven to be scalable or was this research abandoned or intended for something else? – Simon_Weaver Oct 24 '09 at 22:43
  • Great answer, helped me too, I sort of had the same question. – Francisco Noriega Dec 03 '09 at 01:26
  • 10
    StackOverflow itself doesn't use profiles. This code is probably not fast enough for a site with 1,000,000 page views a day. I was working on another project when I wrote this. – Joel Spolsky Dec 03 '09 at 03:55
  • If you are implementing this and receive "Unable to cast object of type 'ProfileCommon' to type 'XYZ'", then see http://stackoverflow.com/questions/79129/implementing-profile-provider-in-asp-net-mvc. (I used 'ProfileCommon' instead of 'AccountProfile' as my class name). – Even Mien Dec 14 '09 at 21:58
  • You may also want to use [CustomProviderData] attribute. – majkinetor Mar 19 '10 at 13:16
  • 1
    It's my own fault, but I struggled for quite a while with setting properties after calling CurrentUser. Since CurrentUser instantiates and returns a new instance each time, can I suggest defining it as a method instead? Without thinking, I wrote this: AccountProfile.CurrentUser.FullName = "Snoopy"; AccountProfile.CurrentUser.OtherProperty = "ABC"; AccountProfile.CurrentUser.Save(); which doesn't work. It should be: AccountProfile currentProfile = AccountProfile.CurrentUser; currentProfile.FullName = "Snoopy"; currentProfile.OtherProperty = "ABC"; currentProfile.Save(); – Jeremy Gruenwald Apr 07 '10 at 14:00
  • 1
    Doesn't this kind of defeat the whole purpose of Profile? You can't use "Profile.MyProperty", you have to use your own home-grown class, which isn't set up on every page the way Profile automatically is. All this does is let you utilize the Save() feature without having to code it. – Mike K Jul 09 '10 at 00:40
  • 7
    Web Application Projects cannot use Profile. – Joel Spolsky Jul 09 '10 at 01:35
  • @joel Actually Web Application Projects can use the auto-generated class. It is just not available at compile-time. See my answer below for details. – colivier Jun 04 '11 at 13:35
  • 2
    If you accidentally define profile properties in the web config under `` as well as in the AccountProfile class, you'll get a "This property has already been defined," easily fixed by removing the properties in the web.config. – Zachary Scott May 25 '12 at 14:09
  • Bless you...for giving a Web App option ! – granadaCoder Jun 18 '15 at 21:41
  • I had to use 'inherits="YourNamespace.AccountProfile, MyAssemblyName">' but small potatoes. Thanks! – granadaCoder Jun 18 '15 at 21:51
  • Can you try/comment on a DateTimeOffset ? Example : public DateTimeOffset BirthDateUTC { get { return ((DateTimeOffset)(base["BirthDateUTC"])); } set { base["BirthDateUTC"] = value; Save(); } } – granadaCoder Jun 18 '15 at 22:00
  • Yeah, I can save/read an Int32, Int64, but not a DateTimeOffset. :( – granadaCoder Jun 18 '15 at 22:04
  • If you got to the end of all these comments and are still stuck... I recommend Bede Amarasekara's answer, with a long climb up the stack to get the notice it deserves. – fortboise Nov 17 '15 at 23:57
17

Web Application Projects can still use the ProfileCommon object but only at runtime. The code for it is just not generated in the project itself but the class is generated by ASP.Net and is present at runtime.

The simplest way to get to object is to use a dynamic type as demonstrated below.

In the Web.config file declare the profile properties:

<profile ...
 <properties>
   <add name="GivenName"/>
   <add name="Surname"/>
 </properties>

Then to access the properties:

dynamic profile = ProfileBase.Create(Membership.GetUser().UserName);
string s = profile.GivenName;
profile.Surname = "Smith";

To save changes to profile properties:

profile.Save();

The above works fine if you are comfortable using dynamic types and don't mind the lack of compile-time checking and intellisense.

If you use this with ASP.Net MVC you have to do some additional work if you pass the dynamic profile object to your views since the HTML helper methods don't play well with "model" objects that are dynamic. You will have to assign profile properties to statically typed variables before passing them to HTML helper methods.

// model is of type dynamic and was passed in from the controller
@Html.TextBox("Surname", model.Surname) <-- this breaks

@{ string sn = model.Surname; }
@Html.TextBox("Surname", sn); <-- will work

If you create a custom profile class, as Joel described above, ASP.Net will still generate the ProfileCommon class but it will inherit from your custom profile class. If you don't specify a custom profile class ProfileCommon will inherit from System.Web.Profile.ProfileBase.

If you create your own profile class make sure that you don't specify profile properties in the Web.config file that you've already declared in your custom profile class. If you do ASP.Net will give a compiler error when it tries to generate the ProfileCommon class.

Dror
  • 1,406
  • 12
  • 15
colivier
  • 621
  • 7
  • 11
  • i can not save the profile. the error is: **'ProfileCommon' does not contain a definition for 'save'** – Sal-laS Oct 26 '13 at 10:27
  • 2
    @Salman: ProfileCommon is derived from System.Web.Profile.ProfileBase which does have a Save() method. Please check that your capitalization is correct. See: http://msdn.microsoft.com/en-us/library/system.web.profile.profilebase.save(v=vs.110).aspx – colivier Oct 28 '13 at 07:04
13

Profile can be used in Web Application Projects too. The properties can be defined in Web.config at design time or programmatically. In Web.config:

<profile enabled="true" automaticSaveEnabled="true" defaultProvider="AspNetSqlProfileProvider">
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="TestRolesNProfiles"/>
      </providers>
      <properties>
        <add name="FirstName"/>
        <add name="LastName"/>
        <add name ="Street"/>
        <add name="Address2"/>
        <add name="City"/>
        <add name="ZIP"/>
        <add name="HomePhone"/>
        <add name="MobilePhone"/>
        <add name="DOB"/>

      </properties>
    </profile>

or Programmatically, create the profile section by instantiating a ProfileSection and creating individual properties using ProfilePropertySettings and ProfilePropertySettingsColletion, all of which are in System.Web.Configuration Namespace. To use those properties of the profile, use System.Web.Profile.ProfileBase Objects. The profile properties cannot be accessed with profile. syntax as mentioned above, but can be easily done by instantiating a ProfileBase and using SetPropertyValue("PropertyName") and GetPropertyValue{"PropertyName") as follows:

ProfileBase curProfile = ProfileBase.Create("MyName");

or to access the profile of current user:

ProfileBase curProfile = ProfileBase.Create(System.Web.Security.Membership.GetUser().UserName);



        curProfile.SetPropertyValue("FirstName", this.txtName.Text);
        curProfile.SetPropertyValue("LastName", this.txtLname.Text);
        curProfile.SetPropertyValue("Street", this.txtStreet.Text);
        curProfile.SetPropertyValue("Address2", this.txtAdd2.Text);
        curProfile.SetPropertyValue("ZIP", this.txtZip.Text);
        curProfile.SetPropertyValue("MobilePhone", txtMphone.Text);
        curProfile.SetPropertyValue("HomePhone", txtHphone.Text);
        curProfile.SetPropertyValue("DOB", txtDob.Text);
        curProfile.Save();
8

When you create a new Web site project in Visual Studio then the object that is returned from Profile will be (automatically) generated for you. When you create a Web application project or an MVC project, you will have to roll your own.

This probably sounds more difficult than it is. You need to do the following:

  • Create a database using aspnet_regsql.exe This tool is installed along with the .NET framework.
  • Write a class that derives from ProfileGroupBase or install the Web Profile Builder (WPB) that can generate the class for you from the definition in Web.Config. I have been using WPB for a while and up until now it has done what is expected of it. If you have a lot of properties, using WPB can save quite a bit of time.
  • Make sure the connection to the database is properly configured in Web.Config.
  • Now you are set to create an instance of your profile class (in the controller)
  • You will probably need the profile property values in your views. I like to pass the profile object itself along to the view (not individual properties).
Captain Sensible
  • 4,946
  • 4
  • 36
  • 46
3

If you are using a web application project, you cannot access the Profile object at design-time out-of-the-box. Here is a utility that supposedly does it for you: http://weblogs.asp.net/joewrobel/archive/2008/02/03/web-profile-builder-for-web-application-projects.aspx. Personally, that utility caused an error in my project so I ended up rolling my own profile class to inherit from ProfileBase. It was not hard to do at all.

nshaw
  • 2,585
  • 2
  • 19
  • 22
  • 3
    System.Web.Profile - Here is some sample code: http://weblogs.asp.net/jgalloway/archive/2008/01/19/writing-a-custom-asp-net-profile-class.aspx – nshaw Jan 09 '09 at 15:02
2

I was also running through the same issue. But instead of creating a class which inherits from ProfileBase, I used the HttpContext.

Specify properties in web.config file as follows : - ProfilePropertyWeb.config

Now, write the following code : -

Code Behind Profile Properties

Compile and run the code. You will get following output: -

Output

Anil Purswani
  • 1,857
  • 6
  • 35
  • 63
2

MSDN walkthrough for creating a custom class (a.k.a. Joel's method):
http://msdn.microsoft.com/en-us/magazine/cc163624.aspx

Even Mien
  • 44,393
  • 43
  • 115
  • 119
1

The Web Profile Builder worked great for me. The class it generated has a lot more in it than as described by Joel's post. Whether or not its actually needed or useful I dont know.

Anyway for those looking for an easy way to generate the class, but not wanting to have an external build tool dependency you can always

  • use the web profile builder
  • delete all trace of it!
  • keep using the generated Profile class

OR (untested but may just work)

  • create a web site project
  • create your element
  • snap the generated class and copy it over to your web project project

if this second approach does work can someone let me know for future reference

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
1

Just want to add to Joel Spolsky's answer

I implemented his solution, working brilliantly btw - Cudos!

For anyone wanting to get a user profile that's not the logged in user I used:

web.config:

  <connectionStrings>
    <clear />
    <add name="LocalSqlConnection" connectionString="Data Source=***;Database=***;User Id=***;Password=***;Initial Catalog=***;Integrated Security=false" providerName="System.Data.SqlClient" />
  </connectionStrings>

and

<profile defaultProvider="SqlProvider" inherits="NameSpace.AccountProfile" enabled="true">
  <providers>
    <clear/>
    <add name="SqlProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="LocalSqlConnection"/>
  </providers>

And then my custom class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Profile;
using System.Web.Security;

namespace NameSpace
{
    public class AccountProfile : ProfileBase
    {
        static public AccountProfile CurrentUser
        {
            get
            {
                return (AccountProfile)
                 (ProfileBase.Create(Membership.GetUser().UserName));
            }
        }

        static public AccountProfile GetUser(MembershipUser User)
        {
            return (AccountProfile)
                (ProfileBase.Create(User.UserName));
        }

        /// <summary>
        /// Find user with matching barcode, if no user is found function throws exception
        /// </summary>
        /// <param name="Barcode">The barcode to compare against the user barcode</param>
        /// <returns>The AccountProfile class with matching barcode or null if the user is not found</returns>
        static public AccountProfile GetUser(string Barcode)
        {
            MembershipUserCollection muc = Membership.GetAllUsers();

            foreach (MembershipUser user in muc)
            {
                if (AccountProfile.GetUser(user).Barcode == Barcode)
                {
                    return (AccountProfile)
                        (ProfileBase.Create(user.UserName));
                }
            }
            throw new Exception("User does not exist");
        }

        public bool isOnJob
        {
            get { return (bool)(base["isOnJob"]); }
            set { base["isOnJob"] = value; Save(); }
        }

        public string Barcode
        {
            get { return (string)(base["Barcode"]); }
            set { base["Barcode"] = value; Save(); }
        }
    }
}

Works like a charm...

Kickass
  • 1,114
  • 9
  • 16
0

Great post,

Just a note on the web.config if you dont specify the inherit attribute in the profile element you will need to specify each indiviudal profile property inside the profile element on the web.config as below

 <properties>
    <clear/>
    <add name="property-name-1" />
    <add name="property-name-2" />
    ..........

 </properties>
indy
  • 1
  • 1