MVC3 Tutorial HtmlHelper not working - asp.net-mvc

I'm having a problem with the MVC3 MusicStore tutorial. It defines an HtmlHelper with a Truncate method. The helper looks like this:
using System.Web.Mvc;
namespace MusicStore.Helpers
{
public class HtmlHelpers
{
public static string Truncate(this HtmlHelper helper, string input, int length)
{
if (input.Length <= length)
{
return input;
}
else
{
return input.Substring(0, length) + "...";
}
}
}
}
In the view, I import it using #using MusicStore.Helpers, and then try to use it with <td>#Html.Truncate(item.Title, 25) </td>
However the compiler tells me no such method (Truncate) exists, and seems to be looking for Truncate on IEnumerable[MvcMusicStore.Models.Album] (which is my model) rather than on my HtmlHelpers class.
(NB the square brackets above are really angled brackets in my code, couldnt escape them)
Can anyone tell me what I'm doing wrong please?

Extension methods should be declared in a static class:
public static class HtmlHelpers
{
public static string Truncate(
this HtmlHelper helper,
string input,
int length
)
{
if (input.Length <= length)
{
return input;
}
return input.Substring(0, length) + "...";
}
}
and then in your view make sure you have referenced the namespace containing the static class with the extension method:
#using System.Web.Mvc
...
<td>#Html.Truncate(item.Title, 25)</td>
or if you want the helper to be available in all Razor views without the need of adding a using directive you could add the corresponding namespace to the namespaces section of ~/Views/web.config file:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="Namespace.Containig.Static.Class.With.Custom.Helpers" />
</namespaces>
</pages>
</system.web.webPages.razor>

Extension methods must be defined in a static class. So change you code to:
public static class HtmlHelpers
{
public static string Truncate(this HtmlHelper helper, string input, int length)
{
if (input.Length <= length)
{
return input;
}
else
{
return input.Substring(0, length) + "...";
}
}
}
Also, #Darin Dimitrov brings up a good point - you should really retrun an instance of MvcHtmlString.
On a related note, you can import namespaces into your views through the web.config - I'd recommend doing that so you don't have to remember to do it in every page.

You may also want to consider adding the namespace to your web.config. I know I use my helpers on multiple pages. Remembering to add using on every view is a pain.
<system.web>
<pages>
<namespaces>
<add namespace="MusicStore.Helpers"/>
</namespaces>
</pages>
</system.web>

Related

Can a View be reached in MVC without that view being returned or redirected to?

Is there any way that an MVC view could be hit without that view being explicitly returned. In other words, is there some delegated redirect or back-end default page load in ASP.NET-MVC that could throw to a view without that view's name showing up anywhere in the source code? Because if so, I would like to be able to pinpoint all places in my code that could cause a given view to be loaded, even without explicitly returning that view.
Edit for clarity:
I just want to know how/if any View could get loaded without that view being explicitly returned or redirected to from an ActionResult so that I can identify the block of code that is causing the page to get hit.
Here is my RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Default", id = UrlParameter.Optional }
);
}
private static string EnsureTrailingSlash(string value)
{
if (value == null)
{
value = string.Empty;
}
if (!value.EndsWith("/", StringComparison.Ordinal))
{
return value + "/";
}
return value;
}
}
Here are some potentially relevant sections from my Web.config
<configuration>
......
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
</configuration>
It may be from outside the code. Check if where you are hosting your web app if there is any special rules set in place that are automatically showing your errror.cshtml page.
Also, check your web.config if there are special rules in there:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpErrors errorMode="Custom" defaultResponseMode="ExecuteURL">
try looking in here for something like this
</httpErrors>
</system.webServer>
</configuration>
Without seeing anymore of your code or what not it is tough to say exactly. But this might be helpful.

umbraco MVC custom routes using a dot in url

I have a problem with using a dot in url umbraco MVC custom routes.
/logo/images/image.jpg?width=100 gives following errors:
[NullReferenceException: Object reference not set to an instance of an object.]
Umbraco.Web.Mvc.UmbracoVirtualNodeByIdRouteHandler.FindContent(RequestContext requestContext, UmbracoContext umbracoContext) +18
Umbraco.Web.Mvc.UmbracoVirtualNodeRouteHandler.GetHttpHandler(RequestContext requestContext) +48
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +11987058
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +141
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +91
/logo/images/image.jpg/?width=100
Works, but this isn’t a good solution for me.
I have tried adding this in webconfig
<location path="logo">
<!-- This only applies it to the relevant path and keeps the protection in place for elsewhere -->
<system.web>
<httpHandlers>
<add path="/images/*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" />
</httpHandlers>
</system.web>
<!-- Required for IIS 7.0+ -->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
</location>
taken from https://average-joe.info/allow-dots-in-url-iis/
but it won't work:(
My custom route looks like this:
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
//custom route
RouteTable.Routes.MapUmbracoRoute(
"images",
"logo/{action}/{key}",
new
{
controller = "Image",
key = UrlParameter.Optional,
},
new ProductsRouteHandler(4884));
}
}
public class ProductsRouteHandler : UmbracoVirtualNodeByIdRouteHandler
{
public ProductsRouteHandler(int realNodeId) : base(realNodeId)
{
}
protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext, IPublishedContent baseContent)
{
return base.FindContent(requestContext, umbracoContext, baseContent);
}
}
I'am using umbraco vs.7.4.3
The UmbracoModule ignores Urls with a file extension, so an UmbracoContext will never get created for a request containing a file extension.
You can create a context using UmbracoContext.EnsureContext, however if you did this in FindContent method of your handler, you'd encounter this exception. This is caused by a stale variable on line 18 of the UmbracoVirtualNodeRouteHandler holding a reference to a null UmbracoContext, and doesn't pick up the freshly created context.
The following is how worked around it so I could call EnsureContext before the VirtualNodeRouteHandler gets called.
var route = routes.MapRoute("RouteName", "some/url/file.ext", new
{
controller = "MyController",
action = "Index"
}
route.RouteHandler = new UrlWithExtensionHandler();
Notice its not the MapUmbracoRoute, but the standard MVC Map Route, and a standard MVC IRouteHandler which calls EnsureContext before returning an instance of a UmbracoVirtualNodeRouteHandler.
public class UrlWithExtensionHandler : IRouteHandler
{
#region Implementation of IRouteHandler
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// init umbraco context
var httpContext = new HttpContextWrapper(HttpContext.Current);
UmbracoContext.EnsureContext(
httpContext,
ApplicationContext.Current,
new WebSecurity(httpContext, ApplicationContext.Current),
UmbracoConfig.For.UmbracoSettings(),
UrlProviderResolver.Current.Providers,
false);
var handler = new UrlWithExtensionVirtualNodeRouteHandler();
return handler.GetHttpHandler(requestContext);
}
#endregion
}
public class UrlWithExtensionVirtualNodeRouteHandler : UmbracoVirtualNodeRouteHandler
{
protected override IPublishedContent FindContent(RequestContext requestContext,
UmbracoContext umbracoContext)
{
return someIPublishedContent;
}
}
Not an ideal solution, but a valid workaround until the stale variable issue gets merged into core - I've submitted a PR to fix it
A few others have had the same issue too http://issues.umbraco.org/issue/U4-9384

ASP.NET MVC 3 user Profiles not being generated

For my ASP.NET MVC 3 app (using Razor) my web.config has this:
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
<properties>
<add name="FirstName"/>
<add name="LastName"/>
</properties>
</profile>
It is my understanding from reading the docs that ASP.NET will automatically generate properties off the HttpContext so that I can access these like this:
// MyController.cs
Email = u.Email;
FirstName = HttpContext.Profile.FirstName;
LasttName = HttpContext.Profile.LastName;
However, the compiler is bitching that .FirstName and .LastName don't exist.
What is going on here?
Try this approach:
Create a class (name it UserProfile for example) extending ProfileBase
Add your public properties Email etc (don't forget {get;set;})
Change your Web.config like the following (change the inherits to your namespace)
You should be able to access it now via (UserProfile)HttpContext.Current.Profile
Your Web.config:
<profile defaultProvider="AspNetSqlProfileProvider" inherits="Namespace.To.Your.UserProfile">
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" connectionStringName="ConnectionStringName" type="System.Web.Profile.SqlProfileProvider" applicationName="App" />
</providers>
</profile>
Your profile class:
public class UserProfile : ProfileBase
{
public string Email
{
get { return (string)GetPropertyValue("Email"); }
set { SetPropertyValue("Email", value); }
}
}
When using ASP.NET Web Forms, the profile data is accessed through a proxy object whose properties correspond to the profile properties. This feature isn't available for MVC Framework applications.
u can get user profile property value just like this:
public ActionResult Index()
{
ViewBag.Name = HttpContext.Profile["Name"];
ViewBag.City = HttpContext.Profile.GetProfileGroup("Address")["City"];
return View();
}
it will return current logged in user profile property value.

Where to put view-specific javascript files in an ASP.NET MVC application?

What is the best place (which folder, etc) to put view-specific javascript files in an ASP.NET MVC application?
To keep my project organized, I'd really love to be able to put them side-by-side with the view's .aspx files, but I haven't found a good way to reference them when doing that without exposing the ~/Views/Action/ folder structure. Is it really a bad thing to let details of that folder structure leak?
The alternative is to put them in the ~/Scripts or ~/Content folders, but is a minor irritation because now I have to worry about filename clashes. It's an irritation I can get over, though, if it is "the right thing."
Old question, but I wanted to put my answer incase anyone else comes looking for it.
I too wanted my view specific js/css files under the views folder, and here's how I did it:
In the web.config folder in the root of /Views you need to modify two sections to enable the webserver to serve the files:
<system.web>
<httpHandlers>
<add path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<!-- other content here -->
</system.web>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
<!-- other content here -->
</system.webServer>
Then from your view file you can reference the urls like you expect:
#Url.Content("~/Views/<ControllerName>/somefile.css")
This will allow serving of .js and .css files, and will forbid serving of anything else.
One way of achieving this is to supply your own ActionInvoker. Using the code included below, you can add to your controller's constructor:
ActionInvoker = new JavaScriptActionInvoker();
Now, whenever you place a .js file next to your view:
You can access it directly:
http://yourdomain.com/YourController/Index.js
Below is the source:
namespace JavaScriptViews {
public class JavaScriptActionDescriptor : ActionDescriptor
{
private string actionName;
private ControllerDescriptor controllerDescriptor;
public JavaScriptActionDescriptor(string actionName, ControllerDescriptor controllerDescriptor)
{
this.actionName = actionName;
this.controllerDescriptor = controllerDescriptor;
}
public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)
{
return new ViewResult();
}
public override ParameterDescriptor[] GetParameters()
{
return new ParameterDescriptor[0];
}
public override string ActionName
{
get { return actionName; }
}
public override ControllerDescriptor ControllerDescriptor
{
get { return controllerDescriptor; }
}
}
public class JavaScriptActionInvoker : ControllerActionInvoker
{
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
if (action != null)
{
return action;
}
if (actionName.EndsWith(".js"))
{
return new JavaScriptActionDescriptor(actionName, controllerDescriptor);
}
else
return null;
}
}
public class JavaScriptView : IView
{
private string fileName;
public JavaScriptView(string fileName)
{
this.fileName = fileName;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
var file = File.ReadAllText(viewContext.HttpContext.Server.MapPath(fileName));
writer.Write(file);
}
}
public class JavaScriptViewEngine : VirtualPathProviderViewEngine
{
public JavaScriptViewEngine()
: this(null)
{
}
public JavaScriptViewEngine(IViewPageActivator viewPageActivator)
: base()
{
AreaViewLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.js",
"~/Areas/{2}/Views/Shared/{0}.js"
};
AreaMasterLocationFormats = new[]
{
"~/Areas/{2}/Views/{1}/{0}.js",
"~/Areas/{2}/Views/Shared/{0}.js"
};
AreaPartialViewLocationFormats = new []
{
"~/Areas/{2}/Views/{1}/{0}.js",
"~/Areas/{2}/Views/Shared/{0}.js"
};
ViewLocationFormats = new[]
{
"~/Views/{1}/{0}.js",
"~/Views/Shared/{0}.js"
};
MasterLocationFormats = new[]
{
"~/Views/{1}/{0}.js",
"~/Views/Shared/{0}.js"
};
PartialViewLocationFormats = new[]
{
"~/Views/{1}/{0}.js",
"~/Views/Shared/{0}.js"
};
FileExtensions = new[]
{
"js"
};
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (viewName.EndsWith(".js"))
viewName = viewName.ChopEnd(".js");
return base.FindView(controllerContext, viewName, masterName, useCache);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new JavaScriptView(partialPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new JavaScriptView(viewPath);
}
}
}
You can invert davesw's suggestion and block only .cshtml
<httpHandlers>
<add path="*.cshtml" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
I know this is a rather old topic, but I have a few things I would like to add. I tried davesw's answer but it was throwing a 500 error when trying to load the script files, so I had to add this to the web.config:
<validation validateIntegratedModeConfiguration="false" />
to system.webServer. Here is what I have, and I was able to get it to work:
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
<validation validateIntegratedModeConfiguration="false" />
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<httpHandlers>
<add path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
</system.web>
Here is more information on validation: https://www.iis.net/configreference/system.webserver/validation
add this code in web.config file inside system.web tag
<handlers>
<remove name="BlockViewHandler"/>
<add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
I also wanted to place js files related to a view in the same folder as the view.
I wasn't able to get the other solutions in this thread to work, not that they are broken but I am too new to MVC to get them working.
Using information given here and several other stacks I came up with a solution that:
Allows the javascript file to be placed in the same directory as the view it is associated with.
Script URL's don't give away the underlying physical site structure
Script URL's don't have to end with a trailing slash (/)
Doesn't interfere with static resources, eg: /Scripts/someFile.js still
works
Doesn't require runAllManagedModulesForAllRequests to be enabled.
Note: I am also using HTTP Attribute Routing. It's possible that the route's used in my soultion could be modified to work without enabling this.
Given the following example directory/file structure:
Controllers
-- Example
-- ExampleController.vb
Views
-- Example
-- Test.vbhtml
-- Test.js
Using the configuration steps given below, combined with the example structure above, the test view URL would be accessed via: /Example/Test and the javascript file would be referenced via: /Example/Scripts/test.js
Step 1 - Enable Attribute Routing:
Edit your /App_start/RouteConfig.vb file and add routes.MapMvcAttributeRoutes() just above the existing routes.MapRoute:
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc
Imports System.Web.Routing
Public Module RouteConfig
Public Sub RegisterRoutes(ByVal routes As RouteCollection)
routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
' Enable HTTP atribute routing
routes.MapMvcAttributeRoutes()
routes.MapRoute(
name:="Default",
url:="{controller}/{action}/{id}",
defaults:=New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional}
)
End Sub
End Module
Step 2 -Configure your site to treat, and process, /{controller}/Scripts/*.js as an MVC path and not a static resource
Edit your /Web.config file, adding the following to the system.webServer --> handlers section of the file:
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*/scripts/*.js" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
Here it is again with context:
<system.webServer>
<modules>
<remove name="TelemetryCorrelationHttpModule"/>
<add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="managedHandler"/>
<remove name="ApplicationInsightsWebTracking"/>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/>
</modules>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
<remove name="OPTIONSVerbHandler"/>
<remove name="TRACEVerbHandler"/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
<add name="ApiURIs-ISAPI-Integrated-4.0" path="*/scripts/*.js" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
Step 3 - Add the following scripts action result to your Controller file
Be sure to edit the route path to match the {controller} name for the
controller, for this example it's:
<Route("Example/Scripts/{filename}")>
You will need to copy this into each of your Controller files. If you wanted, there is probably a way to do this as a single, one-time, route configuration somehow.
' /Example/Scripts/*.js
<Route("Example/Scripts/{filename}")>
Function Scripts(filename As String) As ActionResult
' ControllerName could be hardcoded but doing it this way allows for copy/pasting this code block into other controllers without having to edit
Dim ControllerName As String = System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values("controller").ToString()
' the real file path
Dim filePath As String = Server.MapPath("~/Views/" & ControllerName & "/" & filename)
' send the file contents back
Return Content(System.IO.File.ReadAllText(filePath), "text/javascript")
End Function
For context, this is my ExampleController.vb file:
Imports System.Web.Mvc
Namespace myAppName
Public Class ExampleController
Inherits Controller
' /Example/Test
Function Test() As ActionResult
Return View()
End Function
' /Example/Scripts/*.js
<Route("Example/Scripts/{filename}")>
Function Scripts(filename As String) As ActionResult
' ControllerName could be hardcoded but doing it this way allows for copy/pasting this code block into other controllers without having to edit
Dim ControllerName As String = System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values("controller").ToString()
' the real file path
Dim filePath As String = Server.MapPath("~/Views/" & ControllerName & "/" & filename)
' send the file contents back
Return Content(System.IO.File.ReadAllText(filePath), "text/javascript")
End Function
End Class
End Namespace
Final Notes
There is nothing special about the test.vbhtml view / test.js javascript files and are not shown here.
I keep my CSS in the view file but you could easily add to this solution so that you can reference your CSS files in a similar way.

Implementing Profile Provider in ASP.NET MVC

For the life of me, I cannot get the SqlProfileProvider to work in an MVC project that I'm working on.
The first interesting thing that I realized is that Visual Studio does not automatically generate the ProfileCommon proxy class for you. That's not a big deal since it's simpy a matter of extending the ProfileBase class. After creating a ProfileCommon class, I wrote the following Action method for creating the user profile.
[AcceptVerbs("POST")]
public ActionResult CreateProfile(string company, string phone, string fax, string city, string state, string zip)
{
MembershipUser user = Membership.GetUser();
ProfileCommon profile = ProfileCommon.Create(user.UserName, user.IsApproved) as ProfileCommon;
profile.Company = company;
profile.Phone = phone;
profile.Fax = fax;
profile.City = city;
profile.State = state;
profile.Zip = zip;
profile.Save();
return RedirectToAction("Index", "Account");
}
The problem that I'm having is that the call to ProfileCommon.Create() cannot cast to type ProfileCommon, so I'm not able to get back my profile object, which obviously causes the next line to fail since profile is null.
Following is a snippet of my web.config:
<profile defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" enabled="true">
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="ApplicationServices" applicationName="/" />
</providers>
<properties>
<add name="FirstName" type="string" />
<add name="LastName" type="string" />
<add name="Company" type="string" />
<add name="Phone" type="string" />
<add name="Fax" type="string" />
<add name="City" type="string" />
<add name="State" type="string" />
<add name="Zip" type="string" />
<add name="Email" type="string" >
</properties>
</profile>
The MembershipProvider is working without a hitch, so I know that the connection string is good.
Just in case it's helpful, here is my ProfileCommon class:
public class ProfileCommon : ProfileBase
{
public virtual string Company
{
get
{
return ((string)(this.GetPropertyValue("Company")));
}
set
{
this.SetPropertyValue("Company", value);
}
}
public virtual string Phone
{
get
{
return ((string)(this.GetPropertyValue("Phone")));
}
set
{
this.SetPropertyValue("Phone", value);
}
}
public virtual string Fax
{
get
{
return ((string)(this.GetPropertyValue("Fax")));
}
set
{
this.SetPropertyValue("Fax", value);
}
}
public virtual string City
{
get
{
return ((string)(this.GetPropertyValue("City")));
}
set
{
this.SetPropertyValue("City", value);
}
}
public virtual string State
{
get
{
return ((string)(this.GetPropertyValue("State")));
}
set
{
this.SetPropertyValue("State", value);
}
}
public virtual string Zip
{
get
{
return ((string)(this.GetPropertyValue("Zip")));
}
set
{
this.SetPropertyValue("Zip", value);
}
}
public virtual ProfileCommon GetProfile(string username)
{
return ((ProfileCommon)(ProfileBase.Create(username)));
}
}
Any thoughts on what I might be doing wrong? Have any of the rest of you successfully integrated a ProfileProvider with your ASP.NET MVC projects?
Thank you in advance...
Here's what you need to do:
1) In Web.config's section, add "inherits" attribute in addition to your other attribute settings:
<profile inherits="MySite.Models.ProfileCommon" defaultProvider="....
2) Remove entire <properties> section from Web.config, since you have already defined them in your custom ProfileCommon class and also instructed to inherit from your custom class in previous step
3) Change the code of your ProfileCommon.GetProfile() method to
public virtual ProfileCommon GetProfile(string username)
{
return Create(username) as ProfileCommon;
}
Hope this helps.
Not sure about the whole question, but one thing I noticed in your code:
ProfileCommon profile = (ProfileCommon)ProfileCommon.Create(user.UserName, user.IsApproved) as ProfileCommon;
You do not need both the (ProfileCommon) and the as ProfileCommon. They both do casts, but the () throws and exception while the as returns a null if the cast can't be made.
Try Web Profile Builder. It's a build script that automagically generates a WebProfile class (equivalent to ProfileCommon) from web.config.
The web.config file in the MVC Beta is wrong. The SqlProfileProvider is in System.Web.Profile, not System.Web.Security. Change this, and it should start working for you.

Resources