Labeling values returned from API - c#

This method returns a bool and a string -
public (bool active, string name) Report()
{
}
From my controller, I call it like this -
public IActionResult Credit([FromBody] Data data)
{
return Ok(Report())
}
The response I get is something like this -
{
"item1": false,
"item2": "Your name"
}
How do I get this response instead -
{
"Active": false,
"Name": "Your name"
}

The quick and easy way would be to return an anonymous type, taking the values from the returned tuple
public IActionResult Credit([FromBody] Data data)
{
//...
var report = Report();
return Ok(new
{
Active = report.active,
Name = report.name
})
}
Ideally you should return a strongly typed model that can be returned from the API
public class ReportModel
{
public string Name { get;set; }
public bool Active { get;set; }
}
and update accordingly
public ReportModel Report()
{
//...
}
public IActionResult Credit([FromBody] Data data)
{
//...
var report = Report();
return Ok(report);
}

Value tuples (as you are using for the return from your Report() method) are just syntactic sugar around a ValueTuple<T,T,...,T> object. Therefore, the real property names are not active and name but, in fact, item1 and item2.
So your method gets converted to something like this:
[return: TupleElementNames(new string[] {
"active",
"name"
})]
public ValueTuple<bool, string> Report()
How do you resolve this? You should create a model that reflects what you want to return:
public class ActiveName
{
public string Name { get;set;}
public bool Active {get;set;}
}
and then change your method to return this ActiveName type.
Another way is to return an anonymous type as dynamic, but I recommend against this approach since using dynamic introduces the potential for runtime errors if mistakes are made. If you're just using it to return from your API method, then it's probably OK.
public dynamic Report()
{
return new { name = "abc", active = true };
}

public IActionResult Credit([FromBody] Data data)
{
var x = Report();
return Ok(new {Active = x.Item1, UserName = x.Item2});
}

Related

Complex IEnumerable Result from LINQ Select

I'm trying to understand the resultant type from a LINQ select action. I'm having trouble understanding how to utilize the result.
Consider the following:
public class ReportContract
{
public int ContractId { get; set; }
public string ContractName { get; set; }
public string Title { get; set; }
public string[] Details { get; set; }
}
public class ReportContractVM
{
public int ContactId { get; set; }
public string ContractName { get; set; }
}
public class SomeController : ControllerBase
{
public ActionResult<IEnumerable<ReportContractVM>> Get()
{
IEnumerable<ReportContract> contracts = CreateConrtacts();
IEnumerable<ReportContractVM> result = contracts.Select(
x => new ReportContractVM
{
ContactId = x.ContractId,
ContractName = x.ContractName
}
);
return Ok(result);
}
private IEnumerable<ReportContract> CreateContracts()
{
List<ReportContract> contracts = new List<ReportContract>()
{
new ReportContract {
ContractId = 1234,
ContractName= "ABCD",
Details= new string[] {
"test", "Best", "rest", "jest"},
Title="First"
},
new ReportContract {
ContractId = 1235,
ContractName= "EFGH",
Details= new string[] {
"fish", "dish", "wish", "pish"},
Title="Second"
},
new ReportContract {
ContractId = 1236,
ContractName= "IJKL",
Details= new string[] {
"hot", "tot", "mot", "jot"},
Title="Third"
},
new ReportContract {
ContractId = 1237,
ContractName= "MNOP",
Details= new string[] {
"ring", "sing", "bing", "ping"},
Title="Fourth"
}
};
return contracts;
}
}
Inspecting the type assigned to 'result' in the debugger yields:
System.Linq.Enumberable.SelectListIterator
<
ComplexIEnumerable.ReportContract,
ComplexIEnumerable.ReportContractVM
>
I am not sure how this relates to
IEnumerable<ReportContractVM>.
How is
IEnumerable<ReportContractVM>
accessed from this result?
Please ignore the rest of this text. The "intelligent" editor is demanding more detail and I think this suffices.
I recommend reading this article that explains what Enumerator (aka yield return methods) materialization is - as it's core to how Linq works and how to use IEnumerable<T> results (note that an IEnumerable<T> can refer to a both materialized collections like T[] or List<T> and also non-materialized enumerators):
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/intermediate-materialization
As for your case, generally speaking, never return a non-materialized Entity Framework (i.e. database-backed) IEnumerable<T> or IQueryable<T> from an ASP.NET Web API Controller or pass a non-materialized IEnumerable<T> as a member of a ViewModel (this is because the lifetime of the parent DbContext may be shorter than the lifetime of the IEnumerable<T>/IQueryable<T> object). However returning an non-materialized collection using only in-memory objects is fine (such as an enumerator over enum values to get SelectListItem objects):
Specifically, change this:
IEnumerable<ReportContractVM> result = contracts.Select(
x => new ReportContractVM
{
ContactId = x.ContractId,
ContractName = x.ContractName
}
);
to this:
List<ReportContractVM> list = contracts
.Select( x => new ReportContractVM { ContractId = x.ContractId, ContractName = x.ContractName } )
.ToList();
Also, change your ActionResult<T> to use IReadOnlyList<T> instead of IEnumerable<T> which will help ensure you always return a materialized list instead of a possibly non-materialized enumerator:
public class SomeController : ControllerBase
{
public ActionResult<IReadOnlyList<ReportContractVM>> Get()
{
}
}
BTW, I've been having problems with ActionResult<T> not working well with generics and in async Task<ActionResult<T>> actions so I personally prefer to do this, but YMMV:
public class SomeController : ControllerBase
{
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public IActionResult Get()
{
}
// if async:
[Produces( typeof( IReadOnlyList<ReportContractVM> ) )
public async Task<IActionResult> Get()
{
}
}
Basically the output is the concrete enumerable type returned from Select with type parameters referencing both the input and output types. It may be easier to see in a simplified example below.
namespace ExampleNamespace
{
class A
{
public A(int i) { }
}
}
Now say I have this code somewhere:
List<int> i = new List<int>() { 1, 2, 3, 4, 5, 6 };
var temp = i.Select(x => new A(x));
Upon inspection the type of temp is:
System.Linq.Enumerable.WhereSelectListIterator<int, ExampleNamespace.A>
So to break this down:
System.Linq.Enumerable.WhereSelectListIterator is the type of iterator returned from Select. This differs from your type slightly probably due to different .NET versions.
int is the the type that your starting IEnumerable held
ExampleNamespace.A is the full name of the output type.
That all being said, you should read and understand what Dai posted in their answer.

How can i see if my JSON contains a certain value and then compare it?

var Name = "Resources.myjson.json";
var NameJSON = new System.IO.StreamReader(typeof(Strings).GetTypeInfo().Assembly.GetManifestResourceStream(Name)).ReadToEnd();
var ParsedBrandJSON = Newtonsoft.Json.JsonConvert.DeserializeObject<TheInfo>(NameJSON);
await JsonCS.LoadJson(ParsedBrandJSON);
And on the page:
static public class TheInfoJSON
{
static public TheInfo Data { get; set; }
static public async Task LoadJson(Data JSON)
{
Data = JSON;
}
}
and
public class TheInfo
{
public List<TheName> TheName { get; set; } = new List<TheName>();
}
My json:
{
"TheInfo":[
{
"TheName": ["Martin", "Jonas", "Alex", "Oscar"]
}
]
}
When i now try to compare how can i see if my JSON contains a certain object and then store that as a single TheName? Is it possible to do it in the same cast?
var TheNameDataFromOtherPage = OtherPage.TheName; //Here i gather the name from another page that i will compare with the JSON
//Wrong syntax
bool DoTheyMatch = TheNameDataFromOtherPage == TheInfoJSON.Data.TheName.Contains("Alex");
This is now wrong syntax because i cant compare the value to a bool. How can i get out the data i find and then instead of having TheInfoJSON.Data.TheName.Contains("Alex"); as a bool, back to a single value of TheName containing "Alex" so I can create a bool out of the two values to see if the JSON has it or not.
I tried to add something along the lines like this after the contains(): as TheInfo.TheName but that isnt the correct syntax either.
bool DoTheyMatch = TheInfoJSON.Data.TheName.Contains(TheNameDataFromOtherPage);

Pass complex parameters to [Theory]

Xunit has a nice feature: you can create one test with a Theory attribute and put data in InlineData attributes, and xUnit will generate many tests, and test them all.
I want to have something like this, but the parameters to my method are not 'simple data' (like string, int, double), but a list of my class:
public static void WriteReportsToMemoryStream(
IEnumerable<MyCustomClass> listReport,
MemoryStream ms,
StreamWriter writer) { ... }
There are many xxxxData attributes in XUnit. Check out for example the MemberData attribute.
You can implement a property that returns IEnumerable<object[]>. Each object[] that this method generates will be then "unpacked" as a parameters for a single call to your [Theory] method.
See i.e. these examples from here
Here are some examples, just for a quick glance.
MemberData Example: just here at hand
public class StringTests2
{
[Theory, MemberData(nameof(SplitCountData))]
public void SplitCount(string input, int expectedCount)
{
var actualCount = input.Split(' ').Count();
Assert.Equal(expectedCount, actualCount);
}
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "xUnit", 1 },
new object[] { "is fun", 2 },
new object[] { "to test with", 3 }
};
}
XUnit < 2.0: Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
ClassData Example
public class StringTests3
{
[Theory, ClassData(typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
XUnit >= 2.0: Instead of ClassData, now there's an 'overload' of [MemberData] that allows to use static members from other classes. Examples below have been updated to use it, since XUnit < 2.x is pretty ancient now.
Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
MemberData Example: look there to another type
public class StringTests3
{
[Theory, MemberData(nameof(IndexOfData.SplitCountData), MemberType = typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
}
Disclaimer :)
Last time checked #20210903 with dotnetfiddle.net on C# 5.0 and xunit 2.4.1 .. and failed. I couldn't mix-in a test-runner into that fiddle. But at least it compiled fine. Note that this was originally written years ago, things changed a little. I fixed them according to my hunch and comments. So.. it may contain inobvious typos, otherwise obvious bugs that would instantly pop up at runtime, and traces of milk & nuts.
Suppose that we have a complex Car class that has a Manufacturer class:
public class Car
{
public int Id { get; set; }
public long Price { get; set; }
public Manufacturer Manufacturer { get; set; }
}
public class Manufacturer
{
public string Name { get; set; }
public string Country { get; set; }
}
We're going to fill and pass the Car class to a Theory test.
So create a 'CarClassData' class that returns an instance of the Car class like below:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="country",
Name="name"
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
It's time for creating a test method(CarTest) and define the car as a parameter:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(Car car)
{
var output = car;
var result = _myRepository.BuyCar(car);
}
**If you're going to pass a list of car objects to Theory then change the CarClassData as follow:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new List<Car>()
{
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="Iran",
Name="arya"
}
},
new Car
{
Id=2,
Price=45000,
Manufacturer = new Manufacturer
{
Country="Torbat",
Name="kurosh"
}
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
And the theory will be:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(List<Car> cars)
{
var output = cars;
}
Good Luck
To update #Quetzalcoatl's answer: The attribute [PropertyData] has been superseded by [MemberData] which takes as argument the string name of any static method, field, or property that returns an IEnumerable<object[]>. (I find it particularly nice to have an iterator method that can actually calculate test cases one at a time, yielding them up as they're computed.)
Each element in the sequence returned by the enumerator is an object[] and each array must be the same length and that length must be the number of arguments to your test case (annotated with the attribute [MemberData] and each element must have the same type as the corresponding method parameter. (Or maybe they can be convertible types, I don't know.)
(See release notes for xUnit.net March 2014 and the actual patch with example code.)
Creating anonymous object arrays is not the easiest way to construct the data so I used this pattern in my project.
First define some reusable, shared classes:
//http://stackoverflow.com/questions/22093843
public interface ITheoryDatum
{
object[] ToParameterArray();
}
public abstract class TheoryDatum : ITheoryDatum
{
public abstract object[] ToParameterArray();
public static ITheoryDatum Factory<TSystemUnderTest, TExpectedOutput>(TSystemUnderTest sut, TExpectedOutput expectedOutput, string description)
{
var datum= new TheoryDatum<TSystemUnderTest, TExpectedOutput>();
datum.SystemUnderTest = sut;
datum.Description = description;
datum.ExpectedOutput = expectedOutput;
return datum;
}
}
public class TheoryDatum<TSystemUnderTest, TExpectedOutput> : TheoryDatum
{
public TSystemUnderTest SystemUnderTest { get; set; }
public string Description { get; set; }
public TExpectedOutput ExpectedOutput { get; set; }
public override object[] ToParameterArray()
{
var output = new object[3];
output[0] = SystemUnderTest;
output[1] = ExpectedOutput;
output[2] = Description;
return output;
}
}
Now your individual test and member data is easier to write and cleaner...
public class IngredientTests : TestBase
{
[Theory]
[MemberData(nameof(IsValidData))]
public void IsValid(Ingredient ingredient, bool expectedResult, string testDescription)
{
Assert.True(ingredient.IsValid == expectedResult, testDescription);
}
public static IEnumerable<object[]> IsValidData
{
get
{
var food = new Food();
var quantity = new Quantity();
var data= new List<ITheoryDatum>();
data.Add(TheoryDatum.Factory(new Ingredient { Food = food } , false, "Quantity missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity } , false, "Food missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid" ));
return data.ConvertAll(d => d.ToParameterArray());
}
}
}
The string Description property is to throw yourself a bone when one of your many test cases fail.
You can try this way:
public class TestClass {
bool isSaturday(DateTime dt)
{
string day = dt.DayOfWeek.ToString();
return (day == "Saturday");
}
[Theory]
[MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))]
public void test(int i)
{
// parse test case
var input = TestCase.IsSaturdayTestCase[i];
DateTime dt = (DateTime)input[0];
bool expected = (bool)input[1];
// test
bool result = isSaturday(dt);
result.Should().Be(expected);
}
}
Create another class to hold the test data:
public class TestCase
{
public static readonly List<object[]> IsSaturdayTestCase = new List<object[]>
{
new object[]{new DateTime(2016,1,23),true},
new object[]{new DateTime(2016,1,24),false}
};
public static IEnumerable<object[]> IsSaturdayIndex
{
get
{
List<object[]> tmp = new List<object[]>();
for (int i = 0; i < IsSaturdayTestCase.Count; i++)
tmp.Add(new object[] { i });
return tmp;
}
}
}
For my needs I just wanted to run a series of 'test users' through some tests - but [ClassData] etc. seemed overkill for what I needed (because the list of items was localized to each test).
So I did the following, with an array inside the test - indexed from the outside:
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Account_ExistingUser_CorrectPassword(int userIndex)
{
// DIFFERENT INPUT DATA (static fake users on class)
var user = new[]
{
EXISTING_USER_NO_MAPPING,
EXISTING_USER_MAPPING_TO_DIFFERENT_EXISTING_USER,
EXISTING_USER_MAPPING_TO_SAME_USER,
NEW_USER
} [userIndex];
var response = await Analyze(new CreateOrLoginMsgIn
{
Username = user.Username,
Password = user.Password
});
// expected result (using ExpectedObjects)
new CreateOrLoginResult
{
AccessGrantedTo = user.Username
}.ToExpectedObject().ShouldEqual(response);
}
This achieved my goal, while keeping the intent of the test clear. You just need to keep the indexes in sync but that's all.
Looks nice in the results, it's collapsable and you can rerun a specific instance if you get an error:
This is how I solved your problem, I had the same scenario. So inline with custom objects and a different number of objects on each run.
[Theory]
[ClassData(typeof(DeviceTelemetryTestData))]
public async Task ProcessDeviceTelemetries_TypicalDeserialization_NoErrorAsync(params DeviceTelemetry[] expected)
{
// Arrange
var timeStamp = DateTimeOffset.UtcNow;
mockInflux.Setup(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>())).ReturnsAsync("Success");
// Act
var actual = await MessageProcessingTelemetry.ProcessTelemetry(JsonConvert.SerializeObject(expected), mockInflux.Object);
// Assert
mockInflux.Verify(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>()), Times.Once);
Assert.Equal("Success", actual);
}
So this is my unit test, notice the params parameter. This allow to send a different number of object. And now my DeviceTelemetryTestData class :
public class DeviceTelemetryTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Hope it helps !
Despite this has already been answered I just want to add an improvement here.
The restriction of passing objects in InlineData attribute is not a limitiation of xUnit itself but C# attributes.
See this compiler error: Compiler Error CS0182
xUnit.Sdk provides you with DataAttribute class that you could inherit and override its GetData method and use it to pass whatever you feel like you want..
I usually use it alongside DataTestBuilders pattern and build something like that..
public class ValidComplexObjectDataSource : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.Build()
};
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.WithoutSomeAttribute()
.Build()
};
// ... list all test cases you want to pass to your method
}
}
This ComplexObjectBuilder could be whatever your object is, highly recommend checking builder pattern
[Theory]
[Trait("Validation", "CreateXYZCommand")]
[ValidComplexObjectDataSource]
public void CreateXYZCommandValidator_WithValidInput_ShouldPassAllValidations(CreateComplexObjectInput createComplexObjectInput)
{
var command = new CreateXYZCommand(createComplexObjectInput);
var result = _validator.TestValidate(command);
result.ShouldNotHaveAnyValidationErrors();
}
I only demonstrated it with a single object, you have an array of objects you can yield.
yield return new object[] {
ComplexObject_1,
ComplexObject_2,
string_attribute,
int_attribute
};
and have these as arguments to your test cases.
You can utilize TheoryData for complex types like classes.
[Theory, MemberData(nameof(CustomClassTests))]
public async Task myTestName(MyCustomClass customClassTestData) { ... }
public record MyCustomClass { ... }
public static TheoryData<MyCustomClass> CustomClassTests {
get {
return new() {
new MyCustomClass{ ... },
new MyCustomClass{ ... },
...
};
}
}
I guess you mistaken here. What xUnit Theory attribute actually means: You want to test this function by sending special/random values as parameters that this function-under-test receives. That means that what you define as the next attribute, such as: InlineData, PropertyData, ClassData, etc.. will be the source for those parameters. That means that you should construct the source object to provide those parameters. In your case I guess you should use ClassData object as source. Also - please note that ClassData inherits from: IEnumerable<> - that means each time another set of generated parameters will be used as incoming parameters for function-under-test until IEnumerable<> produces values.
Example here: Tom DuPont .NET
Example may be incorrect - I didn't use xUnit for a long time

Selectively Whitelisting Model Fields to Bind

(I realize this question is very similar to How to whitelist/blacklist child object fields in the ModelBinder/UpdateModel method? but my situation is slightly different and there may be a better solution available now that wasn't then.)
Our company sells web-based software that is extremely configurable by the end-user. The nature of this flexibility means that we must do a number of things at run time that would normally be done at compile time.
There are some rather complex rules regarding who has read or read/write access to most everything.
For instance, take this model that we would like to create:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace j6.Business.Site.Models
{
public class ModelBindModel
{
[Required]
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string FirstName { get; set; }
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string MiddleName { get; set; }
[Required]
[Whitelist(ReadAccess = true, WriteAccess = true)]
public string LastName { get; set; }
[Required]
[Whitelist(ReadAccess = User.CanReadSalary, WriteAccess = User.CanWriteSalary)]
public string Salary { get; set; }
[Required]
[Whitelist(ReadAccess = User.CanReadSsn, WriteAccess = User.CanWriteSsn)]
public string Ssn { get; set; }
[Required]
public string SirNotAppearingOnThisPage { get; set; }
}
}
In the controller, it is not difficult to "unbind" things manually.
var resetValue = null;
modelState.Remove(field);
pi = model.GetType().GetProperty(field);
if (pi == null)
{
throw new Exception("An exception occured in ModelHelper.RemoveUnwanted. Field " +
field +
" does not exist in the model " + model.GetType().FullName);
}
// Set the default value.
pi.SetValue(model, resetValue, null);
Using HTML helpers, I can easily access the model metadata and suppress rendering of any fields the user does not have access to.
The kicker: I can't figure out how to access the model metadata anywhere in the CONTROLLER itself to prevent over-posting.
Note that using [Bind(Include...)] is not a functional solution, at least not without additional support. The properties to Include are run-time (not compile time) dependent, and excluding the property does not remove it from the validation.
ViewData.Model is null
ViewData.ModelMetaData is null
[AllowAnonymous]
[HttpPost]
// [Bind(Exclude = "Dummy1" + ",Dummy2")]
public ViewResult Index(ModelBindModel dto)
{
zzz.ModelHelper.RemoveUnwanted(ModelState, dto, new string[] {"Salary", "Ssn"});
ViewBag.Method = "Post";
if (!ModelState.IsValid)
{
return View(dto);
}
return View(dto);
}
Any suggestions on how to access the Model MetaData from the controller? Or a better way to whitelist properties at run time?
Update:
I borrowed a page from this rather excellent resource:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=687
With a model that looks like this:
[Required]
[WhiteList(ReadAccessRule = "Nope", WriteAccessRule = "Nope")]
public string FirstName { get; set; }
[Required]
[WhiteList(ReadAccessRule = "Database.CanRead.Key", WriteAccessRule = "Database.CanWrite.Key")]
public string LastName { get; set; }
The class:
public class WhiteList : Attribute
{
public string ReadAccessRule { get; set; }
public string WriteAccessRule { get; set; }
public Dictionary<string, object> OptionalAttributes()
{
var options = new Dictionary<string, object>();
var canRead = false;
if (ReadAccessRule != "")
{
options.Add("readaccessrule", ReadAccessRule);
}
if (WriteAccessRule != "")
{
options.Add("writeaccessrule", WriteAccessRule);
}
if (ReadAccessRule == "Database.CanRead.Key")
{
canRead = true;
}
options.Add("canread", canRead);
options.Add("always", "be there");
return options;
}
}
And adding these lines to the MetadataProvider class mentioned in the link:
var whiteListValues = attributes.OfType<WhiteList>().FirstOrDefault();
if (whiteListValues != null)
{
metadata.AdditionalValues.Add("WhiteList", whiteListValues.OptionalAttributes());
}
Finally, the heart of the system:
public static void DemandFieldAuthorization<T>(ModelStateDictionary modelState, T model)
{
var metaData = ModelMetadataProviders
.Current
.GetMetadataForType(null, model.GetType());
var props = model.GetType().GetProperties();
foreach (var p in metaData.Properties)
{
if (p.AdditionalValues.ContainsKey("WhiteList"))
{
var whiteListDictionary = (Dictionary<string, object>) p.AdditionalValues["WhiteList"];
var key = "canread";
if (whiteListDictionary.ContainsKey(key))
{
var value = (bool) whiteListDictionary[key];
if (!value)
{
RemoveUnwanted(modelState, model, p.PropertyName);
}
}
}
}
}
To recap my interpretation of your question:
Field access is dynamic; some users may be able to write to a field and some may not.
You have a solution to control this in the view.
You want to prevent a malicious form submission from sending restricted properties, which the model binder will then assign to your model.
Perhaps something like this?
// control general access to the method with attributes
[HttpPost, SomeOtherAttributes]
public ViewResult Edit( Foo model ){
// presumably, you must know the user to apply permissions?
DemandFieldAuthorization( model, user );
// if the prior call didn't throw, continue as usual
if (!ModelState.IsValid){
return View(dto);
}
return View(dto);
}
private void DemandFieldAuthorization<T>( T model, User user ){
// read the model's property metadata
// check the user's permissions
// check the actual POST message
// throw if unauthorized
}
I wrote an extension method a year or so ago that has stood me in good stead a couple of times since. I hope this is of some help, despite not being perhaps the full solution for you. It essentially only allows validation on the fields that have been present on the form sent to the controller:
internal static void ValidateOnlyIncomingFields(this ModelStateDictionary modelStateDictionary, FormCollection formCollection)
{
IEnumerable<string> keysWithNoIncomingValue = null;
IValueProvider valueProvider = null;
try
{
// Transform into a value provider for linq/iteration.
valueProvider = formCollection.ToValueProvider();
// Get all validation keys from the model that haven't just been on screen...
keysWithNoIncomingValue = modelStateDictionary.Keys.Where(keyString => !valueProvider.ContainsPrefix(keyString));
// ...and clear them.
foreach (string errorKey in keysWithNoIncomingValue)
modelStateDictionary[errorKey].Errors.Clear();
}
catch (Exception exception)
{
Functions.LogError(exception);
}
}
Usage:
ModelState.ValidateOnlyIncomingFields(formCollection);
And you'll need a FormCollection parameter on your ActionResult declaration, of course:
public ActionResult MyAction (FormCollection formCollection) {

Json serialization in asp.net mvc c#

public ActionResult About()
{
List listStores = new List();
listStores = this.GetResults(“param”);
return Json(listStores, “Stores”, JsonRequestBehavior.AllowGet);
}
Using the above code I am able to get the below result :
[{"id":"1","name":"Store1","cust_name":"custname1","telephone":"1233455555",
"email":"abc#ac.com","geo":{"latitude":"12.9876","longitude":"122.376237"}},
{"id":"2","name":"Store2","cust_name":"custname2","telephone":"1556454",
"email":"nfnf#ac.com","geo":{"latitude":"12.9876","longitude":"122.376237"}},
how would I able to get the result in below format? Would need stores at the beginning of the result.
{
"stores" : [
{"id":"1","name":"Store1","cust_name":"custname1","telephone":"1233455555",
"email":"abc#ac.com",
"geo":{"latitude":"12.9876","longitude":"122.376237"}},
{"id":"2","name":"Store2","cust_name":"custname2","telephone":"1556454",
"email":"nfnf#ac.com","geo":{"latitude":"12.9876","longitude":"122.376237"
}} ] }
Try
return Json(new { stores = listStores }, JsonRequestBehavior.AllowGet);
In the above statement, you're creating a new object with a property named "stores", which is then populated with the array of items from the list.
You could also use a class, something defined like so:
[DataContract]
public class StoreListJsonDTO
{
[DataMember(Name = "stores")]
public List Stores { get; set; }
public StoreListJsonDTO(List storeList)
{
this.Stores = storeList;
}
}
Then in your code, you'd do:
var result = new StoreListJsonDTO(listStores);
return Json(result, JsonRequestBehavior.AllowGet);

Categories