`[Bind(Exclude="Password")]` not working? - asp.net-mvc-2

I was trying to edit a user page but i dont want to validate the password field. so i put a [Bind(Exclude="Password")] before my model. but it still causes the modelstate.Isvalid to return false. Can anybody know why is that so?
public ActionResult Edit([Bind(Exclude="Password")]User user, string selectedStatus, string password, string confirmPassword)
{
if(ModelState.Isvalid)<---- returns false
{
///logic
}
}

This attribute excludes a property from being bound, but not from being validated. So basically the model binder will never assign it a value but if you have validation attributes on it that require a value it won't work.
Obviously the solution to this is to use a view model (which by the way is the solution to about 70% of the questions I am answering on SO related to ASP.NET MVC).
So if you don't want a password field at all simply don't include it in this view model and if you want a password but don't want validation simply don't decorate it with a Required attribute.
So instead of:
public ActionResult Edit([Bind(Exclude="Password")]User user, string selectedStatus, string password, string confirmPassword)
use:
public ActionResult Edit(EditUserViewModel user)

Related

EF Lazy load proxy interceptor

I'm looking for a way to intercept Entity Framework's lazy load proxy implementation, or otherwise control what is returned when accessing a Navigation property that may have no value in the database.
An example of what I'm looking for is this Contact class with mailing address, business phone, etc. that may or may not have a contact person.
public partial class Contact
{
private Nullable<System.Guid> _personId;
public Nullable<System.Guid> PersonId
{
get { return _personId; }
set { SetProperty(ref _personId, value); }
}
public virtual Person Person{ get; set; }
// mailing address, other properties...
}
public partial class Person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { SetProperty(ref _firstName, value); }
}
private string _lastName;
public string LastName
{
get { return _lastName;}
set { SetProperty(ref _lastName;value); }
}
}
It is very useful in ASP.net Razor pages, WPF or ad-hoc reporting tools, to be able to use expressions like:
Contact c = repo.GetContact(id);
Console.WriteLine("Contact Person " + c.Person.FirstName);
Which of course fails if there is no PersonId, and hence contact.Person is null.
Tools like Ideablade Devforce have a mechanism to return a "NullEntity" for Person in this case, which allows the WriteLine to succeed. Additionally, the NullEntity for Person can be configured to have a sensible value for FirstName, like "NA".
Is there some way to override the Dynamic Proxy mechanism in EF, or otherwise intercept the reference to Person from Contact to enable this scenario?
I have investigated IDbCommandInterceptor, but that does not seem to intercept virtual navigation to individual entity properties, only navigation to entity collections.
Update _____________________________________
To elaborate on my original question, I can't modify the expression by introducing null conditional operators into the them, as these expressions are incorporated into WPF, ASP.Net Razor binding expressions, and/or report data fields, created by other developers or authors. Also, there may be multiple layers of null properties to deal with, e.g. Contact.Person.Spouse.FirstName, where either Person and/or Spouse might be a "null" property. The Devforce Ideablade implementation deals with this perfectly, but is unfortunately not an option on my current project.
you can use a null-conditional operator from c# like this
c.Person?.FirstName
This means that when Person == null , return null or otherwise return FirstName. You would still need to handle the null value
See : https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-

Validate Unique Value in MVC5 and EF6

In my MVC application I have a requirement where I want user to insert Unique value in a column.
i.e.: Username should be unique in Users table.
I used [Indes(IsUnique = true)] data annotation in my model.
But when I insert duplicate value in the field it throws an exception, but I want to display an Error Message on my View saying Please try with a different Username
Please help me what should I do here?
You can use one of those:
Write your CustomValidator (ny recommendation)
[CustomRemoteValidator(ErrorMessage = #"Username already in use")]
public string Username{ get; set; }`
And override IsValid method
public override bool IsValid(object value)
{
return !(this.DbContext.Set<User>().Any(a =>
a.Username.Equals((string)value));
}
Check it in your business layer.
Check it before save entity in database by overriding SaveChanges() method.

How to authenticate and redirect a user to his 'own' page in Jersey REST service

How to authenticate and redirect a user to his own page i.e to www.mysite.com/"user's email".
I am using the following algo which is not working...
userDB in User class:
Map<String,String> userdata=new HashMap<String,String>();
First my login process form :
#Path("/login")
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void login(
#FormParam("email") String emailc,
#FormParam("password") String pass,
#Context HttpServletResponse servletResponse
) throws IOException,RuntimeException {
User u1=new User();
pass=u1.getPassword();
emailc=u1.getEmailaddrs();
boolean checked=false;
boolean exists;
exists=u1.userdata.containsKey(emailc);
if(exists){
String mypass =u1.userdata.get(emailc);
if(mypass==pass){
checked=true;
}else{
checked=false;
}
}else{
checked=false;
}
if(!checked){
//User Doesn't exists
servletResponse.sendRedirect("http://localhost:8080/MySite/pages/Create_Profile.html");
}else{
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
}
}
createprofile
#POST
#Produces(MediaType.TEXT_HTML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newUser(
#FormParam("email") String email,
#FormParam("password") String password,
#Context HttpServletResponse servletResponse
) throws IOException {
User u = new User(email,password);
User.userdata.put(email,password);
}
Your usage of userdata [Map] looks wrong to me. Is it a part of user class, is it non static or static ?
If it is non static then every time you will do new User() .. that map will be initialized and it will have no data in it. Hence u1.userdata.containsKey(emailc); will be always false.
If you are using a hashmap as a temporary database for dev purposes then, make it static rather keep it in a different class like UserStore or some DB access layer. Exmaple below:
public class UserDAO(){
private static Map<String,User> userdata = new HashMap<String,User>();
public boolean hasUser(String email){
return userdata.contains(email);
}
public User saveUser(String email, String password ...){
//make user object save it in map and return the same
}
// more methods for delete and edit etc.
}
And use this in your REST layer classes like this
exists = userDao.hasUser(email);
Advantages :
Your problem will be solved.
Later on when you move to actual db implementation you will just have to change your UserDao code and rest application code will be just fine. -- Loose coupling :)
Also regarding forward using email
servletResponse.sendRedirect("http://localhost:8080/MySite/{email}"); <<<< How to redirect using #FormParam("email")
add the email parameter there in the url only, if thats what you want:
servletResponse.sendRedirect("http://localhost:8080/MySite/"+emailc);
UPDATE :
See the fundamental thing is that you get request parameters [email , password]. You check it whether it is present in map or not. Now what you are doing wrong here is you create a new user like this User u = new User(); and then get email and password from it emailc = u.getEmail();. This emailc will always be null and your userdata map will always return false for that. You have two choices :
Either set email and password in user object and then get the data from user object.
Use the email and password obtained from request parameters for your logic. Do not alter them
One good practice to follow while programming is that at all times think of your method parameters as final parameters.
UPDATE 2 :
if(mypass==pass){
checked=true;
}else{
checked=false;
}
Change == to equals method. String matching should be done by equals or equalsIgnoreCase method not ==.
You always create a new User without any parameters: User u1=new User();. All these User instances will have the same property values and probably exists is always false.

ASP.NET Mvc - nullable parameters and comma as separator

How should I define route in my global.asax to be able use nullable parameters and coma as separator?
I'm trying to implement routing rule for my search users page like
"{Controller}/{Action},{name},{page},{status}"
Full entry from the Global.asax:
routes.MapRoute(
"Search",
"{controller}/{action},{name},{page},{status}",
new { controller = "User", action = "Find",
name = UrlParameter.Optional,
page = UrlParameter.Optional,
status = UrlParameter.Optional }
);
Routine defined like above works fine when I'm entering all parameters, but when some parameters are equal to null routing fails (for example "user/find,,,")
According to Clicktricity comment bellow - the singature of action method that handes the request:
public ActionResult Find(string userName, int? page, int? status)
{
// [...] some actions to handle the request
}
On the beginning I was testing the route by VS debugger, now I'm using route debugger described on Phil's Haack blog. The tool confirm - that routing with null values is not properly handled (or I'm doing something wrong ;) )
As far as I know .Net routing doesn't let you do multiple nullable parameters like that. Multiple parameters will only work if they are missing working backwards from the end and with the separator also missing so you'd get matches on
user/find,bob,2,live
user/find,bob,2
user/find,bob
user/find
It'd be a lot easier to use querystrings for what you're trying to do.
Edit based on comment:
If this is necessary then you could try doing it this way (though it's not a nice approach)
Change your path to match
{Controller}/{Action},{*parameters}
Make sure to put a constraint on the action and controller so this is limited to as few as possible.
Rename each action that would take your full list to something else, adding a standard prefix to each one would be the cleanest way, and add the [NonAction] attribute. Add a new method with the original name that takes a string, this string is a comma separated string of your variables. In this method split the string and return the original action passing in the values from the split.
So you go from:
public ActionResult Find(string name, int page, string status){
//Do stuff here
return View(result);
}
To
public ActionResult Find(string parameters){
string name;
int? page;
string status;
//split parameters and parse into variables
return FindAction(name, page, status);
}
[NonAction]
public ActionResult FindAction(string parameters){
//Do what you did in your previous Find action
return View(results);
}

How to change ErrorMessage property of the DataAnnotation validation in MVC2.0

My task is to change the ErrorMessage property of the DataAnnotation validation attribute in MVC2.0. For example I should be able to pass an ID instead of the actual error message for the Model property and use that ID to retrieve some content(error message) from a another service e.g database, and display that error message in the View instead of the ID. In order to do this I need to set the DataAnnotation validation attribute’s ErrorMessage property.
[StringLength(2, ErrorMessage = "EmailContentID.")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
It seems like an easy task by just overriding the DataAnnotationsModelValidatorProvider ‘s
protected override IEnumerable GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable attributes)
However it seems to be a complicated enough.
a. MVC DatannotationsModelValidator’s ErrorMessage property is read only. So I cannot set anything here
b. System.ComponentModel.DataAnnotationErrorMessage property(get and set) which is already set in MVC DatannotationsModelValidator so we cannot set again. If you try to set you get “The property cannot set more than once…” error message appears.
public class CustomDataAnnotationProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
IEnumerable<ModelValidator> validators = base.GetValidators(metadata, context, attributes);
foreach (ValidationAttribute validator in validators.OfType<ValidationAttribute>())
{
messageId = validator.ErrorMessage;
validator.ErrorMessage = "Error string from DB And" + messageId ;
}
//......
}
}
Can anyone please help me on this?
Here is the question: What is your motivation to changing the error message property?
Think this through very carefully, as you are heading down a path where you are obfuscating what is actually happening in the application. Certainly the database informatino is useful, but it is not really part of the validation, nor should it be.
When you head in this direction, you are essentially saying that the validation can only be invalid if there is a database problem. I see two issues with this:
It breaks the separation of concerns. You are reporting a persistance error in the model, which is not where it occurred.
The solution is not unit testable, as you must engage the database.
I don't like either of the two above.
Can you solve this? Possibly if you will create your own custom validation attribute. I would have to check and ensure that is correct. Another option is to aim for custom validation:
http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
This article can also help you head in the direction you desire:
http://ryanrivest.com/blog/archive/2010/01/15/reusable-validation-error-message-resource-strings-for-dataannotations.aspx
Do you want to solve this? Not really if you are attempting to keep a proper separation of concerns in your application. I would not polute my validation error message (this is not valid) with a database error (I am not valid, but the database also blew up). Just my two cents.
There are built in ways to get the error message via a resource. Instead of a database lookup to get a resource at runtime, generate resources from your database and use that for your error messages.
You can then use the ErrorMessageResourceName and ErrorMessageResourceType to allow the DataAnnotation to perform a resource lookup instead of hard-coding a specific string.
public sealed class MyModel
{
[Required(
ErrorMessageResourceName="MyDescriptionResource",
ErrorMessageResourceType=typeof(MyCustomResource))]
public string Description { get; set; }
}
Also you may want to have a look at ValidationAttribute.FormatErrorMessage Method on msdn.
This method formats an error message
by using the ErrorMessageString
property. This method appends the name
of the data field that triggered the
error to the formatted error message.
You can customize how the error
message is formatted by creating a
derived class that overrides this
method.
A quick sample (and not meant to be a definitive example)
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false,
Inherited = true)]
public sealed class PostCodeValidationAttribute
: ValidationAttribute
{
public override bool IsValid(object value)
{
if( value == null )
return true;
string postCode = value as string;
if( string.IsNullOrEmpty(postCode) )
return true;
if ( !PostCode.IsValidPostCode(postCode, this.PostCodeStyle) )
return false;
return true;
}
public PostCodeStyle PostCodeStyle { get; set; }
public override string FormatErrorMessage(string name)
{
return string.Format(
"{0} is not a valid postcode for {1}", name, PostCodeStyle);
}
}
* I've omitted the PostCodeStyle enumeration as well as the PostCode class for validating a postcode.