Dynamic validation on MVC 2 - asp.net-mvc-2

This works fine
[MetadataType(typeof(Area_Validation))]
public partial class Area
{
...
}
public class Area_Validation
{
[Required(ErrorMessage = "Please add this field.")]
public int Email { get; set; }
[Required(ErrorMessage = "Please add this field")]
public string Name { get; set; }
}
but how about if Area_Validation is dynamically created? for example Subscription Fields that on back-end can be created by the user and end up like this:
How can I set the Metadata on each field for auto validation?
Currently I'm doing:
public class SubscriberFormViewModel
{
public List<SubscriberFieldModel> Fields { get; private set; }
public Calendar Calendar { get; private set; }
public Company Company { get; private set; }
public SubscriberFormViewModel()
{
// TODO: This is only for testing while validation is not set
}
public SubscriberFormViewModel(Decimal calendarId)
{
if (calendarId > 0)
{
SubscribersRepository db = new SubscribersRepository();
Calendar calendar = db.GetCalendarById(calendarId);
Company company = db.GetCompanyById(calendar.company_id);
this.Fields = db.FindAllSubscriberFieldsByCalendar(calendarId);
this.Calendar = calendar;
this.Company = company;
}
else
this.Fields = new List<SubscriberFieldModel>();
}
}
and I want to set the Metadata in all Fields property
In other words, this Fields are filled up from the Database and can have several types, can be a string, number, dropdown, etc ... kinda like MailChimp Fields Properties:
is there a way to do this programmaticaly or I need to create a jQuery plugin to validate it and stop using use validation from MVC2 ?
Thank you

Actually you can make several validation scenarios, one per type(or more per type if you need). Then Add this validation rules on type creation. When you need to validate, you can call template Validate method, that will check if these rules and if not - will add errors to ModelState, that you will be able to show on front end.
Actually its not that profitable to add any attributes, as attributes pro is that you can decorate your type with them. When you are doing some dynamic things, you`d better have some dynamic way to add validation.

I dont think you can do this using Data Annotations attributes.
There is a project in Codeplex, called Fluent Validation that permit you to add validation rules in a fluent way using .Net code. I never used that project but it seems that maybe can help you in your case with dynamically created objects.
Hope it helps!

Related

Add Columns/Properties to AspNetUserLogins/Logins in IdentityDbContext

Is it possible to add columns to the AspNetUserLogins table, or subclass the IdentityUserLogin class, such that the Identity Framework will use that class properly?
This is an answer but I'm sure it's not going to end up the best one:
It can be done, but it's ugly.
First, you'll want to make a class of all the generics you're about to use, just to make your life easier. Those are:
[Table("AspNetUserRoles")]
public class StandardUserRole : IdentityUserRole<string>
[Table("AspNetRoles")]
public class StandardRole : IdentityRole<string, StandardUserRole>
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
(The above superclasses can be found in Microsoft.AspNet.Identity.EntityFramework).
This is going to make the following generic definitions shorter, and harder to get into a place where they won't compile due to clerical errors.
While you're here may as well add these to the DbContext, which normally does not leave them available to you:
public DbSet<LoginIdentity> LoginIdentities { get; set; }
public DbSet<StandardUserRole> UserRoles { get; set; }
Now, here comes the crazy:
public class Db :
// Replace this with a custom implementation
//IdentityDbContext<Visitor>,
IdentityDbContext<Visitor, StandardRole, string, LoginIdentity,
StandardUserRole, IdentityUserClaim>,
And, Visitor is going to need its own adjustment to match this declaration:
public class Visitor : IdentityUser<string, LoginIdentity, StandardUserRole,
IdentityUserClaim>
That satisfies the Models (which btw, are best to have in their own Project for Migrations performance reasons). But, you've still got all the Identity/OWIN stuff to deal with.
By default you're provided with an ApplicationUserManager that involves a UserStore. It normally inherits from UserManager, but that's going to be too restrictive now - you need to slightly expand it:
public class VisitorManager : UserManager<Visitor, string>
{
public VisitorManager(IUserStore<Visitor, string> store)
: base(store)
{
}
public static VisitorManager Create(
IdentityFactoryOptions<VisitorManager> options,
IOwinContext context)
{
var manager = new VisitorManager(new UserStore<Visitor,
StandardRole, string, LoginIdentity, StandardUserRole,
IdentityUserClaim>(context.Get<Db>()));
I warned you about crazy. SignInManager:
public class SignInManager : SignInManager<Visitor, string>
{
public SignInManager(VisitorManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(
Visitor user)
{
return user.GenerateUserIdentityAsync((VisitorManager)UserManager);
}
public static SignInManager Create(
IdentityFactoryOptions<SignInManager> options, IOwinContext context)
{
return new SignInManager(context.GetUserManager<VisitorManager>(),
context.Authentication);
}
}
That should get you through most of the dirty work. Not easy. But, having done that, you've got a working implementation where you can add extra fields to the Logins table! You can now extend the OWIN Auth stuff to provide events, and listen for the creation of new Logins. You can then respond to those by adding that extra info.
In our case, the goal was to have multiple Logins from multiple OpenId/OAuth Providers (Google, Facebook, etc) across multiple email addresses, on a single User/Visitor account. The framework does support that, but, it doesn't make a record of what Email is associated with what Login row, which is important when merging more Logins with a given account.
[Table("AspNetUserLogins")]
public class LoginIdentity : IdentityUserLogin
{
/// <summary>
/// The email address associated with this identity at this provider
/// </summary>
[MaxLength(300)]
public string Email { get; set; }
}
There's more you'll need to do to get the whole thing working, but it should be relatively obvious from the above starting point - with one exception, which I'll point out here.
By migrating from UserManager<TVisitor> to UserManager<TVisitor, string>, you quietly lose the ID-generation functionality built-in to the former. You'll need to emulate it yourself. As another gotcha, along the way you'll most likely implement Visitor as IUser<string>. Doing so will prevent you from setting the Id property, because it's read-only (no setter). You can avoid that with a second interface:
public interface IVisitor
{
string Id { get; set; }
string Uid { get; set; }
string UserName { get; set; }
string Email { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
ICollection<StandardUserRole> Roles { get; }
ICollection<LoginIdentity> Logins { get; }
}
With that in place you can set Id safely (even in an abstracted class):
public override Task<IdentityResult> CreateAsync(Visitor user)
{
var guid = Guid.NewGuid();
string id = guid.ToString();
((IVisitor)user).Id = id;
return base.CreateAsync(user);
}
Remember to do same for CreateAsync(Visitor user, string password). Otherwise created users explode with DbEntityValidationException complaining Id is a required field.

Should I provide different views on the same REST entity?

I've seen this that suggest I can build different views based on user:
different json views for the same entity
However in asp web api, one uses a Model class, I can't just add new properties willy-nilly.
So, for example I may have uri:
http://host/api/products/id
Returning the model:
public class Product{
public string Code { get; set; }
public string Description { get; set; }
}
But for another purpose I want to add more information, suppose this is expensive because it joins other data to build the model, or formats the data in a very specific way:
http://host/api/productsspecial/id
Returning the model:
public class ProductSpecial{
public string Code { get; set; }
public string Description { get; set; }
public decimal Price { get; set; } //assume expensive to look up
}
So obviously I have a way to do this, two different controllers, returning different views on the data. My question is, is this OK or is there a better way?
Anyway I could do this for example: http://host/api/products/id?includeprice=true and use that to return the alternative model? And is that a good idea?
I would suggest
GET /host/api/products/{id}?fields=code,description,price
You should avoid complicating your resource URL in the manner you describe. Every possible configuration of values would need a new name: "productsReallySpecial", etc.
The problem with ?includePrice=true is you then have a parameter for every variable you might want to make optional. Your documentation can list the default return values and the available return values.

How to get EF POCOs from System.Data.Entities.DynamicProxies

My question is the same as this one
However, I don't really see a solution there. Lets say I have a simple model with two POCOs, Country and State.
public class Country
{
public string Code { get; set; }
public string Name { get; set; }
}
public class State
{
public string Code { get; set; }
public string Name { get; set; }
public virtual Country Country { get; set; }
}
When I use the repository to .GetStateByCode(myCode), it retrieves a dynamic proxy object. I want to send that over the wire using a WCF service to my client. The dynamic proxy is not a know type so it fails.
Here are my alternatives. I can set ProxyCreationEnabled to false on the context and then my .GetStateByCode(myCode) gives me a POCO which is great. However, the navigation property in the POCO to Country is then NULL (not great).
Should I new up a state POCO and manually populate and return that from the dynamic proxy that is returned from the repository? Should I try to use AutoMapper to map the dynamic proxy objects to POCOs? Is there something I'm totally missing here?
I think the answer from Ladislav Mrnka is clear. The Warnings Still apply. Even with this idea below. Becareful what gets picked Up. He just didnt include , if you want to proceed how to easily get data from Object a to object B. That is question at hand really.
Sample solution
See nuget package ValueInjecter (not the only tool that can do this... but very easy to use)
it allows easy copying of One object to another especially with the same properties and types.
( remember the lazy loading / navigation implications).
So vanilla option is :
var PocoObject = new Poco();
PocoObject.InjectFrom(DynamicProxy); // copy contents of DynamicProxy to PocoObject
but check the default behaviour and consider a custom rule
var PocoObject = new Poco();
PocoObject.InjectFrom<CopyRule>(DynamicProxy);
public class CopyRule : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
bool usePropertry; // return if the property it be included in inject process
usePropertry = c.SourceProp.Name == "Id"; // just an example
//or
// usePropertry = c.SourceProp.Type... == "???"
return usePropertry;
}
}

inject behaviour on manual created objects

I'm working with autofac. So far i resolve all my dependencies with constructor injection.
There is a case where i get stuck:
Considering the given customer class:
public class Customer : ICustomer
{
public string Name { get; set; }
private int ExternId { get; set; }
public IExternalIdProvider externalIdProvider { get; set; }
public Customer()
{
this.externalIdProvider = new ConcreteIdProvider(this);
}
public BevorSave()
{
this.ExternId = externalIdProvider.GetNextId();
}
}
In Order to create a new customer object based on a request or gui action. I use the new Operator. However - There is an IdProvider within the CustomerClass i want to inject. (as property).
If the Customer would be resolved by the ioC Container i would use a configuration like:
builder.RegisterType<ConcreteIdProvider>().As<IExternalIdProvider>();
builder.RegisterType<Customer>().As<ICustomer>()
.OnActivated(ae =>
{
IExternalIdProvider idProvider =
ae.Context.Resolve<IExternalIdProvider>(TypedParameter.From(ae.Instance));
ae.Instance.externalIdProvider = idProvider;
});
My Question is: How can I inject the behaviour of the ExternalIdProvider in the Customer? (using autofac)
This article shows a sample, how this would be done with a service locator:
http://blogs.msdn.com/b/simonince/archive/2008/06/30/dependency-injection-is-dead.aspx
Thanks for your help.
You should reconsider having behavior on your entities. Having behavior in your entities forces you to do dependency injection on them, and this leads to an awkward situation, which you already noticed. Take a look at this related SO question and Mark Seemann's great answer.
So instead of having these operations on the Customer class, move them to an repository class. Other patterns to look at are unit of work, commands and queries.

Entity Framework error when submitting empty fields

VS 2010 Beta 2, .NET 4.
In my ASP.NET MVC 2 application, when I submit a form to an action method that accepts an object created by the entity framework, I get the following error:
Exception Details: System.Data.ConstraintException: This property cannot be set to a
null value.
Source Error:
Line 4500: OnTextChanging(value);
Line 4501: ReportPropertyChanging("Text");
Line 4502: _Text = StructuralObject.SetValidValue(value, false);
Line 4503: ReportPropertyChanged("Text");
Line 4504: OnTextChanged();
The property is called "Text" and is of type "text NOT NULL" in MS SQL 2008.
My action will check if the value is nullorempty, if it is, a model error will be added, but I get the error as soon as I submit the form.
Are you binding directly to the entity? Sure looks like it. So you have two choices:
Write a custom model binder which translates null -> empty string.
Bind to an edit model which allows nulls instead, and then change this to empty string when you copy the values to the entity in the action.
I'd choose #2, personally. I think you should always use view/edit models, and this is a great example of why.
I was having the same problem. I looked around and found a work around here. It describes the problem as being caused by the EF validation taking place before the Required field validation. It also shows how we can work around this problem by using a [DisplayFormat] Tag. Hope this will help you.
Here's the link to the question and the workaround:
Server-side validation of a REQUIRED String Property in MVC2 Entity Framework 4 does not work
Is this an issue with the MVC2 and Entity Framework 4 or is this by design? It appears that validation of EF properties works fine for datetime non-nullable (required) fields and data type validation of numeric versus string fields is working without having to use ViewModels.
I recreated the issue using with a simple FOOBAR table using a single, non-nullable varchar(50) column called barName in slq 2008. I generated the EF model from that database and quickly added a controller and a CREATE view for the FOOBAR entity. If I try to POST to the CREATE action without entering in a value for the property barName, VS steps into an exception within the designer.cs file of the model (just like the one above). When, I try to step past the exception, the validation message shows up on the form and the field is highlighted in pink.
It seems like something is not firing in the correct sequence. Because the exception occurs before VS steps into the HTTPPOST CREATE method.
I found the code from the ASP.Net MvcMusicStore sample helpful. http://mvcmusicstore.codeplex.com/releases/view/44445#DownloadId=119336
It appears that binding to the ViewModel fixes the issue.
namespace MvcMusicStore.ViewModels
{
public class StoreManagerViewModel
{
public Album Album { get; set; }
public List<Artist> Artists { get; set; }
public List<Genre> Genres { get; set; }
}
}
........
namespace MvcMusicStore.Models
{
[MetadataType(typeof(AlbumMetaData))]
public partial class Album
{
// Validation rules for the Album class
[Bind(Exclude = "AlbumId")]
public class AlbumMetaData
{
[ScaffoldColumn(false)]
public object AlbumId { get; set; }
[DisplayName("Genre")]
public object GenreId { get; set; }
[DisplayName("Artist")]
public object ArtistId { get; set; }
[Required(ErrorMessage = "An Album Title is required")]
[StringLength(160)]
public object Title { get; set; }
[DisplayName("Album Art URL")]
[StringLength(1024)]
public object AlbumArtUrl { get; set; }
[Required(ErrorMessage = "Price is required")]
[Range(0.01, 100.00, ErrorMessage="Price must be between 0.01 and 100.00")]
public object Price { get; set; }
}
}
}
Ashish Shakya's answer helped me. I added this attribute to the property and now it works.
[DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText="")]
So it looks like this:
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
[DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText="")]
public global::System.String MyProperty
{
get
{
return _MyProperty;
}
set
{
OnMyPropertyChanging(value);
ReportPropertyChanging("MyProperty");
_MyProperty = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("MyProperty");
OnMyPropertyChanged();
}
}
Import the namespace:
using System.ComponentModel.DataAnnotations;
And add the attribute property [Required]
[Required]
public global::System.String MyProperty
{
get
{
return _MyProperty;
}
set
{
OnMyPropertyChanging(value);
ReportPropertyChanging("MyProperty");
_MyProperty = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("MyProperty");
OnMyPropertyChanged();
}
}
Thus ModelState.IsValid equals false, showing error message in the validation and will not fail on the server with Null.
I had the same problem and fixed it by making false to true like this:
Line 4502:
_Text = StructuralObject.SetValidValue(value, false);
I just had the same problem myself, and came here to find the solution. However, the answer can be enhanced.
Svavar's and HackITMngr were on the right track, however combining both gives the best outcome. You don't want to go decorating the generated classes, as you risk losing your custom changes upon modifications to the EF model.
[MetadataType(typeof(MyTableMetaData))]
public partial class MyTable
{
// Validation rules for the Album class
public class MyTableMetaData
{
[DisplayFormat(ConvertEmptyStringToNull = false, NullDisplayText="")]
public string MyTextProperty { get; set; }
}
}
To settle any arguments between the two. I'd say Svavar's was the direct answer, HackITMngr was the enhancement.
Works great for me!
I set StoreGeneratedPattern property as Computed for each field and it solved the problem for me.