How to use Custom SiteMapProvider in ASP NET MVC? - asp.net

I am currently trying to implement a Custom SiteMap Provider. I have read several tutorials about it, and followed their lead.
I have created a subclass of XmlSiteMapProvider named MySiteMapProvider which is located in MyProject.Security.
I have added the following code to the system.web section of my Web.config:
<siteMap defaultProvider="MySiteMapProvider" enabled="true">
<providers>
<add name="MySiteMapProvider"
description="Custom SiteMap provider."
type="MyProject.Security.MySiteMapProvider "
siteMapFile="Web.sitemap"
securityTrimmingEnabled="true" />
</providers>
</siteMap>
But I am sure that my Provider is not used correctly. I couldn't even start with the implementation. To verify that I have included the following (pseudo) implementation:
public override bool IsAccessibleToUser(System.Web.HttpContext context, System.Web.SiteMapNode node)
{
Debug.Print("Hello World");
throw new Exception();
return base.IsAccessibleToUser(context, node);
}
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
{
Debug.Print("Hello World");
throw new Exception();
base.Initialize(name, attributes);
}
public override SiteMapNode BuildSiteMap()
{
Debug.Print("Hello World");
throw new Exception();
return base.BuildSiteMap();
}
But I can use the site and navigate as much as I want to, no Exception comes up and the Debug console shows no Hello World at all.
Did I forget something important?

Instead of implementing my own Provider, I went along with the MvcSiteMapProvider.
The customization of the behaviour that I needed to implement was realized in one day with dynamic sitemaps and a custom SiteMapNodeVisibilityProvider.
I also considered implementing the whole SiteMapProvider, maybe on SQL basis, and I am glad that I did not have to do it.

Try calling System.Web.SiteMap.RootNode from code.

Related

HttpRequest.Form collection gets cleared after Managed HttpModule

I’m suffering this issue I can’t find an explanation for.
Have a website that handles ASP and ASPX requests.
All requests run through a custom managed module, named MyModule, let’s say for “logging purposes”.
This is the WebConfig:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="MyModule" type="MySample.MyModule" preCondition="managedHandler" />
</modules>
</system.webServer>
</configuration>
So, if a form is posted to /action.asp via AJAX, an html form, or whatever, on /action.asp I can see and print the data on the Request.Form collection.
This is /action.asp
<%#LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<%
dim count
count = Request.Form.Count
Response.write("Result: " & count)
Response.End()
%>
But, if in my custom managed module I just “PEEK” at the form collection before it’s handled by the ASP page, the collection disappears, it’s no longer available to /action.asp
This is the MyModule:
namespace MySample
{
public class MyModule : IHttpModule
{
public MyModule()
{
}
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
}
void context_BeginRequest(object sender, EventArgs e)
{
/*
* ALL REQUESTS PASS THROUGH THIS POINT BEFORE CONTINUING.
* COUNTING THE ITEMS ON THE FORM WILL CAUSE THE FORM-COLLECTION
* TO BE CLEARED WHEN IT'S HANDLED BY ASP.
*/
int count = HttpContext.Current.Request.Form.Count;
}
public void Dispose()
{
}
}
}
It’s extremely odd. If I “comment” the count line, the form collection is handled unmodified to the ASP page. I just have to peek at it to run it.
I want to find an explanation for this backed up with some documentation, not just a hunch.
I can’t set to false the runAllManagedModulesForAllRequests, this is not an option.
I debugged the request through different .NET method calls, and many things happen when you query the Form object on .NET HttpRequest object,
// Form collection
/// Gets a collection of Form variables.
public NameValueCollection Form {
get {
EnsureForm();
if (_flags[needToValidateForm]) {
_flags.Clear(needToValidateForm);
ValidateHttpValueCollection(_form, RequestValidationSource.Form);
}
return _form;
}
}
// Populates the Form property but does not hook up validation.
internal HttpValueCollection EnsureForm() {
if (_form == null) {
_form = new HttpValueCollection();
/// THE FOLLWING METHOD AS A LOT OF SUB-CALLS AS WELL
if (_wr != null)
FillInFormCollection();
_form.MakeReadOnly();
}
return _form;
}
Is what I am experiencing an expected behavior ? What’s the documentation or the reasoning to back up this behavior ?

How to re-implement legacy aspx with ServiceStack and maintain the address?

Is it possible to keep the following address and re-implement it with ServiceStack?
http://example.com/Routing/LeadPost.aspx?LeadType=AAA&MYId=3000
I don't have access to the original code as it was a 3rd party who created it, but I do know what the post to it, a gigantic xml package. This I have mimicked perfectly with a clean request DTO and service, but I'm not clear on the addressing part, or if it's even plausible. Below is my implementation so far.
public class Service : ServiceStack.Service
{
public IMessageQueueClient MessageQueueClient { get; set; }
public object Post(LeadInformation request)
{
if (request == null) throw new ArgumentNullException("request");
var sw = Stopwatch.StartNew();
MessageQueueClient.Publish(request);
return new LeadInformationResponse
{
TimeTakenMs = sw.ElapsedMilliseconds,
};
}
}
Thank you,
Stephen
You can use this route definition to handle the Legacy ASP.NET WebForms Request:
[Route("/Routing/LeadPost.aspx")]
public class LegacyLeadPost
{
public string LeadType { get; set; }
public int MyId { get; set; }
}
Which will let you handle the desired route with LeadType and MyId properties populated:
/Routing/LeadPost.aspx?LeadType=AAA&MYId=3000
An alternative approach is to use a WebForms Page and call into ServiceStack, the ServiceStack Integration docs explores the different ways from accessing ServiceStack from external ASP.NET MVC or WebForms Web Frameworks.
When you installed ServiceStack (at least up to version 3, which is what I last used), it adds something to your application's Web.Config file that looks like:
<system.web>
<httpHandlers>
<add path="somePath/*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
</system.web>
If you modify the path attribute to match your legacy URL, it may work. Then again, without knowing what other HttpHandlers are set up for your application, it is impossible to tell for sure. Also keep in mind that you may introduce conflicts, because there is an HttpHandler that passes all .aspx urls to the .NET webforms Page base class and all the other code-behind files in your application.

Very simple single user login in ASP.NET MVC2?

I'm building my site, and I want to restrict a part of my site (The admin parts) from normal public display.
I am using LINQ for database access.
I have a Service class to handle calls to the database through LINQ
I have the whole site running, except for the Login part.
So far I have only been able to find examples using MembershipProvider and/or RoleProviders etc. And to be honest, it does seem like too much work for what I want. All this has to do is to let you in if you type the correct password in the input fields.
Can i really not avoid the Providers?
Since you only have a single user you don't need to create a database dependency. You can make a very simple authorization service based off of a hard coded credentials. For example,
public class AuthorizationService{
private AuthorizationService(){}
public static readonly AuthorizationService Instance = new AuthorizationService();
private const string HardCodedAdminUsername = "someone";
private const string HardCodedAdminPassword = "secret";
private readonly string AuthorizationKey = "ADMIN_AUTHORIZATION";
public bool Login(string username, string password, HttpSessionStateBase session){
if(username.ToLowerInvariant().Trim()==HardCodedAdminUsername && password.ToLowerInvariant().Trim()==HardCodedAdminPassword){
session[AuthorizationKey] = true;
return true;
}
return false;
}
public void Logout(HttpSessionStateBase session){
session[AuthorizationKey] = false;
}
public bool IsAdmin(HttpSessionStateBase session){
return session[AuthorizationKey] == true;
}
}
Then you can build a custom IAuthorizationFilter like:
public class SimpleAuthFilterAttribute: FilterAttribute, IAuthorizationFilter{
public void OnAuthorization(AuthorizationContext filterContext){
if(!AuthorizationService.Instance.IsAdmin(filterContext.HttpContext.Session)){
throw new UnauthorizedAccessException();
}
}
}
Then all you have to do is decorate the protected controller actions with the SimpleAuthFilter and you're application's login suddenly works. Yay! (Note, I wrote all this code in the StackOverflow answer window, so you may need to clean up typos, etc. before it actually works)
Also, you could refactor this to omit the username if you find that unnecessary. You will need to create a controller action for Login and Logout that make the corresponding calls to the AuthorizationService, if you want your protected controller actions to ever be accessible.
Its worth building a light-weight Membership Provider with minimal implementation; GetUser, ValidateUser etc methods. YOu dont need to implement the whole thing. It just helps with authorising pages and checking User.Identity etc when needed. You also dont need the RoleProvider or ProfileProvider to do this.
Its also scalable for the future.
UPDATE
You just need to implement the core methods to valudate and get the user and insert your own validation/data access code.
Something like this....
web.config settings:
<membership defaultProvider="ApplicationMembershipProvider">
<providers>
<clear/>
<add name="ApplicationMembershipProvider" type="YourNamespace.ApplicationMembershipProvider"/>
</providers>
</membership>
Login Code:
if (Membership.ValidateUser(username, password))
{
FormsAuthentication.SetAuthCookie(username, false);
}
You can set the status (logged in or not) in a session variable. Set the variable to true if the user entered the correct password, then on every page you want to restrict access, check if the variable is true.
#KristianB a while ago I gave an answer to this SO question. I believe it may be useful since it's very straightforward to implement and at the same time it's better than hardcoding a username and a password in your code.
Good luck!

profile in asp.net MVC [duplicate]

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.
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;
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.
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();
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).
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.
MSDN walkthrough for creating a custom class (a.k.a. Joel's method):
http://msdn.microsoft.com/en-us/magazine/cc163624.aspx
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 : -
Now, write the following code : -
Compile and run the code. You will get following output: -
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
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...
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>

How to assign Profile values?

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.
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;
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.
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();
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).
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.
MSDN walkthrough for creating a custom class (a.k.a. Joel's method):
http://msdn.microsoft.com/en-us/magazine/cc163624.aspx
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 : -
Now, write the following code : -
Compile and run the code. You will get following output: -
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
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...
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>

Resources