Using model's property for requiredif statement without putting it in an actual DOM - expressiveannotations

I need to validate input field based on a property that is within the same model, however is not being used in the DOM. Something like so:
[RequiredIf("NameRequired", ErrorMessage = "Name is required")]
public string Name {get;set;}
public bool NameRequired {get;set} = true;
DOM:
#HTML.TextBoxFor(model=>model.Name);
NameRequired by default is true however it can be changed in other places of the code, which is why it is needed, however when running this code expressiveannotations complains that NameRequired field is not found in the DOM

Put it in the DOM as a hidden field (using #Html.HiddenFor() )

Related

How does cascaded parameter Task<AthenticationState> get unwrapped and exposed as "context" in AuthorizeView and AuthorizedRouteView in Blazor WASM?

There is a an object of type AuthenticationState named "context" that is available inside AuthorizeView and AuthorizedRouteView components. This object allows to access the ClaimsPrincipal via context.User.
I can see in the source code that AuthenticationState is passed down to these components as Task by the CascadingValue component implemented in the CascadingAuthenticationState component (which in turn is defined at the top of the component hierarchy in App.razor).
However, when I inspect the source of the AuthorizeRouteView I can see the cascading parameter of type Task named ExistingCascadedAuthenticationState. Yet, it is a complete mystery to me how and where does the Task gets unwrapped and exposed as "context". Does anyone know the answer?
You need to dig deep, and it's a little complicated.
AuthorizeView inherits from AuthorizeViewCore
AuthorizedRouteView builds it's own AuthorizeRouteViewCore inheriting from AuthorizeViewCore.
Code at the bottom of AuthorizedRouteView:
private sealed class AuthorizeRouteViewCore : AuthorizeViewCore
{
[Parameter]
public RouteData RouteData { get; set; } = default!;
protected override IAuthorizeData[]? GetAuthorizeData()
=> AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}
AuthorizedRouteView captures any cascade into ExistingCascadedAuthenticationState. If one exists (not null) then CascadingAuthenticationState exists in App, so nothing more needs doing. If it's null then it adds CascadingAuthenticationState as the component root component into its render fragment. This guarantees that Task<AuthenticationState> is cascaded.
AuthorizeViewCore captures the cascaded value:
[CascadingParameter] private Task<AuthenticationState>? AuthenticationState { get; set; }
It gets "unwrapped" in OnParametersSetAsync
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
and used in BuildRenderTree to the "context" you see.
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));
The content comes from:
RenderFragment<TValue>
declared as follows where TValue- content - is declared as AuthenticationState :
[Parameter] public RenderFragment<AuthenticationState>? Authorized { get; set; }
The comment of enet helped me to find the answer.
When we have a RenderFragment<TValue> delegate, the <TValue> is exposed by default as #context.
For example, in AuthorizeRouteView we have a parameter NotAuthorized:
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
In this case AuthenticationState is TValue, therefore AuthenticationState is exposed as #context.
This article on Blazor University was the key for me to get the concept: Passing placeholders to RenderFragments.
Edit 2022-05-29
The recently added answer of #somedotnetguy makes it even more clear how render templates work. I suggest also reading his answer to get a more complete picture.
I was wondering the exact same thing:
How come, that suddenly inside the inner markup of a component (between its opening and closing tag, when its consumed in a parent) we can write #context and where does the value come from?
The provided answers helped me to figure it out and there is good explanaition on Blazor University - RenderFragements (entire chapter, this page and the 4 following)
As seen in Official Docs here and here the AuthorizeView has a property ChildContent of type RenderFragment<AuthenticationState> decorated with Parameter.
[Microsoft.AspNetCore.Components.Parameter]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>?
ChildContent { get; set; }
Here I go:
You cannot do this with a plain (empty, newly created) component:
//Parent.razor consumes MyComp
<MyComp>Some markup as content for MyComp</MyComp>
The FRAMEWORK CONVENTION is, you need the following code inside the component (class) definition, to enable the possibility to write markup between the tags (when it is consumed). The markup then gets passed as the ChildComponent property of type RenderFragment:
//MyComp.razor
[Parameter]
public RenderFragment ChildContent { get; set; }
You can also use properties/fields of the type RenderFragment or RenderFragment with other property-names, but they won't get automatically filled from the parent like above. However you can use them inside your component definition as you please. To set the content in the consuming parents of any RenderFragment you use its property name like an html tag and write the razor content inside it. The exception here is, if you ONLY want to fill the ChildContent and don't use other RenderFragments, it can be omitted like above (since this is a special name).
//MyComp.razor
<p>Fragment1:</p>
#SomeOtherRenderFragment
<p>Fragment2:</p>
#SomeGenericRenderFragment(_instanceT)
#code {
[Parameter]
public RenderFragment SomeOtherRenderFragment{ get; set; } =
#<h1>My Render Fragment Example</h1>;
[Parameter]
public RenderFragment<T> SomeGenericRenderFragment{ get; set; } // not set here in this example
private T _instanceT = null // T must be an explicit type, it's a placeholder in this example... for instance change it to 'string'. You can get the instance from where ever you want, probably through some service that you inject with DI
}
//Parent.razor consumes MyComp
// Implicit setting ChildContent
<MyComp>Some markup as content for MyComp</MyComp>
// Explicit setting ChildContent
<MyComp><ChildContent>Some markup as content for MyComp</ChildContent></MyComp>
// Explicit setting various RenderFragments
<MyComp>
<ChildContent>Some markup as content for MyComp</ChildContent>
<SomeOtherRenderFragment>SomeContent</SomeOtherRenderFragment>
<SomeGenericRenderFragment>SomeContent with #context</SomeGenericRenderFragment>
</MyComp>
And now putting it all together. You can also use the generic RenderFragment as type for the convention-property ChildContent. However, it is your responsibility to provide an instance of the generic class inside the component definition.
//MyComp.razor
<p>Content passed from parent:</p>
#ChildContent(_instanceT1)
<p>Content passed from parent a second time:</p>
#ChildContent(_instanceT2)
#code {
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
private string _instanceT1 = "Hello World!";
private string _instanceT2 = "Good Night!";
}
Parent:
//Parent.razor
<h1>Parent:</h1>
<MyComp>
<p>I can type content here and now comes the value of T: #context</p>
<p>and again again: #context</p>
</MyComp>
Final Html will look sth like this:
<h1>Parent:</h1>
<p>Content passed from parent:</p>
<p>I can type content here and now comes the value of T: Hello World!</p>
<p>and again again: Hello World!</p>
<p>Content passed from parent a second time:</p>
<p>I can type content here and now comes the value of T: Good Night!</p>
<p>and again again: Good Night!</p>
Note: #context is ONLY available inside a component's tag (when consumed), if it has this generic RenderFragment property in its definition:
//MyComp.razor
[Parameter]
public RenderFragment<T> ChildContent{ get; set; }
AuthorizeView can use RenderFragments:
ChildContent (most often used implicit, also explicit possible)
Authorized (explicit)
NotAuthorized (explicit)
However it is implemented in such a way, that an exception is thrown if both are specified: Unhandled exception rendering component: Do not specify both 'Authorized' and 'ChildContent'. Basically Authorized substitutes ChildContent, or in other words, when not setting any RenderFragment explicitly, the ChildContent gets treated like Authorized, like shown in the answer by MrC aka Shaun Curtis.
Final Words: I hope this helps and I hope I kept typos to a minimum :)
The cascading parameter is Task<AuthenticationState> context
Which tells you that context, when awaited, will return the object of type AuthenticationState. So what you get is a Task. The task when awaited, returns the value returned by the Task. The actual syntax for accessing the User is
var state = await context;
var user = state.User;
Also, you can give any name to the cascading parameter. So
[CascadingParameter]
public Task<AuthenticationState> AuthState {get;set;}
var state = await AuthState;
var user = state.User;
is equally valid.

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);

Should I have both text and value in my model for a property that is selected from dropdownlist

In ASP.NET MVC application I have a model named CarSearchCriteria:
public class CarSearchCriteria{
public int CarMake {get;set;} // This is selected from a dropdownlist
public int YearOfFirstReg {get;set;}
public string ModelVariant {get;set}
}
I have two views - one for editing and the other one for viewing. In the editing view for the CarMake property I can do the following. I know I could have used DropDownListFor but didn't want to mess with SelectList for the time being:
<select name="CarMake">
<option value="1">BMW</option>
<option value="2">Mercedes</option>
<option value="3">Toyota</option>
</select>
So the model binding mechanism will easily bind the selected value to the appropriate model property. But what about the reading mode. I can't show 1s or 2s. I need to show BMW, Mercedes and so on. My question is what is the preferred way, do I have to have a property name that holds the actual textual information, something like CarMakeText?
You could have both the identifier (which you currently have) as well as the Make object itself. The latter would never need to be accessed when building the model, but can be accessed when reading the model. A lazy-loaded read-only property often works well for that. Something like this:
public int CarMakeID { get; set; }
public Make CarMake
{
get
{
if (CarMakeID == default(int))
return null;
// fetch the Make from data and return it
}
}
Naturally, this depends a lot on what a Make actually is and where you get it. If there's just some in-memory list somewhere then that should work fine. If fetching an instance of a Make is a little more of an operation (say, fetching from a database) then maybe some in-object caching would be in order in case you need to access it more than once:
public int CarMakeID { get; set; }
private Make _carMake;
public Make CarMake
{
get
{
if (CarMakeID == default(int))
return null;
if (_carMake == null)
// fetch the Make from data and save it to _carMake
return _carMake;
}
}
David's solution is just fine but for some reason I find my own solution to better fit my needs and besides that I find it more elegant. So basically what I do is I create a class that holds the textual descriptions of all the properties that keep just ID. For example, I have the following model:
public class EmployeeModel{
public int EmployeeID {get;set;}
public string FullName {get;set}
*public int DepartmentID {get;set}
*public int SpecialityID {get;set;}
public int Age {get;set;}
}
The properties marked with asterisk are the properties that keep ids of possible many predefined options and when showing we're supposed to show the actual descriptions, not the number representations. So for this purpose, we create a separate class:
public class EmployeeTextValues{
public string DepartmentName {get;set;}
public string SpecialityName {get;set;}
}
And then I just add this class as a property to my model:
public EmployeeTextValues TextValues {get;set;}
After that, it's quite easy to access it from anywhere, including Razor.
P.S. I'm sure that a lot of people will tend to do the following before initializing this property:
Employee emp=new Employee;
emp.Age=25;
emp.TextValues.DepartmentName="Engineering";// Don't do this
If you try to access or set Textvalues.Someproperty you'll get Object reference not set to an instance of an object. So do not forget to set TextValues first to some initialized object. Just a kind reminder, that's all.

How to hide a Model class field based on custom logic in MVC Web Api RC

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

BindingSource / BindingNavigator: How to prevent editing of bound DataSource?

I created a Data Source with VB.NET and Visual Studio 2005. I dragged the data source onto my dialog, and VS created the text boxes with the members of my linked Object, a System.Windows.Forms.BindingSource and a System.Windows.Forms.BindingNavigator.
I populate the List (called myList), set myList as the DataSource in the BindingSource, and things work peachy except for the fact that I want this to be read-only. If the user changes something in one of the text boxes, it saves the changes.
I tried creating a read-only collection to bind to the BindingSource, but that didn't solve the problem:
Dim detailsDlg As New dlgMyDetails
Dim readOnlyList As New ReadOnlyCollection(Of MyObjects)(myList)
detailsDlg.MyBindingSource.DataSource = readOnlyList
detailsDlg.ShowDialog()
I guess I could disable all of the text boxes, but that seems a bit heavy-handed, plus I'd probably want to change the font color so that it's easier to read.
Ideally, I probably wouldn't care if users were able to set focus to the text boxes, or even edit the contents, but I just wouldn't want any changes to persist. That is, if someone edited something, used the navigator to go to the next record, and then returned, I'd want it as it was before the user played with it.
Any suggestions / guidance?
Thanks in advance!
From a Model-View-Control perspective, the constraint you want is not on the model or control, but the view. The view should restrict what is editable on the screen.
If it truly is read-only, why not go with a read-only user interface element, ie, a label? The reason you do this is to reduce confusion to the user. If it is a textbox, there is a reasonable expectation that at some point the data becomes editable. If this is not the case, then presenting a disabled textbox is likely not the right UI element to present, rather, as mentioned, a label.
Instead of making a ReadOnlyCollection you can change the property in your class (MyObjects) to ReadOnly or add attribute System.ComponentModel.ReadOnly(true) to your property, example:
class Person
{
public Person(int id, string name, string address)
{
_id = id;
Name = name;
Address = address;
}
private int _id = 0;
public int ID { get { return _id; } }
[System.ComponentModel.ReadOnly(true)]
public string Name { get; set; }
public string Address { get; set; }
}
ID and Name is going to be readonly, sorry if the example is in C#. Hope this helps.
Cheers.