How to hide a Model class field based on custom logic in MVC Web Api RC - asp.net-mvc-4

I am using Asp.Net Mvc Web api RC.
I wanted to hide the fields/properties of my model class using custom attribute. Below is my class:
public class Employee
{
public int EmpId { get; set; }
public string Name{ get; set; }
//Wanted to hide this attribute based on custom logic. Like for a certain role, i want to hide the designation
public string Designation{ get; set; }
public string Department{ get; set; }
}
How can we achieve using Data Annotations. I mean i wanted to create a separate attribute to use in this manner:
[HideForRoles(Roles="Admin,Writer")]
public string Designation{ get; set; }
UPDATE :
As i am developing web api. The response is serialized to either XML or Json format depend upon the formatter. So better question would be how not to allow the fields to be serialize while writing to the response.
However one option could be using IgnoreDataMember attribute. Like
[IgnoreDataMember]
public string Designation{ get; set; }
But the above is a compile time declaration where i cannot impose any condition.
Question: How to ignore the field/property while serializing based on some condition at runtime?

Totally missed on the first go-round that you were using Web Api, my apologies.
What you want to do is to create a custom formatter.
There's a good article here on the flow/differences between MVC and Web Api (which I'm getting that you already understand, still some valid points here):
http://lostechies.com/jimmybogard/2012/04/10/asp-net-web-api-mvc-viewmodels-and-formatters/
And here's a sample implementation of a custom formatter:
http://www.tugberkugurlu.com/archive/creating-custom-csvmediatypeformatter-in-asp-net-web-api-for-comma-separated-values-csv-format
Building from that, you would use reflection to read from the attributes, building on the custom ActionFilterAttribute you would have to write, where you evaluate the user's roles and determine which fields should be omitted/included. Here's a sample of an action filter:
https://github.com/MisterJames/MovieFu/blob/master/MovieFu/ActionFilters/UserNameFilter.cs
Hope this helps more.
Cheers.

Your best bet is to return a dynamic object. In this case you can say:
dynamic viewModel = new ExpandoObject();
viewModel.Id = 12;
if(role == "Admin")
{
viewModel.SecureStuff = "Others should not see it";
}

It won't be as straightforward as that, as you'll need to have the fields conditionally rendering in the view. But you can get most of the way there through the attribute.
You will need to make your custom attribute meta-data aware, then check the attribute in your view. A solution is posted here: Can't get Custom Attribute Value in MVC3 HTML Helper.
Cheers.

I have done the authorization checking in the model repository itself. Rather ideal way was to create custom formatters for hiding the certain fields based on some condition.
After getting the list of Employees from db and have them in list, i iterated again and place a NULL to the fields i don't want to display.
The code i have written as:
foreach (var employee in listEmployees)
{
//get all props. of Employees object using reflection
var props = employee .GetType().GetProperties();
//loop through each field to match with the field name to remove/place null
foreach (var propertyInfo in props)
{
var fieldName = propertyInfo.Name;
if (fieldsNamesToRemove .Contains(fieldName))
{
propertyInfo.SetValue(employee , null, null);
}
}
}
here fieldsNamesToRemove is a list that i created dynamically based on roles of current user.
This solution actually placing a NULL for the fields we do not want display. As a result in JSon format the fields are not displaying but in the XML the fields are displaying with syntax like lt; Designation i:nil="true"/ gt;, but manageable as we need to deal mostly with json response.
Thanks Ali and MisterJames for your valuable suggestions

Related

Instantiating ModelExpression directly

Let's say I have the following input tag which utilizes the built-in tag helper:
#model ProductViewModel
<label asp-for="Product.Id"></label>
In my case, this expands into the following:
<label for="Product_Id">Id</label>
I see that asp-for is expecting a ModelExpression:
In tag helper implementations, I often see a property like the following:
public ModelExpression For { get; set; }
It appears that this is automatically populated when the tag helper is used.
Is there a way to instantiate a ModelExpression directly in C#?
I.e. something like this:
var exp = new ModelExpression("Product.Id",...)
I'd like to be able to generate "Product_Id" and "Id" from Product.Id as the input tag helper did.
As far as I know, you can specify that your property is to be set to the name of some property on the View's Model object by declaring your property with the ModelExpression type. This will enable any developer using your property to get IntelliSense support for entering a property name from the Model object. More importantly, your code will be passed the value of that property through the ModelExpression's Model property.
Sample code as below:
[HtmlTargetElement("employee-details")]
public class EmployeeDetailTagHelper : TagHelper
{
[HtmlAttributeName("for-name")]
public ModelExpression EmployeeName { get; set; }
[HtmlAttributeName("for-designation")]
public ModelExpression Designation { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = "EmployeeDetails";
output.TagMode = TagMode.StartTagAndEndTag;
var sb = new StringBuilder();
sb.AppendFormat("<span>Name: {0}</span> <br/>", this.EmployeeName.Model);
sb.AppendFormat("<span>Designation: {0}</span>", this.Designation.Model);
output.PreContent.SetHtmlContent(sb.ToString());
}
}
Code in the View page:
#model WebApplication7.Models.EmployeeViewModel
<div class="row">
<employee-details for-name="Name" for-designation="Designation"></employee-details>
</div>
Code in the Model
public class EmployeeViewModel
{
public string Name { get; set; }
public string Designation { get; set; }
}
From above code, you can see that we could custom the attribute name. More detail information about using the ModelExpression, check the following links:
Creating Custom Tag Helpers With ASP.NET Core MVC
Expression names
I'd like to be able to generate "Product_Id" and "Id" from Product.Id
as the input tag helper did.
Besides, do you mean you want to change the Product. Id to Product_Id, in my opinion, I'm not suggesting you change it, because generally we can use "_" as a separator in the property name. So, if we are using Product.Id, it means the Product's Id property, and the Product_Id means there have a Product_Id property.
To answer the question:
Is there a way to instantiate a ModelExpression directly in C#"
Yes you can, through IModelExpressionProvider and its CreateModelExpression method. You can get an instance of this interface through DI.
Now, if you're already in your view and working with tag helpers, Zhi Lv's answer is all you need, as the functionality is built-in and much easier to use. You only need IModelExpressionProvider for when you're in your Razor Page, Controller, or perhaps some custom middleware. Personally, I find this functionality useful for my Ajax handlers that need to return one of my ViewComponents that has a ModelExpression argument (so that I can easily call it from my Pages/Views too.)
To call CreateModelExpression, you'll need a strongly-typed instance of ViewData. In Razor Pages, this is as easy as casting the ViewData property to the strongly-typed instance of your PageModel's type (presuming you don't have a page model hierarchy):
var viewData = (ViewDataDictionary<IndexModel>)ViewData;
If you're using MVC and you're in the controller, that won't exist yet. Best you can do is make your own instance.
var viewData = new ViewDataDictionary<ErrorViewModel>(new EmptyModelMetadataProvider(),
new ModelStateDictionary());
Once you get your strongly-typed ViewData instance, you can obtain your desired ModelExpression like this, just like using a lambda expression in your views:
var myPropertyEx = _modelExpressionProvider.CreateModelExpression(viewData,
m => m.MyProperty);

How to bind dynamic complex objects created using partial-view to a collection property in view-model

I'm unable to bind a collection of child-complext objects created dynamically using a partial-view to view-model IEnumerable property.
I have successfully bound objects created dynamically using partial-views to a view-model using a technique I found on this blog https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/. I have followed the same technique but I'm unable to bind a collection to a IEnumerable property in a view-model.
[BindRequired]
public class EmployeeViewModel
{
other properties....
public IEnumerable<ContactDetailViewModel> EmployeeContact { get; set; }
}
[BindRequired]
public class ContactDetailViewModel
{
// I use this as my indexer for dynamic elements
public string RecordId { get; set; } = Guid.NewGuid().ToString();
public string Telephone { get; set; }
public string EmailAddress { get; set; }
public string ContactDescription { get; set; }
}
I call into this action-method via ajax to add dynamic contact detail elements and it returns the partial-view as html and it works fine.
[Route("[action]", Name = "BlankEmployeeContactDetail"), HttpGet("AddBlankContactDetail")]
public PartialViewResult AddBlankContactDetail()
{
return PartialView("_ContactInformation", new ContactDetailViewModel());
}
The initial contact detail is added to the main-view using the following, kindly follow this link https://1drv.ms/u/s!AkRSHVUtFlKhuHaxH96Ik4ineATE to download the main view and partial-view cshtml files. It is also noteworthy to mention that model binding fails for all other properties when I include this partial-view but works when I comment it out. I'm baffled and would greatly appreciate any help you can afford me.
<section id="widget-grid" class="">
<div class="row contactContainer">
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel()); }
</div>
</section>
This is the controller action method I'm trying to bind posted data to:
[Route("[action]"), HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public IActionResult Register([FromForm] EmployeeViewModel model, [FromQuery] string returnUrl = null)
{
if (ModelState.IsValid)
{
}
return View(model);
}
In order to bind, the input names much follow a particular convention that maps to what you're binding to. While it's unclear from your question, my best guess is that you're trying to ultimately bind to an instance of EmployeeViewModel, which means that your contact information inputs would need names like: EmployeeContact[0].Telephone, but when you pass an instance of ContactDetailViewModel along as the "model" of the partial view, the names will be just Telephone, and worse, these same names will be repeated over and over, i.e. each contact information set of fields you create will all have an input named just Telephone.
Long and short, you need the context of the whole model to generate the correct input names. You have a couple of options.
Since you're retrieving the set of fields via an AJAX request, it would be possible to pass the "prefix" to use along with that request. In other words, you can keep track of an index value, counting how many of these sections you've added, and then send along with the request for a new section something like
prefix: 'EmployeeContact[' + (i + 1) + ']',
Then, in your partial view:
#{ await Html.RenderPartialAsync("_ContactInformation", new ContactDetailViewModel(), new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = ViewBag.Prefix } } ); }
That's a little hacky, and honestly probably rather prone to error, though. The better option would be to take an entirely different approach. Instead of calling back to get the partial view, define it just once as a template:
<script type="text/html" id="ContactInformationTemplate">
<!-- HTML for contact information inputs -->
</script>
Then, using a library like Vue, React, Angular, etc., you can set up a "foreach" construction tied to a particular JavaScript array which uses this template to render items in that array. Then, adding a new set of inputs is as simple as adding a new item to the array. You will have to do some works to customize the input names based on the index of the item in the array, but all of these client-side frameworks have ways to do that. That would also have the side benefit of not having to make an AJAX request every time you want to add a new section.

MVC 4 Validation Attribute is not working for dynamically added fields

Here are my Product and ProductItem classes/models:
public class Product
{
public int ProductId { get; set; }
[Required(ErrorMessage="Enter Name")]
public string Name { get; set; }
public List<ProductItem> productitems { get; set; }
[Required(ErrorMessage="Enter Price")]
public decimal Price { get; set; }
}
public class ProductItem
{
[Required(ErrorMessage="Select Raw Material")]
public int RawMaterial { get; set; }
[Required(ErrorMessage="Enter Quantity")]
public decimal Qty { get; set; }
}
For ProductItem I am adding its fields dynamically with jQuery, as you can see here:
$("#btnAddProductItem").click(function () {
$.getJSON("/rawmaterial/GetRawMaterials", null, function (data) {
var productItem = $("<tr class='productItem' id='productItem-0'><td><select id='rmlist-0' name='productitems[0].RawMaterial'></select><span class='field-validation-valid' data-valmsg-for='productitems[0].RawMaterial' data-valmsg-replace='true'></span></td><td><input type='text' id='rmqty-0' name='productitems[0].Qty'/><span class='field-validation-valid' data-valmsg-for='productitems[0].Qty' data-valmsg-replace='true'></span></td></tr>");
$("#productItem").append(productItem);
$("#rmlist-0").addItems(data);
});
});
Now the validation attributes applied on Name and Price are working fine but not on the fields added dynamically (i.e. "RawMaterial" and "Qty").
Please give me the suggestions how this validation will work ?
Note: For testing purpose I have just added the first object of the List indexed with 0.
There are several ways to accomplish this -
PARTIAL VIEW: Since you are using Server Side data annotation as I see from the class definitions, then it is not a good idea to load dynamically with js. Because you will miss out all the validation that MVC 4 could have created automatically. So, the best solution I would suggest is taking the code that you are adding dynamically to a partial view file and then get the html with ajax call and then populating the HTML.
JS VALIDATION: But, if it is a must that you should use JS, then you have to add all the validation items yourself. To do that you have to do some extra works -
First, inspect the HTML with any developer tools, you will notice that there is a <span> attribute appended after each item to show the error which has a target mentioned. You have to append similar attributes to your elements
With MVC 4 unobtrusive validation, all the validation attributes and rules are added with the target element with data attributes. Each one is based one the validation they stands for. You have you create attributes similar to that.
Finally, after adding all the validation items in JS, reset the form so that it parses the new validations added and work accordingly. The code to parse the validations are here -
var form = $("form") //use more specific selector if you like
form.removeData("validator").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse(form);
But I would prefer the partial view solution, since it will require least amount of re-work and also gives you option to keep all your validation in one place. You don't have to worry about new validations to be ported to js in future.

What is the recommended way to do partial updates with PATCH in ServiceStack?

I am building a RESTful API using the ServiceStack framework. A lot of the resources that I need to update are quite big, with up to 40 attributes per class, so I would like to do partial updates instead of replacing the entire resource. Often the client will only need to update one or two attributes out of the 40, so I would like to just send a JSON body consisting of the few attributes.
Since all combinations of attributes are possible, it is not feasible to make an "Update" class per class as suggested here: https://github.com/ServiceStack/ServiceStack/wiki/New-Api#patch-request-example
In the Microsoft ASP.NET WebAPI OData package there is a Delta class that takes a subset of a class and updates the resource based on this subset (http://www.strathweb.com/2013/01/easy-asp-net-web-api-resource-updates-with-delta/). This is the functionality I would like to have, as I will be having quite a few classes so a generic method would be best.
Basically, if I have a class
public class MyClass {
public int a { get; set; }
public int b { get; set; }
...
public int z { get; set; }
}
I would like to update a resource of MyClass with a PATCH request with body
{"a":42,"c":42}
Is there a standard or recommended way to accomplish this with ServiceStack?
Declare any scalar values in your DTO as nullable. This will allow you to determine which fields were actually sent in the request:
public class MyClass {
public int? a { get; set; }
public int? b { get; set; }
public int? c { get; set; }
// etc.
// object-type properties are already nullable of course
public string MyString { get; set; }
}
Now if a client sends a partial request, like so:
{ "a": 1, "b": 0 }
You'll be able to determine which properties were actually sent when inspecting your DTO:
myClass.a == 1
myClass.b == 0
myClass.c == null
myClass.MyString == null
etc.
Set up a PATCH route for your DTO and implement a Patch method in your service:
public object Patch(MyClass request)
{
var existing = GetMyClassObjectFromDatabase();
existing.PopulateWithNonDefaultValues(request);
SaveToDatabase(existing);
...
}
PopulateWithNonDefaultValues is key here. It will copy values from your request object onto the database entity, but will only copy properties that are not the default values. Thus, if a value is null, it won't copy it, because the client didn't send a value for it. Notice that it will copy an integer value of zero though, because we made it a nullable int, and the default value for a nullable int is considered by this method to be null, not zero. Declaring your DTO properties as nullable shouldn't cause much of a hassle in the rest of your code.
Note that this approach works easily with JSON. If you need to support XML requests/responses, you may need need to do some additional work with DataContract/DataMember attributes to insure that nulls are handled correctly.
While esker's response is fine I would like to add that it might not be enough for nullable fields - since you don't know if the deserializer or the user have created that null field.
One approach would be to peek at the raw request.
A different approach is to ask the user to provide additional request (querystring) parameter to clearly specify which fields to patch.
Something like: patch_fields=name,description,field3
The bonus of that approach is that the end user has more control over the patching and is not overriding a value by mistake (because he used the original entity and forgot to clear some fields)

EF4 and intentionally returning only *some* of the properties of an entity

Folks, I know I didn't phrase that title very well, but here's the scenario.
I have a WinForm UI tier, and a WCF middle tier, serving up my EF4 entity objects, which are (of course) mapped to my database tables. Everything works fine.
One of my objects is the Client - and in the Client db table are three varbinary(max) fields for PDF documents. So my entity object has three Byte() properties, one for each document.
But when I load up an initial grid listing the Clients, it's going to drag ALL that PDF data from the MT - making a much bigger payload than I generally need.
With DataSets, I'd write my SQL to not include the PDF binary - but I'd include a Boolean flag field for each to indicate whether there IS one to download if the user wants it. Then I'd load the PDFs via a separate call as needed.
With EF4 - what's the best pattern for this?
First, I'm thinking to put the documents into a child-table/child-objects, so I don't pull it across the tier with the Client. One problem solved.
Second, I suppose I could use partial classes to extend my Client entity object to have the three Boolean properties I want.
Am I on the right track?
I think you have three options:
1) Create a custom class which you project the properties you want into:
public class MySpecialSelection
{
public int ID { get; set; }
public string Name { get; set; }
// more
public bool HasPDFDoc1 { get; set; }
public bool HasPDFDoc2 { get; set; }
public bool HasPDFDoc3 { get; set; }
}
using (var context = new MyContext())
{
var mySpecialSelectionList = context.MyEntities.Where(...some predicate...)
.Select(e => new MySpecialSelection
{
ID = e.ID,
Name = e.Name,
// ...
HasPdfDoc1 = (e.PdfDoc1 != null),
HasPdfDoc2 = (e.PdfDoc2 != null),
HasPdfDoc3 = (e.PdfDoc3 != null),
}).ToList();
// ...
}
Instead of a "named" object you can also project into anonymous types.
Note: This doesn't attach any full model entity to the context, so you won't have any change tracking of entities.
2) Table splitting: It means that you split your single entity into two separate classes which are related by a navigation property. You can map then both entities to a single table in the database. It allows you to load the navigation properties (for instance the binary fields) on request (by lazy, eager or explicite loading). Details about this for EF4.0 are here and for EF4.1 here.
3) Your own proposal: Create separate tables and separate entities which are linked by navigation properties and FK constraints.