Brad Willson has a great article on descripting how to use DataAnnotations. http://bradwilson.typepad.com/blog/2009/04/dataannotations-and-aspnet-mvc.html What I would like to do is extend the available attributes that I can use. Something like [ PastDate(you must enter a date in the past)] or [InvoiceNumber( all invoices start with INV and end with 002)]. I know that I could use the Regular expression attribute to accomplish this. However having more descriptive attributes would be a cleaner solution.
You need to create a class that inherits from System.ComponentModel.DataAnnotations.ValidationAttribute and then use that attribute like this :
public class yourModel {
[CustomValidation(typeof(yourClass), "yourMethod")]
public int yourProperty { get; set; }
}
Haven't tried it but it should work.
I have a few of these in my project - some still use regular expressions, but at least this way they're only in one place:
public class TelephoneAttribute : RegularExpressionAttribute
{
public TelephoneAttribute()
: base(#"^\(?(\d{3}\)?)((-| )?\d{3})(-?\d{4})$") { }
}
And more like what your example:
public class MinimumDateAttribute : RangeAttribute
{
public MinimumDateAttribute(string MinimumDate)
: base(typeof(DateTime), MinimumDate, DateTime.MaxValue.ToShortDateString()) { }
}
Related
I have a tag helper that includes an attribute whose value is an enumeration:
public class MyElementTagHelper : TagHelper
{
public MyAttribute MyAttribute { get; set; }
}
public attribute MyAttribute { apple, banana, cherry }
This allows me to create an HTML <my-element> tag that takes a my-attribute attribute, like this:
<my-element my-attribute="apple">
My question is: Suppose my view model looks like this:
public class MyViewModel
{
public MyAttribute MyAttribute { get; set; }
}
Is there any way to use the MyAttribute value from the view model as the value to the HTML attribute, kind of like this:
<my-element my-attribute="Model.MyAttribute">
More generally, is there a way to reference a variable as the attribute value?
[Answering my own question]
Simply introduce the C# variable with an "#" inside of the quote marks, like this:
<my-element my-attribute="#Model.MyAttribute">
(I posted the question because I was certain that this wouldn't work. I'm pretty sure I had tried this a few months ago and it didn't work. Either I'm loosing my mind - a distinct possibility - or this is new stuff added in the last several months.)
ScottGu showed a feature in vNext to use the Activate Attribute like this:
public class HomeController : Controller
{
[Activate]
public TimeService TimeService { get; set; }
}
I'm on beta-8 and I can't seem to find this attribute, did it get removed?
In addition to using the renamed [FromServices] annotation on your properties, you can also utilize constructor injection:
public class HomeController : Controller
{
private TimeService _timeService;
public HomeController(TimeService timeService)
{
_timeService = timeService;
}
}
I prefer this approach since ASP.NET 5 will fail to construct HomeController if it cannot find TimeService, rather than failing later with timeService being null.
Found it...changed to [FromService]
I simply am trying to declare partial classes for the tool-generated LLBLGenPro (partial) classes, so that I can use DataAnnotation for validation purposes. However, things don't seem to work here.
Following is how my code looks like :
namespace MyApp.DataLayer.EntityClasses
{
[Serializable]
public partial class LoginEntity : CommonEntityBase, ISerializable
{
.....
}
}
And for DataAnnotations ...
namespace MyApp.DataLayer.EntityClasses
{
[MetadataType(typeof(LoginEntityValidation))]
public partial class LoginEntity
{
}
public class LoginEntityValidation
{
[Required(ErrorMessage = "Required !")]
public string Username { get; set; }
}
}
// This gives me compile time errors "MyApp.DataLayer.EntityClasses.LoginEntity' does not contain a constructor that takes 1 arguments" and so on.
Any idea on how to make this working?
Thanks in advance !
You should not use your entity classes in views. You should use ViewModel classes and put validation there. For entity->viewmodel conversion you can use AutoMapper
I have read many posts on Session-scoped data in MVC, but I am still unclear where is the right place to include a custom Session wrapper into the solution.
I want to get the Username of the current user from the IPrincipal, load additional information about that User and store it in the Session. Then I want to access that User data from the Controller and the View.
None of the following approaches seem to fit what I want to do.
Option 1 : Access the Session collection directly
Everyone seems to agree this is a bad idea, but honestly it seems like the simplest thing that works. However, it doesn't make the User available to the view.
public class ControllerBase : Controller {
public ControllerBase() : this(new UserRepository()) {}
public ControllerBase(IUserRepository userRepository) {
_userRepository = userRepository;
}
protected IUserRepository _userRepository = null;
protected const string _userSessionKey = "ControllerBase_UserSessionKey";
protected User {
get {
var user = HttpContext.Current.Session[_userSessionKey] as User;
if (user == null) {
var principal = this.HttpContext.User;
if (principal != null) {
user = _userRepository.LoadByName(principal.Identity.Name);
HttpContext.Current.Session[_userSessionKey] = user;
}
}
return user;
}
}
}
Option 2: Injecting the Session into the class constructor forum post
This option seems pretty good, but I am still not sure how to attach it to the Controller and the View. I could new-it-up in the Controller, but shouldn't it be injected as a dependency?
public class UserContext {
public UserContext()
: this(new HttpSessionStateWrapper(HttpContext.Current.Session),
new UserRepository()) { }
public UserContext(HttpSessionStateBase sessionWrapper, IUserRepository userRepository) {
Session = sessionWrapper;
UserRepository = userRepository;
}
private HttpSessionStateBase Session { get; set; }
private IUserRepository UserRepository{ get; set; }
public User Current {
get {
//see same code as option one
}
}
}
Option 3 : Use Brad Wilson's StatefulStorage class
In his presentation Brad Wilson features his StatefulStorage class. It is a clever and useful set of classes which include interfaces and uses constructor injection. However, it seems to lead me down the same path as Option 2. It uses interfaces, but I couldn't use the Container to inject it because it relies on a static factory. Even if I could inject it, how does it get passed to the View. Does every ViewModel have to have a base class with a setable User property?
Option 4 : Use something similar to the Hanselman IPrincipal ModelBinder
I could add the User as a parameter to the Action method and use a ModelBinder to hydrate it from the Session. This seems like a lot of overhead to add it everywhere it is needed. Plus I would still have to add it to the ViewModel to make it available to the View.
public ActionResult Edit(int id,
[ModelBinder(typeof(IPrincipalModelBinder))] IPrincipal user)
{ ... }
I feel like I am overthinking this, but it also seems like there should be an obvious place to do this sort of thing. What am I missing?
My approach to Session:
Cover Session with interface:
public interface ISessionWrapper
{
int SomeInteger { get; set; }
}
Implement interface using HttpContext.Current.Session:
public class HttpContextSessionWrapper : ISessionWrapper
{
private T GetFromSession<T>(string key)
{
return (T) HttpContext.Current.Session[key];
}
private void SetInSession(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
public int SomeInteger
{
get { return GetFromSession<int>("SomeInteger"); }
set { SetInSession("SomeInteger", value); }
}
}
Inject into Controller:
public class BaseController : Controller
{
public ISessionWrapper SessionWrapper { get; set; }
public BaseController(ISessionWrapper sessionWrapper)
{
SessionWrapper = sessionWrapper;
}
}
Ninject dependency:
Bind<ISessionWrapper>().To<HttpContextSessionWrapper>()
You can pass some commonly used information using ViewData when you want to use it in master page and using view model in specific views.
I would strongly recommend passing anything you need in the view down via the controller. That way, the decision on exactly what data the view should render stays with the controller. In order to make that as easy as possible, creating an abstract ViewModelWithUserBase class that has a settable User property really isn't a bad idea. An option is to create an interface IViewModelWithUser, and re-implement the User property every time (or combine with the base class, but you would have the option to re-implement instead of inheriting the base class if that makes things easier in some corner cases).
As far as populating this property, it can probably be done easily with an action filter. Utilizing the OnActionExecuted method you can test if the model passed to the view implements your base class (or interface), and then fill the property with the correct IPrincipal object if appropriate. This has the advantage that since action filters aren't executed in unit tests, you can use the HttpContext.Current.Session dependent code from your option 1 in your action filter, and still have a testable interface on the controller.
I'd like to create a custom validation attribute for MVC2 for an email address that doesn't inherit from RegularExpressionAttribute but that can be used in client validation. Can anyone point me in the right direction?
I tried something as simple as this:
[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false )]
public class EmailAddressAttribute : RegularExpressionAttribute
{
public EmailAddressAttribute( )
: base( Validation.EmailAddressRegex ) { }
}
but it doesn't seem to work for the client. However, if I use RegularExpression(Validation.EmailAddressRegex)] it seems to work fine.
You need to register an adapter for the new attribute in order to enable client side validation.
Since the RegularExpressionAttribute already has an adapter, which is RegularExpressionAttributeAdapter, all you have to do is reuse it.
Use a static constructor to keep all the necessary code within the same class.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class EmailAddressAttribute : RegularExpressionAttribute
{
private const string pattern = #"^\w+([-+.]*[\w-]+)*#(\w+([-.]?\w+)){1,}\.\w{2,4}$";
static EmailAddressAttribute()
{
// necessary to enable client side validation
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAddressAttribute), typeof(RegularExpressionAttributeAdapter));
}
public EmailAddressAttribute() : base(pattern)
{
}
}
For more information checkout this post explaining the complete process.
http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
The CustomValidationAttribute Class MSDN page has a few examples on it now. The Phil Haacked post is out of date.
Look at the universal Dependent Property Validator in this article
Have you tried using Data Annotations?
This is my Annotations project
using System.ComponentModel.DataAnnotations;
public class IsEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
//do some checking on 'value' here
return true;
}
}
This is in my Models project
namespace Models
{
public class ContactFormViewModel : ValidationAttributes
{
[Required(ErrorMessage = "Please provide a short message")]
public string Message { get; set; }
}
}
This is my controller
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ContactUs(ContactFormViewModel formViewModel)
{
if (ModelState.IsValid)
{
RedirectToAction("ContactSuccess");
}
return View(formViewModel);
}
You'll need to google DataAnnotations as you need to grab the project and compile it. I'd do it but I need to get outta here for a long w/end.
Hope this helps.
EDIT
Found this as a quick google.