Remote validation asp.net mvc - asp.net-mvc

I am new in mvc so forgive me if the question is stupid but I want to do the best I can. So, my situation is that:
I have created a model and decorated like
Partial Public Class App_Modules
<Required>
<Remote("CheckForDuplicate", "Validation")>
<Display(Name:="Code")>
Public Property code As String
<Required>
<Display(Name:="Description")>
Public Property name As String
End Class
As you can see, the code column must be remote validated.
In my ValidationController I have the code
Public Function CheckForDuplicate(code As String) As JsonResult
Dim data = db.App_Modules.Where(Function(p) p.code.Equals(code, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()
If data IsNot Nothing Then
Return Json("This code already exists",JsonRequestBehavior.AllowGet)
Else
Return Json(True, JsonRequestBehavior.AllowGet)
End If
End Function
End Class
Everything works fine! Now I want to do the same for another model with the same field "code". Is there any way to pass the model name to the function so instead of the line
Dim data = db.**App_Modules**.Where(Function(p) p.code.Equals(code, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()
I could have something like
Dim data = db.**TABLENAME**.Where(Function(p) p.code.Equals(code, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault()
So the function would be generic and can be called from other models too?

I am not sure of the syntax in VB but you could modify your CheckForDuplicate function to accept a generic parameter that represents your Model class and pass that to the Set function of your DBContext. you will also need to define an interface for your Model that contains the Code property. Sample code in c# is as follows.
public JsonResult CheckForDuplicate<T>(string code) where T : IModelWithCode
{
var data = db.Set<T>().Where(t => t.Code.Equals(code));
....
}
public interface IModelWithCode
{
string Code { get; set; }
}
Hopefully that will get you started in the right direction.

I don't think this can be done or at least easily.
I would stick with the simple here: create a Select Case and check in the tables depending on the parameter passed (model name).
Dim exist = false;
Select Case myModel
Case "Model1"
exist = db.Model1Table.Where(Function(p) p.code.Equals(code, StringComparison.CurrentCultureIgnoreCase)).Any()
Case "Model2"
exist = db.Model2Table.Where(Function(p) p.code.Equals(code, StringComparison.CurrentCultureIgnoreCase)).Any()
End Select
If each table has different layout or you have to do some other checks... you are free to do the special thing in each case.
UPDATE:
Here you can see an article showing how to pass other fields to the validator action.
You should create a Hidden Field to hold the Model name.
http://www.codeproject.com/Articles/674288/Remote-Validation-in-MVC-Simple-Way-to-Pass-the-F
Other resource: MVC Remote Attribute Additional Fields

Related

MVC - Inherited view model members [duplicate]

My Post call does not return the correct Model type. It always use the baseObject instead of the correct derived object that I passed in from the Get
RestaurantViewModel.cs
public class RestaurantViewModel{
public Food BaseFoodObject{get;set;}
}
Food.cs
public class Food{
public string Price{get;set;)
}
Bread.cs -- Inherit from Food
public class Bread:Food{
public int Unit{get;set;}
}
Milk.cs -- Inherit from Food
public class Milk:Food{
public string Brand{get;set}
}
Editor For Template for Bread. Display the unit and allow user to edit
Index.html
#Model RestaurantViewModel
#using(Html.BeginForm("SaveFood", "Food"))
{
#Html.EditorFor(m=>m.BaseFoodObject)
<input type="submit" value="Process"/>
}
Bread.cshtml
#Model Bread
<div>
#Html.TextboxFor(bread=>bread.Unit)
</div>
FoodController.cs
public ActionResult Index(){
Bread bread = new Bread(){
Price = "$10",
Unit = 1
}
RestaurantViewModel viewModel = new RestaurantViewModel(){
BaseFoodObject = bread
}
return View(viewModel);
}
public ActionResult Post(RestaurantViewModel viewModelPost)
{
// When I inspect the viewModelPost, there is no attribute for unit
}
Final Result:
1. The display looks correct. EditorFor is smart enough to pick the correct editor template and display the value correctly
2. The Save does not work. The Unit attribute of Bread Object does not get passed in with the RestaurantViewModel. The reason for that is the RestaurantViewModel used the Food object instead of Bread
I hope there is away to modify the EditorFor and tell it to use the Model in the View or the Object Type that I passed in when I display it.
Thanks
Update 1: I solved this problem by using the custom binder and using a factory to decide which object I really want. This helps construct the correct Model which I want
MVC is stateless. A couple of references.
There's a couple of statements in your question that conflict with this, and how MVC binding works eg:
My Post call does not return the correct Model type.
Possibly just terminology, but your Post call does not 'return a model type' - it goes into the model that's defined in the post action, in this case RestaurantViewModel.
instead of the correct derived object that I passed in from the Get
because it is stateless, it knows nothing about the model you passed in from the get... absolutely nothing.
The final html rendered via the getaction+view.cshtml+model is not linked to the postaction. You could just as easily take the rendered html, save it, reboot your PC, reload the rendered html and it will work exactly the same way.
a way to modify the EditorFor and tell it to use the Model in the View or the Object Type that I passed in when I display it
When you use EditorFor it sets an ID and name attribute based on the model it was bound to, so it already does this, but perhaps you are not binding to the model you want to bind to to get the correct id.
So, to the question, if, in 'normal' C# code you were to instantiate a new instance of RestaurantViewModel, what would you expect the type of BaseFoodObject to be?
This is what the ModelBinder is doing - it's creating a new RestaurantViewModel.
As your post action method's signature does not include anything to do with Bread - all the bread properties are ignored.
Some options:
Check for the food properties after binding and read them manually (probably the quickest+easiest but not very "mvc-ish")
public ActionResult Post(RestaurantViewModel viewModelPost)
{
if (!string.IsNullOrEmpty(Request.Form["Unit"]))
// it's a bread form
to make this easier, you could provide a hidden field with the type
if (Request.Form["Type"] == typeof(Bread).Name)
{
var bread = new Bread { Unit = Request.Form["Unit"] }
Add bread to the action so it's bound
public ActionResult Post(RestaurantViewModel viewModelPost, Bread bread)
but then, obviously, it won't work for milk.
So could extend this using an ActionNameSelector to select the correct action
public ActionResult PostBread(RestaurantViewModel viewModelPost, Bread bread)
public ActionResult PostMilk(RestaurantViewModel viewModelPost, Milk milk)
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class FoodSelectorAttribute : ActionNameSelectorAttribute
{
public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
{
... check if provided parameters contains bread/milk
(related link but not a solution to this specific case)
Another option might be to change the Restaurant type to a generic, but would require a few more changes (and ideally use of interfaces), and more details (provided here as an idea, rather than a solution)
The basics would be:
public class RestaurantViewModel<T>
where T: Food
{
}
public ActionResult Post(RestaurantViewModel<Bread> viewModelPost)
public ActionResult Post(RestaurantViewModel<Milk> viewModelPost)
but I've not confirmed if the default ModelBinder would work in this case.
The problem comes with the post. Once you post, all you have is a set of posted data and a parameter of type, RestaurantViewModel. The modelbinder sets all the appropriate fields on Food because that's all it knows. Everything else is discarded. There's nothing that can be done about this. If you need to post fields related to Bread then the type of your property must be Bread. That's the only way it will work.

How to send model object in Html.RenderAction (MVC3)

I'm using MVC3 razor, and I'm trying to pass an object to a partial view, and it's not working.
This works fine without sending the object model to the partial view:
Html.RenderAction("Index", "ViewName");
Trying this doesn't sent the model object, i'm getting nulls instead (the object has data, and the view expects it):'
Html.RenderAction("Index", "ViewName", objectModel);
Is this even possible using RenderAction?
Thanks!
Edit: I found the error, there was an error with the controller's action that didn't pick up the sent object. Thanks for all your help!
You can actually pass an object to a controller method using Action. This can be done on any avaialble view, for instance I have one in a shared library that gets built to project bin folders that reference my shared project (properties - Copy if newer on the view file, in Visual Studio). It is done like so:
Controller:
public class GroovyController : Controller
{
public ActionResult MyTestView(MyModel m)
{
var viewPath = #"~\bin\CommonViews\MyTestView";
return View(viewPath, m);
}
}
MVC page (using Razor syntax):
#Html.Action("MyTestView", "Groovy", new { m = Model })
or using RenderAction method:
#{ Html.RenderAction("MyTestAction", "MyTestController", new { area = "area", m = Model }); }
Note: in the #Html.Action(), the Model object must be of type MyModel and that 3rd parameter must be set to the controller variable name, of which mine is MyModel m. The m is what you must assign to, so I do m = Model.
say you want to pass foo as model, make it first
public class Foo {
public string Name { get; set; }
public int Age { get; set; }
}
now make an ActionResult
public ActionResult FooBar(Foo _foo){
return PartialView(_foo);
}
call it
#Html.RenderAction("FooBar", "Controller", new { Name = "John", Age=20 });
Usually if I have a model already available it makes more sense to use Html.Partial than trying to render an action.
#Html.Partial("Foo", Model.FooModel)
Where Foo.cshtml is a view file (perhaps in your Shared folder) strongly typed with with #model FooProject.Models.FooModel or whatever your model is called. This can be as complex a model as you need it to be. Model is your page's main model into which you must set FooModel - or just omit this parameter if the Foo view uses the same model as the parent page.
RenderAction is generally better when you have just simple parameters, because you're just simulating a request to a regular action which has routing/query string parameters - and then dumping that response into your page. It works well if you need to put something in a Layout that isn't available in your page's model such as an element in a side bar.

MVC2: Using DataAnnotations to validate DataType

I am using Entity Framework + SQL Server DB and am using partial classes with DataAnnotations to validate data. For things like Required and Range, this works fine, but I am unable to get the DataType validators to work.
Here is an example of the (custom) annotation:
[DataTypeWholeNumberAttribute(ErrorMessage = "Zip must be a whole number")]
public object Zip{ get; set; }
...and the Controller Code...
[HttpPost]
public ActionResult Edit(NamedInsuredViewModel viewModel)
{
try
{ //breakpoint here (opening squiggly bracket) shows .Zip is already null
if (ModelState.IsValid)
...save, etc...
}
}
And I know what's happening: The DataType of Zip in the database is int, so the default validation is catching that and applying the generic error message "the value [x] is not valid for [FieldName]" before my validator can get to it (to prove this, I also added the same validator to a string field, and it works just fine). What I don't know is, how can I get around that (and no, I can't change the DB to use strings for everything)?
Some suggestions have been offered in this post (http://forums.asp.net/p/1608322/4162819.aspx#4162819), but so far nothing has helped.
Thanks in advance.
PS - is there really no way to validate a primitive DataType without creating a custom Attribute?
I think the error is to pass something called "viewModel" to a Edit Action.
ViewModel is intended for pass data to a view to render it.
When you submit a form the data have to be mapped to a entity not to a viewModel.
[HttpPost]
public ActionResult Edit(YourEntity entity)
{
try
{ //breakpoint here (opening squiggly bracket) shows .Zip is already null
if (ModelState.IsValid)
...save, etc...
}
}
Apply your custom validator to the class. Then pass in your class instance as the parameter for your validator instead of as a string. Then you can perform the validation on the appropriate property regardless of type.

How to transfer value from a DropDownList to a property of a serialized model object

I'm testing some concepts in ASP.NET MVC multisteps (Style Wizards) with a small application which allow me to records organizations in a database.
To make things easier, I've a class OrganizationFormModelView that contains an object of class Organization and a property called ParentOrgList of SelectList type. The only purpose of the selectList property is to be used by a DropDownList.
I've also serialize OrganizationFormModelView to get the multisteps Wizard effect.
In my first view (or first step), I use a dropdownlist helper to assign a value to one of the the Organization's property called ParentOrganization, which draws data from the ParentOrgList.
...
<% = Html.DropDownList("Organization.ParentOrganization", Model.ParentOrgList)%>
...
The first time the page loads, I'm able to make a choice. And, my choice is reflected in my object Model all along the wizard' steps(see Visual studio in debugging mode).
But, when any time I'm redirected back to the first view (first step), I get the following error message:
"The ViewData item with the key 'Organization.ParentOrganization' is of type 'System.String' but needs to be of type 'IEnumerable'."
Thanks for helping
After considering carefully my code, I understand now what's going on. OrganizationFormModelView is the class that is being serialized, and here's its definition.
[Serializable]
public class OrganizationFormViewModel
{
public Organization Organization { get; set; }
[NonSerialized]
public SelectList ParentOrgList = null;
public OrganizationFormViewModel(Organization organization, SelectList cList)
{
Organization = organization ?? new Organization();
ParentOrgList = pList;
}
}
From that, I've concluded that, After each serialization process, ParentOrgList is set to null, so I need to find a way of re-assigning value to it. So, below is what I did:
public ActionResult CreateOrganization(string nextButton)
{
//Omitted for brievety
if (formViewModel.ParentOrgList == null)
formViewModel.ParentOrgList = repository.CommuneList;
//Omitted for brievety
}
I also, modified the View so that, even if the value of the ParentOrgList is continuously re-assigned, but the DropDownList keeps the user's choice. So, I choose an Helper overload with default value.
...
<% = Html.DropDownList("Organization.ParentOrganization", Model.ParentOrgList,
Model.Organization.ParentOrganization)%>
...
Now, everything is working perfectly.
However, If someone knows how to proceed differently with the Serialization business, it'd be helpful to share.
Thanks

How can I exclude some public properties from being serialized into a JsonResult?

I have a custom viewmodel which serialized using a JsonResult. The ViewModel has some properties which have to be public, but at the same time these properties should not be visible in the resulting Json output.
I've already tried using the [NonSerialized] attribute, but that did not seem to have any effect.
Is there any simple way to do this? Or would I have to code my own result type (in which case I probably won't bother)?
You can put a [ScriptIgnore] attribute on the members that shouldn't be serialized. See ScriptIgnoreAttribute Class in MSDN for an example.
Just create an interface to return instead of a class.
public interface IMyViewModel {
string MyPublicProperty { get; set; }
}
Then create a class that inherits the interface
public class MyViewModel : IMyViewModel {
public string MyPublicProperty { get; set; }
public string MyNotSoPublicProperty { get; set; }
}
And return the interface, not the class, in the Controller Action
public JsonResult MyJson(){
IMyViewModel model = new MyViewModel();
return Json(model);
}
And the resulting JSON will be
{
'MyPublicProperty': ''
}
One of the challenges in client-side scripting is, that if you're changing your classes, you have no idea whether you're destroying the client-side implementation or not. If you use interface types in your JSON, you understand that if you change the interface, you're doing something that potentially may be killing the client side implementation. And it also saves you from double-checking the client side in vain if you're changing something that is NOT in the inteface (thus not being serialized).
Also, many times, your ViewModels might have large collections or complex types in them that you don't necessarily want to output to the client. These might take a long time to serialize or expose information that simply does not belong into the client code. Using interfaces will make it more transparent to know what is being in the output.
Also, using attributes such as [ScriptIgnore] on a property only applies to a specific scenario (JavaScript Serialization) forcing you to face the exact same problem if you're later serializing to XML for example. This would unnecessarily litter your viewmodels with tons of attributes. How many of them you really want in there? Using intefaces applies anywhere and no viewmodel needs to be littered with extra attributes.
Have a look at JSON.NET from James Newton-King. It'll do what you're looking for.
Extend the JavaScriptConverter class to not include properties with the NonSerializedAttribute. Then you can create a custom ActionResult that uses your JavaScriptConverter to serialize the object.
This creates a solid and testable class without having to (re)generate wrapper classes or using anonymous objects.
You can create a wrapper class that exposes only those properties that you want in the JsonResult. In the example below, Cow has 2 properties - "Leg" and "Moo". Suppose you want to only expose "Leg" as a property. Then
Dim cw as CowWrapper = New CowWrapper(c)
will return a wrapper class that only exposes "Leg". This is also useful for things like DataGridView if you only want to display some subset of the properties.
Public Class Cow
Public ReadOnly Property Leg() as String
get
return "leg"
end get
end Property
Public ReadOnly Property Moo() as String
get
return "moo"
end get
end Property
end class
Public Class CowWrapper
Private m_cow as Cow = Nothing
Public Sub New(ByVal cow as Cow)
m_cow = cow
end Sub
m_cow = cow
Public ReadOnly Property Leg() as String
get
return m_cow.Leg()
end get
end Property
end Class
Not exactly the answer you're looking for, but you can cheat Json() using the following code and anonymous classes:
MyModel model = ...;
return Json(new MyModel {model.Prop1, model.Prop2});
I needed the answer to this for ASP.NET Core 6.x and couldn't find it.
I finally found the answer and it is :
[System.Text.Json.Serialization.JsonIgnore]
Here's an example in a class
class Sample{
// Item will not be serialized
[System.Text.Json.Serialization.JsonIgnore]
String Item{get;set;}
// Count will be serialized
int Count{get;set;}
}

Resources