RequiredIf Conditional Validation Attribute - asp.net-mvc-3

I was looking for some advice on the best way to go about implementing a validation attribute that does the following.
Model
public class MyInputModel
{
[Required]
public int Id {get;set;}
public string MyProperty1 {get;set;}
public string MyProperty2 {get;set;}
public bool MyProperty3 {get;set;}
}
I want to have atleast prop1 prop2 prop3 with a value and if prop3 is the only value filled it it should not equal false.
How would i go about writing a validation attribute(s?) for this?
Thanks for any help!

I had the same problem yesterday, but I did it in a very clean way which works for both client side and server side validation.
Condition: Based on the value of other property in the model, you want to make another property required. Here is the code:
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
PropertyName is the property on which you want to make your condition
DesiredValue is the particular value of the PropertyName (property) for which your other property has to be validated for required
Say you have the following:
public enum UserType
{
Admin,
Regular
}
public class User
{
public UserType UserType {get;set;}
[RequiredIf("UserType",UserType.Admin,
ErrorMessageResourceName="PasswordRequired",
ErrorMessageResourceType = typeof(ResourceString))]
public string Password { get; set; }
}
At last but not the least, register adapter for your attribute so that it can do client side validation (I put it in global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),
typeof(RequiredAttributeAdapter));
EDITED
Some people have reported issues that the client side fires no matter what or it does not work. So I modified the above code to do conditional client side validation with Javascript as well. For this case you don't need to register adapter
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
private readonly RequiredAttribute _innerAttribute;
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
_innerAttribute = new RequiredAttribute();
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);
if (dependentValue.ToString() == DesiredValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredif",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;
yield return rule;
}
}
And finally the javascript ( bundle it and renderit...put it in its own script file)
$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options) {
options.rules['requiredif'] = options.params;
options.messages['requiredif'] = options.message;
});
$.validator.addMethod('requiredif', function (value, element, parameters) {
var desiredvalue = parameters.desiredvalue;
desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
var actualvalue = {}
if (controlType == "checkbox" || controlType == "radio") {
var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
actualvalue = control.val();
} else {
actualvalue = $("#" + parameters.dependentproperty).val();
}
if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
var isValid = $.validator.methods.required.call(this, value, element, parameters);
return isValid;
}
return true;
});
You need obviously the unobstrusive validate jQuery to be included as requirement

I know the topic was asked some time ago, but recently I had faced similar issue and found yet another, but in my opinion a more complete solution. I decided to implement mechanism which provides conditional attributes to calculate validation results based on other properties values and relations between them, which are defined in logical expressions.
Using it you are able to achieve the result you asked about in the following manner:
[RequiredIf("MyProperty2 == null && MyProperty3 == false")]
public string MyProperty1 { get; set; }
[RequiredIf("MyProperty1 == null && MyProperty3 == false")]
public string MyProperty2 { get; set; }
[AssertThat("MyProperty1 != null || MyProperty2 != null || MyProperty3 == true")]
public bool MyProperty3 { get; set; }
More information about ExpressiveAnnotations library can be found here. It should simplify many declarative validation cases without the necessity of writing additional case-specific attributes or using imperative way of validation inside controllers.

I got it to work on ASP.NET MVC 5
I saw many people interested in and suffering from this code and i know it's really confusing and disrupting for the first time.
Notes
worked on MVC 5 on both server and client side :D
I didn't install "ExpressiveAnnotations" library at all.
I'm taking about the Original code from "#Dan Hunex", Find him above
Tips To Fix This Error
"The type System.Web.Mvc.RequiredAttributeAdapter must have a public constructor which accepts three parameters of types System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext, and ExpressiveAnnotations.Attributes.RequiredIfAttribute Parameter name: adapterType"
Tip #1: make sure that you're inheriting from 'ValidationAttribute' not from 'RequiredAttribute'
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}
Tip #2: OR remove this entire line from 'Global.asax', It is not needed at all in the newer version of the code (after edit by #Dan_Hunex), and yes this line was a must in the old version ...
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredAttributeAdapter));
Tips To Get The Javascript Code Part Work
1- put the code in a new js file (ex:requiredIfValidator.js)
2- warp the code inside a $(document).ready(function(){........});
3- include our js file after including the JQuery validation libraries, So it look like this now :
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>
4- Edit the C# code
from
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
to
rule.ValidationParameters["dependentproperty"] = PropertyName;
and from
if (dependentValue.ToString() == DesiredValue.ToString())
to
if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
My Entire Code Up and Running
Global.asax
Nothing to add here, keep it clean
requiredIfValidator.js
create this file in ~/content or in ~/scripts folder
$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options)
{
options.rules['requiredif'] = options.params;
options.messages['requiredif'] = options.message;
});
$(document).ready(function ()
{
$.validator.addMethod('requiredif', function (value, element, parameters) {
var desiredvalue = parameters.desiredvalue;
desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
var actualvalue = {}
if (controlType == "checkbox" || controlType == "radio") {
var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
actualvalue = control.val();
} else {
actualvalue = $("#" + parameters.dependentproperty).val();
}
if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
var isValid = $.validator.methods.required.call(this, value, element, parameters);
return isValid;
}
return true;
});
});
_Layout.cshtml or the View
#Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>
RequiredIfAttribute.cs Class
create it some where in your project, For example in ~/models/customValidation/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Your_Project_Name.Models.CustomValidation
{
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
private readonly RequiredAttribute _innerAttribute;
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
_innerAttribute = new RequiredAttribute();
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);
if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredif",
};
rule.ValidationParameters["dependentproperty"] = PropertyName;
rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;
yield return rule;
}
}
}
The Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using Your_Project_Name.Models.CustomValidation;
namespace Your_Project_Name.Models.ViewModels
{
public class CreateOpenActivity
{
public Nullable<int> ORG_BY_CD { get; set; }
[RequiredIf("ORG_BY_CD", "5", ErrorMessage = "Coordinator ID is required")] // This means: IF 'ORG_BY_CD' is equal 5 (for the example) > make 'COR_CI_ID_NUM' required and apply its all validation / data annotations
[RegularExpression("[0-9]+", ErrorMessage = "Enter Numbers Only")]
[MaxLength(9, ErrorMessage = "Enter a valid ID Number")]
[MinLength(9, ErrorMessage = "Enter a valid ID Number")]
public string COR_CI_ID_NUM { get; set; }
}
}
The View
Nothing to note here actually ...
#model Your_Project_Name.Models.ViewModels.CreateOpenActivity
#{
ViewBag.Title = "Testing";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>CreateOpenActivity</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ORG_BY_CD, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ORG_BY_CD, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ORG_BY_CD, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.COR_CI_ID_NUM, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.COR_CI_ID_NUM, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.COR_CI_ID_NUM, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
I may upload a project sample for this later ...
Hope this was helpful
Thank You

If you try to use "ModelState.Remove" or "ModelState["Prop"].Errors.Clear()" the "ModelState.IsValid" stil returns false.
Why not just removing the default "Required" Annotation from Model and make your custom validation before the "ModelState.IsValid" on Controller 'Post' action? Like this:
if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))
ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");

Expanding on the notes from Adel Mourad and Dan Hunex, I amended the code to provide an example that only accepts values that do not match the given value.
I also found that I didn't need the JavaScript.
I added the following class to my Models folder:
public class RequiredIfNotAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
private Object InvalidValue { get; set; }
private readonly RequiredAttribute _innerAttribute;
public RequiredIfNotAttribute(String propertyName, Object invalidValue)
{
PropertyName = propertyName;
InvalidValue = invalidValue;
_innerAttribute = new RequiredAttribute();
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);
if (dependentValue.ToString() != InvalidValue.ToString())
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
}
}
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredifnot",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
rule.ValidationParameters["invalidvalue"] = InvalidValue is bool ? InvalidValue.ToString().ToLower() : InvalidValue;
yield return rule;
}
I didn't need to make any changes to my view, but did make a change to the properties of my model:
[RequiredIfNot("Id", 0, ErrorMessage = "Please select a Source")]
public string TemplateGTSource { get; set; }
public string TemplateGTMedium
{
get
{
return "Email";
}
}
[RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Campaign")]
public string TemplateGTCampaign { get; set; }
[RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Term")]
public string TemplateGTTerm { get; set; }
Hope this helps!

The main difference from other solutions here is that this one reuses logic in RequiredAttribute on the server side, and uses required's validation method depends property on the client side:
public class RequiredIf : RequiredAttribute, IClientValidatable
{
public string OtherProperty { get; private set; }
public object OtherPropertyValue { get; private set; }
public RequiredIf(string otherProperty, object otherPropertyValue)
{
OtherProperty = otherProperty;
OtherPropertyValue = otherPropertyValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(OtherProperty);
if (otherPropertyInfo == null)
{
return new ValidationResult($"Unknown property {OtherProperty}");
}
object otherValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (Equals(OtherPropertyValue, otherValue)) // if other property has the configured value
return base.IsValid(value, validationContext);
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationType = "requiredif"; // data-val-requiredif
rule.ValidationParameters.Add("other", OtherProperty); // data-val-requiredif-other
rule.ValidationParameters.Add("otherval", OtherPropertyValue); // data-val-requiredif-otherval
yield return rule;
}
}
$.validator.unobtrusive.adapters.add("requiredif", ["other", "otherval"], function (options) {
var value = {
depends: function () {
var element = $(options.form).find(":input[name='" + options.params.other + "']")[0];
return element && $(element).val() == options.params.otherval;
}
}
options.rules["required"] = value;
options.messages["required"] = options.message;
});

I think using IValidatableObject is a good choice.
public class MyInputModel : IValidateObject
{
[Required]
public int Id {get;set;}
public string MyProperty1 {get;set;}
public string MyProperty2 {get;set;}
public bool MyProperty3 {get;set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (MyProperty1==null&&MyProperty2==null&&MyPropterty3!=false) //whatever condition
{
yield return new ValidationResult(
"Custom complex error");
}
}
}

Related

How to send complex objects in GET to WEB API 2

Let's say that you have the following code
public class MyClass {
public double Latitude {get; set;}
public double Longitude {get; set;}
}
public class Criteria
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public MyClass MyProp {get; set;}
}
[HttpGet]
public Criteria Get([FromUri] Criteria c)
{
return c;
}
I'd like to know if someone is aware of a library that could transform any object into query string that is understood by a WEB API 2 Controller.
Here is an example of what I'd like
SerializeToQueryString(new Criteria{StartDate=DateTime.Today, EndDate = DateTime.Today.AddDays(1), MyProp = new MyProp{Latitude=1, Longitude=3}});
=> "startDate=2015-10-13&endDate=2015-10-14&myProp.latitude=1&myProp.longitude=3"
A full example with httpClient might look like :
new HttpClient("http://localhost").GetAsync("/tmp?"+SerializeToQueryString(new Criteria{StartDate=DateTime.Today, EndDate = DateTime.Today.AddDays(1), MyProp = new MyProp{Latitude=1, Longitude=3}})).Result;
At the moment, I use a version (taken from a question I do not find again, maybe How do I serialize an object into query-string format? ...).
The problem is that it is not working for anything else than simple properties.
For example, calling ToString on a Date will not give something that is parseable by WEB API 2 controller...
private string SerializeToQueryString<T>(T aObject)
{
var query = HttpUtility.ParseQueryString(string.Empty);
var fields = typeof(T).GetProperties();
foreach (var field in fields)
{
string key = field.Name;
var value = field.GetValue(aObject);
if (value != null)
query[key] = value.ToString();
}
return query.ToString();
}
"Transform any object to a query string" seems to imply there's a standard format for this, and there just isn't. So you would need to pick one or roll your own. JSON seems like the obvious choice due to the availability of great libraries.
Since it seems no one has dealt with the problem before, here is the solution I use in my project :
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Web;
namespace App
{
public class QueryStringSerializer
{
public static string SerializeToQueryString(object aObject)
{
return SerializeToQueryString(aObject, "").ToString();
}
private static NameValueCollection SerializeToQueryString(object aObject, string prefix)
{
//!\ doing this to get back a HttpValueCollection which is an internal class
//we want a HttpValueCollection because toString on this class is what we want in the public method
//cf http://stackoverflow.com/a/17096289/1545567
var query = HttpUtility.ParseQueryString(String.Empty);
var fields = aObject.GetType().GetProperties();
foreach (var field in fields)
{
string key = string.IsNullOrEmpty(prefix) ? field.Name : prefix + "." + field.Name;
var value = field.GetValue(aObject);
if (value != null)
{
var propertyType = GetUnderlyingPropertyType(field.PropertyType);
if (IsSupportedType(propertyType))
{
query.Add(key, ToString(value));
}
else if (value is IEnumerable)
{
var enumerableValue = (IEnumerable) value;
foreach (var enumerableValueElement in enumerableValue)
{
if (IsSupportedType(GetUnderlyingPropertyType(enumerableValueElement.GetType())))
{
query.Add(key, ToString(enumerableValueElement));
}
else
{
//it seems that WEB API 2 Controllers are unable to deserialize collections of complex objects...
throw new Exception("can not use IEnumerable<T> where T is a class because it is not understood server side");
}
}
}
else
{
var subquery = SerializeToQueryString(value, key);
query.Add(subquery);
}
}
}
return query;
}
private static Type GetUnderlyingPropertyType(Type propType)
{
var nullablePropertyType = Nullable.GetUnderlyingType(propType);
return nullablePropertyType ?? propType;
}
private static bool IsSupportedType(Type propertyType)
{
return SUPPORTED_TYPES.Contains(propertyType) || propertyType.IsEnum;
}
private static readonly Type[] SUPPORTED_TYPES = new[]
{
typeof(DateTime),
typeof(string),
typeof(int),
typeof(long),
typeof(float),
typeof(double)
};
private static string ToString(object value)
{
if (value is DateTime)
{
var dateValue = (DateTime) value;
if (dateValue.Hour == 0 && dateValue.Minute == 0 && dateValue.Second == 0)
{
return dateValue.ToString("yyyy-MM-dd");
}
else
{
return dateValue.ToString("yyyy-MM-dd HH:mm:ss");
}
}
else if (value is float)
{
return ((float) value).ToString(CultureInfo.InvariantCulture);
}
else if (value is double)
{
return ((double)value).ToString(CultureInfo.InvariantCulture);
}
else /*int, long, string, ENUM*/
{
return value.ToString();
}
}
}
}
Here is the unit test to demonstrate :
using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Framework.WebApi.Core.Tests
{
[TestClass]
public class QueryStringSerializerTest
{
public class EasyObject
{
public string MyString { get; set; }
public int? MyInt { get; set; }
public long? MyLong { get; set; }
public float? MyFloat { get; set; }
public double? MyDouble { get; set; }
}
[TestMethod]
public void TestEasyObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject(){MyString = "string", MyInt = 1, MyLong = 1L, MyFloat = 1.5F, MyDouble = 1.4});
Assert.IsTrue(queryString.Contains("MyString=string"));
Assert.IsTrue(queryString.Contains("MyInt=1"));
Assert.IsTrue(queryString.Contains("MyLong=1"));
Assert.IsTrue(queryString.Contains("MyFloat=1.5"));
Assert.IsTrue(queryString.Contains("MyDouble=1.4"));
}
[TestMethod]
public void TestEasyObjectNullable()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject() { });
Assert.IsTrue(queryString == "");
}
[TestMethod]
public void TestUrlEncoding()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EasyObject() { MyString = "&=/;+" });
Assert.IsTrue(queryString.Contains("MyString=%26%3d%2f%3b%2b"));
}
public class DateObject
{
public DateTime MyDate { get; set; }
}
[TestMethod]
public void TestDate()
{
var d = DateTime.ParseExact("2010-10-13", "yyyy-MM-dd", CultureInfo.InvariantCulture);
var queryString = QueryStringSerializer.SerializeToQueryString(new DateObject() { MyDate = d });
Assert.IsTrue(queryString.Contains("MyDate=2010-10-13"));
}
[TestMethod]
public void TestDateTime()
{
var d = DateTime.ParseExact("2010-10-13 20:00", "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
var queryString = QueryStringSerializer.SerializeToQueryString(new DateObject() { MyDate = d });
Assert.IsTrue(queryString.Contains("MyDate=2010-10-13+20%3a00%3a00"));
}
public class InnerComplexObject
{
public double Lat { get; set; }
public double Lon { get; set; }
}
public class ComplexObject
{
public InnerComplexObject Inner { get; set; }
}
[TestMethod]
public void TestComplexObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new ComplexObject() { Inner = new InnerComplexObject() {Lat = 50, Lon = 2} });
Assert.IsTrue(queryString.Contains("Inner.Lat=50"));
Assert.IsTrue(queryString.Contains("Inner.Lon=2"));
}
public class EnumerableObject
{
public IEnumerable<int> InnerInts { get; set; }
}
[TestMethod]
public void TestEnumerableObject()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EnumerableObject() {
InnerInts = new[] { 1,2 }
});
Assert.IsTrue(queryString.Contains("InnerInts=1"));
Assert.IsTrue(queryString.Contains("InnerInts=2"));
}
public class ComplexEnumerableObject
{
public IEnumerable<InnerComplexObject> Inners { get; set; }
}
[TestMethod]
public void TestComplexEnumerableObject()
{
try
{
QueryStringSerializer.SerializeToQueryString(new ComplexEnumerableObject()
{
Inners = new[]
{
new InnerComplexObject() {Lat = 50, Lon = 2},
new InnerComplexObject() {Lat = 51, Lon = 3},
}
});
Assert.Fail("we should refuse something that will not be understand by the server");
}
catch (Exception e)
{
Assert.AreEqual("can not use IEnumerable<T> where T is a class because it is not understood server side", e.Message);
}
}
public enum TheEnum : int
{
One = 1,
Two = 2
}
public class EnumObject
{
public TheEnum? MyEnum { get; set; }
}
[TestMethod]
public void TestEnum()
{
var queryString = QueryStringSerializer.SerializeToQueryString(new EnumObject() { MyEnum = TheEnum.Two});
Assert.IsTrue(queryString.Contains("MyEnum=Two"));
}
}
}
I'd like to thank all the participants even if this is not something that you should usually do in a Q&A format :)

Translating expression tree from a type to another type with complex mappings

inspired by this answer I'm trying to map a property on a model class to an expression based on the actual entity.
These are the two classes involved:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public DateTime? BirthDate { get; set; }
public int CustomerTypeId { get; set; }
}
public class CustomerModel
{
...
public bool HasEvenId { get; set; }
}
An example of a possible expression I'd like to convert is:
Expression<Func<CustomerModel, bool>> from = model => model.HasEvenId;
Expression<Func<Customer, bool>> to = entity => ((entity.Id % 2) == 0);
The problem is that I have to expose an OData endpoint via ASP.NET WebAPI but I need to make some operations on the entities before I can them, hence the need of a model class and the need to translate the expression based on the model that I could receive as an OData query in an expression based on the entity, that I would use to query EF4.
This is where I got so far:
private static readonly Dictionary<Expression, Expression> Mappings = GetMappings();
private static Dictionary<Expression, Expression> GetMappings()
{
var mappings = new Dictionary<Expression, Expression>();
var mapping = GetMappingFor((CustomerModel model) => model.HasEvenId, (Customer customer) => (customer.Id%2) == 0);
mappings.Add(mapping.Item1, mapping.Item2);
return mappings;
}
private static Tuple<Expression, Expression> GetMappingFor<TFrom, TTo, TValue>(Expression<Func<TFrom, TValue>> fromExpression, Expression<Func<TTo, TValue>> toExpression)
{
MemberExpression fromMemberExpression = (MemberExpression) fromExpression.Body;
return Tuple.Create<Expression, Expression>(fromMemberExpression, toExpression);
}
public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression, Dictionary<Expression, Expression> mappings = null)
{
if (expression == null)
return null;
string parameterName = expression.Parameters[0].Name;
parameterName = string.IsNullOrWhiteSpace(parameterName) ? "p" : parameterName;
var param = Expression.Parameter(typeof(TTo), parameterName);
var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
ParameterChangeVisitor parameterChange = new ParameterChangeVisitor(parameterName);
if (mappings != null)
foreach (var mapp in mappings)
subst.Add(mapp.Key, parameterChange.Visit(mapp.Value));
var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
}
public IQueryable<CustomerModel> Get()
{
var filterExtractor = new ODataFilterExtractor<CustomerModel>();
Expression<Func<CustomerModel, bool>> expression = filterExtractor.Extract(Request);
Expression<Func<Customer, bool>> translatedExpression = Translate<CustomerModel, Customer>(expression, Mappings);
IQueryable<Customer> query = _context.Customers;
if (translatedExpression != null)
query = query.Where(translatedExpression);
var finalQuery = from item in query.AsEnumerable()
select new CustomerModel()
{
FirstName = item.FirstName,
LastName = item.LastName,
Id = item.Id,
BirthDate = item.BirthDate,
CustomerTypeId = item.CustomerTypeId,
HasEvenId = (item.Id % 2 ) == 0
};
return finalQuery.AsQueryable();
}
where:
ODataFilterExtractor is a class that extract the $filter expression from the RequestMessage we receive;
ParameterChangeVisitor just changes all the ParameterExpression to a new one having the provided string as parameter name;
In addition, I changed the VisitMember method of the answer linked above in this way:
protected override Expression VisitMember(MemberExpression node)
{
// if we see x.Name on the old type, substitute for new type
if (node.Member.DeclaringType == _from)
{
MemberInfo toMember = _to.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SingleOrDefault();
if (toMember != null)
{
return Expression.MakeMemberAccess(Visit(node.Expression), toMember);
}
else
{
if (_substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Any(me => me.Member.Equals(node.Member)))
{
MemberExpression key = _substitutions.Select(kvp => kvp.Key).OfType<MemberExpression>().Single(me => me.Member.Equals(node.Member));
Expression value = _substitutions[key];
// What to return here?
return Expression.Invoke(value);
}
}
}
return base.VisitMember(node);
}
Thanks for you help.
I took the liberty of modifying your code just a hair but this does the trick,
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public DateTime? BirthDate { get; set; }
public int CustomerTypeId { get; set; }
}
public class CustomerModel
{
public string FullName { get; set; }
public bool HasEvenId { get; set; }
}
sealed class AToBConverter<TA, TB> : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> _parameters = new Dictionary<ParameterExpression, ParameterExpression>();
private readonly Dictionary<MemberInfo, LambdaExpression> _mappings;
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(TA))
{
ParameterExpression parameter;
if (!this._parameters.TryGetValue(node, out parameter))
{
this._parameters.Add(node, parameter = Expression.Parameter(typeof(TB), node.Name));
}
return parameter;
}
return node;
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression == null || node.Expression.Type != typeof(TA))
{
return base.VisitMember(node);
}
Expression expression = this.Visit(node.Expression);
if (expression.Type != typeof(TB))
{
throw new Exception("Whoops");
}
LambdaExpression lambdaExpression;
if (this._mappings.TryGetValue(node.Member, out lambdaExpression))
{
return new SimpleExpressionReplacer(lambdaExpression.Parameters.Single(), expression).Visit(lambdaExpression.Body);
}
return Expression.Property(
expression,
node.Member.Name
);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda(
this.Visit(node.Body),
node.Parameters.Select(this.Visit).Cast<ParameterExpression>()
);
}
public AToBConverter(Dictionary<MemberInfo, LambdaExpression> mappings)
{
this._mappings = mappings;
}
}
sealed class SimpleExpressionReplacer : ExpressionVisitor
{
private readonly Expression _replacement;
private readonly Expression _toFind;
public override Expression Visit(Expression node)
{
return node == this._toFind ? this._replacement : base.Visit(node);
}
public SimpleExpressionReplacer(Expression toFind, Expression replacement)
{
this._toFind = toFind;
this._replacement = replacement;
}
}
class Program
{
private static Dictionary<MemberInfo, LambdaExpression> GetMappings()
{
var mappings = new Dictionary<MemberInfo, LambdaExpression>();
var mapping = GetMappingFor(model => model.HasEvenId, customer => (customer.Id % 2) == 0);
mappings.Add(mapping.Item1, mapping.Item2);
mapping = GetMappingFor(model => model.FullName, customer => customer.FirstName + " " + customer.LastName);
mappings.Add(mapping.Item1, mapping.Item2);
return mappings;
}
private static Tuple<MemberInfo, LambdaExpression> GetMappingFor<TValue>(Expression<Func<CustomerModel, TValue>> fromExpression, Expression<Func<Customer, TValue>> toExpression)
{
return Tuple.Create(((MemberExpression)fromExpression.Body).Member, (LambdaExpression)toExpression);
}
static void Main()
{
Expression<Func<CustomerModel, bool>> source = model => model.HasEvenId && model.FullName == "John Smith";
Expression<Func<Customer, bool>> desiredResult = model => (model.Id % 2) == 0 && (model.FirstName + " " + model.LastName) == "John Smith";
Expression output = new AToBConverter<CustomerModel, Customer>(GetMappings()).Visit(source);
Console.WriteLine("The two expressions do {0}match.", desiredResult.ToString() == output.ToString() ? null : "not ");
Console.ReadLine();
}
}
Another solution would be to use AutoMapper to map complex types and modify the resulting expression query with an ExpressionTransformer before it gets executed. I will try to explain with a complete sample:
Model classes
Some POCO classes just for holding data.
public enum CostUnitType
{
None = 0,
StockUnit = 1,
MachineUnit = 2,
MaintenanceUnit = 3
}
public class CostUnit
{
public string CostUnitId { get; set; }
public string WorkplaceId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public CostUnitType Type { get; set; }
public override string ToString()
{
return CostUnitId + " " + Name + " " + Type;
}
}
public class StockUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
public class MachineUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
public class MaintenanceUnit
{
public string Id { get; set; }
public string Name { get; set; }
}
The ExpressionTransformer class
Most of the time, using the Mapper static class is fine, but sometimes you need to map the same types with different configurations, so you need to explicitly use an IMappingEngine like this:
var configuration = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
var engine = new MappingEngine(configuration);
The way to create a MappingEngine is not obvious at all. I had to dig in the source code to find how it was done.
public static class ExpressionTransformer
{
private static readonly MappingEngine Mapper;
/// <summary>
/// Initializes the <see cref="ExpressionTransformer"/> class.
/// Creates an instance of AutoMapper. Initializes mappings.
/// </summary>
static ExpressionTransformer()
{
ConfigurationStore configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
// Create mapping
// Maps Id from StockUnit to CostUnitId from CostUnit
configurationStore.CreateMap<StockUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Maps Id from MachineUnit to CostUnitId from CostUnit
configurationStore.CreateMap<MachineUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Maps Id from MaintenanceUnit to CostUnitId from CostUnit
configurationStore.CreateMap<MaintenanceUnit, CostUnit>()
.ForMember(m => m.CostUnitId, opt => opt.MapFrom(src => src.Id));
// Create instance of AutoMapper
Mapper = new MappingEngine(configurationStore);
}
public static Expression<Func<TDestination, bool>> Tranform<TSource, TDestination>(Expression<Func<TSource, bool>> sourceExpression)
{
// Resolve mappings by AutoMapper for given types.
var map = Mapper.ConfigurationProvider.FindTypeMapFor(typeof(TSource), typeof(TDestination));
if (map == null)
{
throw new AutoMapperMappingException(string.Format("No Mapping found for {0} --> {1}.", typeof(TSource).Name, typeof(TDestination).Name));
}
// Transform from TSource to TDestination with specified mappings
var visitor = new ParameterTypeVisitor<TSource, TDestination>(sourceExpression, map.GetPropertyMaps());
var expression = visitor.Transform();
return expression;
}
private class ParameterTypeVisitor<TSource, TDestination> : ExpressionVisitor
{
private readonly Dictionary<string, ParameterExpression> _parameters;
private readonly Expression<Func<TSource, bool>> _expression;
private readonly IEnumerable<PropertyMap> _maps;
public ParameterTypeVisitor(Expression<Func<TSource, bool>> expression, IEnumerable<PropertyMap> maps)
{
_parameters = expression.Parameters
.ToDictionary(p => p.Name, p => Expression.Parameter(typeof(TDestination), p.Name));
_expression = expression;
_maps = maps;
}
public Expression<Func<TDestination, bool>> Transform()
{
return (Expression<Func<TDestination, bool>>) Visit(_expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TSource))
{
var memberName = node.Member.Name;
var member = _maps.FirstOrDefault(p => typeof(TSource) == node.Expression.Type
&& p.SourceMember.Name == memberName);
if (member != null)
{
// Return Property from TDestination
var expression = Visit(node.Expression);
return Expression.MakeMemberAccess(expression, member.DestinationProperty.MemberInfo);
}
}
return base.VisitMember(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
var parameter = _parameters[node.Name];
return parameter;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
var expression = Visit(node.Body);
return Expression.Lambda(expression, _parameters.Select(x => x.Value));
}
}
}
Usage
To Convert an expression we just need to call Transform Method of ExpressionTransformer class
Expression<Func<StockUnit, bool>> stockQuery = unit => unit.Id == "0815" && unit.Name == "ABC";
// Call Transform<TSource, TDestination> method.
Expression<Func<CostUnit, bool>> costUnitQuery = ExpressionTransformer.Tranform<StockUnit, CostUnit>(stockQuery);
Result

Copying ModelMetaData to Child Property's ModelMetaData

I've created a wrapper model which contains an inner property, Value, which is the property to be shown in the view. In the outermost model, I create a property which is the type of the wrapper and I apply some attributes and validations to that property. When the wrapper view is rendering, I want to copy all validations and attributes from the wrapper to the inner Value property so that when I finally call EditorFor(m => m.Value), the view that renders the child property also renders the validators, etc. related to the container property.
HomeController.cs
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new IndexModel());
}
}
}
IndexModel.cs
namespace MvcApplication1.Models.Home
{
public class IndexModel
{
[Display(Name = "Test Edit Field")]
[MaxLength(10)]
[AdditionalMetadata("ViewName", "String")]
[TemplateVisibility(false, true)]
public BatchEditField<string> TestEditField { get; set; }
public IndexModel()
{
this.TestEditField = new BatchEditField<string>("TEST");
}
}
}
EditField.cs (The "wrapper" model.)
namespace MvcApplication1.Models.Home
{
public class EditField
{
public string RawValue { get; set; }
public object Value { get; set; }
public string Description { get; set; }
public EditField() { }
public EditField(string rawValue, object value, string description = null)
{
this.RawValue = rawValue;
this.Value = value;
this.Description = (description ?? Convert.ToString(value));
}
public override string ToString()
{
return this.Description;
}
}
public class EditField<T> : BatchEditField
{
public new T Value { get { return (T)base.Value; } set { base.Value = value; } }
public EditField() { }
public EditField(string defaultValue) : base(null, defaultValue, null) { }
public EditField(string rawValue, T value, string description = null)
: base(rawValue, value, description)
{
}
}
}
EditField.cshtml (The "wrapper" view.)
#using S3.Common.Extensions
#model MvcApplication1.Models.Home.EditField
#{
//Attempting to copy AdditionalValues from the wrapper property to the child Value property.
ModelMetadata property = this.ViewData.ModelMetadata.Properties.Single(o => "Value".Equals(o.PropertyName));
foreach (var item in this.ViewData.ModelMetadata.AdditionalValues)
{
property.AdditionalValues[item.Key] = item.Value;
}
}
#Html.EditorFor(m => m.Value, Convert.ToString(this.ViewData.ModelMetadata.AdditionalValues.ValueOrDefault("ViewName")))
#if (this.Model != null && !this.Model.RawValue.IsNullOrEmpty() && !this.Model.RawValue.Trim().IsNullOrEmpty())
{
<p class="RawValue#(this.ViewData.ModelMetadata.AdditionalValues.ContainsKey("Style") ? " " + this.ViewData.ModelMetadata.AdditionalValues["Style"] : "")">#(this.Model.RawValue.IsNullOrEmpty() ? "(Not Specified)" : this.Model.RawValue)</p>
}
String.cshtml (The child view.)
#* Inspecting this.ViewData.ModelMetaData.AdditionalValues shows that it is empty; the parent AdditionalValues that were copied in the wrapper view did not make it through. *#
#Html.TextBox(string.Empty, this.ViewData.TemplateInfo.FormattedModelValue)
#Html.ValidationMessage(string.Empty)
Summary
Finally, when the page is rendered, no validation elements, etc. are included because they don't exist in the ModelMetaData when String.cshtml is rendered.

ASP.NET MVC 3 - Bind model to a List<> that contains different types

I'm trying to work out how to bind a model that contains a list of multiple object types to a postback action in ASP.NET MVC 3. I've got the following classes that define a list a vehicle types:
public enum VehicleType
{
Car,
Plane.
Boat
}
public class BaseVehicle
{
public VehicleType VehicleType { get; set; }
public string Name { get; set; }
public int Passengers { get; set; }
}
public class Plane : BaseVehicle
{
public int WingSpan { get; set; }
// -- etc --
}
// Properties omitted
public class Car : BaseVehicle {}
public class Boat : BaseVehicle {}
public class VehiclesViewModel
{
public string Notes { get; set; }
public List<BaseVehicle> Vehicles { get; set; }
}
The above classes are displayed by these views:
<!-- VehiclesView.cshtml - loaded by the controller -->
#model Mvc3Test.Models.VehiclesViewModel
<h2>Vehicles</h2>
#Html.EditorFor(m => m.Notes)
<hr />
#Html.EditorFor(m => m.Vehicles)
<!-- BaseVehicle.cshtml -->
#model BaseVehicle
#using Mvc3Test.Data
#Html.HiddenFor(m => m.VehicleType)
#{
if (Model.VehicleType == VehicleType.Car)
{
Html.RenderPartial("Car", (Car)Model);
}
else if (Model.VehicleType == VehicleType.Plane)
{
Html.RenderPartial("Plane", (Plane)Model);
}
// etc..
}
<-- Plane.cshtml -->
#model Mvc3Test.Data.Plane
<h2>Plane</h2>
<p>Name: #Html.EditorFor(m => m.Name)</p>
<p>Passengers: #Html.EditorFor(m => m.Passengers)</p>
<p>Wingspan: #Html.EditorFor(m => m.WingSpan) metres</p>
<!-- Car.cshtml omitted -->
I don't know it the above is the best way to handle the displaying (especially the if statement within a view), but it works for now. The problem is how to bind back to the viewmodel class. I've tried replacing the Html.TextBorFor() with Html.TextBox() so I can add binding prefixes ("Vehicles.Car" etc..) but there does not seem to be a way of getting the default model binders to determine what kind of class is being represented in the html so the right type can be instantiated.
I think I'll have to write a custom model binder to work it out - is this the right method to use or is there another way I've missed?
Thanks for any help
First of all
if (Model.VehicleType == VehicleType.Car)
{
Html.RenderPartial("Car", (Car)Model);
}
else if (Model.VehicleType == VehicleType.Plane)
{
Html.RenderPartial("Plane", (Plane)Model);
}
Can be omitted with EditorFor.Works just fine.
As for binding back - IMO best way is to create custom model binder.
Example:
public class DerivedTypeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var typeName = GetVehicleType(bindingContext);
if (typeName != null)
{
var modelType = Type.GetType(typeName);
var targetTypeInstance = Activator.CreateInstance(modelType);
bindingContext = new ModelBindingContext
{
ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => targetTypeInstance, modelType),
ModelState = bindingContext.ModelState,
FallbackToEmptyPrefix = bindingContext.FallbackToEmptyPrefix,
ModelName = bindingContext.FallbackToEmptyPrefix ? string.Empty : bindingContext.ModelName,
ValueProvider = bindingContext.ValueProvider,
};
}
return base.BindModel(controllerContext, bindingContext);
}
private string GetVehicleType(ModelBindingContext bindingContext)
{
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + "." + "VehicleType");
if (valueResult == null && bindingContext.FallbackToEmptyPrefix)
{
valueResult = bindingContext.ValueProvider.GetValue("VehicleType");
}
return valueResult == null ? null : valueResult.AttemptedValue;
}
}

Pass data serialized with Json.net library to the View and bind it with knockout

I am trying to send some serialized data to the view and bind it to the knockout code. I am using json.net library for serialization because I want to pass the constants of an enum to the view ( and not the underlying integers.) I am not sure how my controller returning Json data should look like. Here is the sample code:
My view model that will be serialized:
public class FranchiseInfoViewModel
{
public string FolderName { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public LobbyTemplateOptions LobbyTemplate { get; set; }
public List<LobbyTemplateOptions> LobbyTemplates { get; set; }
public void Initialize()
{
FolderName = "Test";
LobbyTemplate = LobbyTemplateOptions.G;
LobbyTemplates = new List<LobbyTemplateOptions>
{
LobbyTemplateOptions.G,
LobbyTemplateOptions.H,
LobbyTemplateOptions.I
};
Enum:
[JsonConverter(typeof(StringEnumConverter))]
public enum LobbyTemplateOptions
{
G = 7,
H = 8,
I = 9
}
My knockout code:
$(function () {
omega.FranchiseInfo = (function () {
var FolderName = ko.observable();
var LobbyTemplates = ko.observableArray([]);
$.getJSON("FranchiseData", function (data) {
FolderName(data.FolderName);
for (var i = 0; i < data.LobbyTemplate.length; i++) {
LobbyTemplates.push(data.LobbyTemplate[i]);
}
});
return {
folderName: FolderName,
lobbyTemplates: LobbyTemplates
}
} ());
ko.applyBindings(omega.FranchiseInfo);
})
}
I am wondering how my controller that passes serialized Json data to the view should look like as I have not used json.net and I am relatively new to programming:
Controller passing the Json data to the view:
public JsonResult FranchiseData()
{
FranchiseInfoViewModel franchiseInfoViewModel = new FranchiseInfoViewModel();
franchiseInfoViewModel.MapFranchiseInfoToFranchiseInfoViewModel();
string json = JsonConvert.SerializeObject(franchiseInfoViewModel);
// this is how I do it with the default Json serializer
// return Json(franchiseInfoViewModel, JsonRequestBehavior.AllowGet);
}
I would be very gratefull if somebody can post a working example of my controller. Thank You!
You need to implement JsonNetResult.
public class JsonNetResult : ActionResult
{
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public JsonSerializerSettings SerializerSettings { get; set; }
public Formatting Formatting { get; set; }
public JsonNetResult()
{
SerializerSettings = new JsonSerializerSettings();
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType)
? ContentType
: "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
JsonTextWriter writer = new JsonTextWriter(response.Output) { Formatting = Formatting };
JsonSerializer serializer = JsonSerializer.Create(SerializerSettings);
serializer.Serialize(writer, Data);
writer.Flush();
}
}
}
To use it, in your case you need to rewrite controller method in this way:
public ActionResult FranchiseData()
{
FranchiseInfoViewModel franchiseInfoViewModel = new FranchiseInfoViewModel();
franchiseInfoViewModel.MapFranchiseInfoToFranchiseInfoViewModel();
JsonNetResult jsonNetResult = new JsonNetResult();
jsonNetResult.Formatting = Formatting.Indented;
jsonNetResult.Data = franchiseInfoViewModel;
return jsonNetResult;
}
(implementation of JsonNetResult above was taken this blog post
http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx )

Resources