How to use different attributes in inherited objects in C# - c#

I am working on a web application where different user roles should have different security rights (view, edit). In order to implement this, I have a custom attribute [ReadOnlyAuthorize("Supervisor, Administrator, SysAdministrator")].
When a field is marked with this attribute and the user does not have one of these roles, my editor templates mark the field as readonly and disabled.
The problem is that by using the same model in create, edit and search operations, I get the same functionality and this is not what I want. This looks really bad but it is a temporary solution.
Is there a way to override these annotations besides by using new and defining new attributes?
I hope this description makes sense to you, these are parts of my classes:
[MetadataType(typeof(AssetMetadata))]
public partial class DTAsset
{
}
public class AssetMetadata
{
[ReadOnlyAuthorize("SysAdministrator")]
[DisplayName("Asset ID:")]
public string AssetID { get; set; }
}
public class AssetSearchViewModel : AssetMetadata
{
// removed [ReadOnlyAuthorize("SysAdministrator")]
[DisplayName("Asset ID:")]
public string AssetID { get; set; }
}

Related

How does custom model binding work for a property of an unknown type at compile time?

I have a UI designer on the front end which creates a layout.
A layout has rows, and each row has columns, and each column has widgets. The widgets are identified by a key and they also have a config.
public class Layout
{
[Required]
public IEnumerable<Row>? Rows { get; init; }
}
public record Row
{
[Required]
public IEnumerable<Column>? Columns { get; init; }
}
public record Column
{
[Required]
public IEnumerable<Widget>? Widgets { get; init; }
}
public record Widget
{
[Required]
public string? WidgetTypeKey { get; init; }
public object? Config { get; init; }
}
The config of a widget could be any number of C# classes that don't share anything in common. I don't know which one it will be until I determine the widget type by key. So therefore I have used object as the type.
But the config classes still have validation requirements such as [Required], [Range], [MaxLength] and so on.
I can resolve the config class at run time, but I'm not sure how to go about this so that I still get all of the usual ASP.NET validation through the pipeline.
At first I thought I could attach [BindModel] to the Config property or the Widget class and use my own IModelBinder, but these aren't used at all. ASP.NET only considers them if they're at the top of the hierarchy. I.e. the layout. So the model binder is never hit.
I also tried writing an IModelBinderProvider, but again the same problem. The provider is only hit for the initial Layout type, but nothing beyond that. It never queries again for any other type.
I also experimented with generics, thinking that maybe Config could be a TConfig type, but I have no idea how to resolve that at runtime during model binding. Especially since each widget can be a different type.
I guess I could write my own model binder for a layout, but then I miss out on all the automated validation don't I?
Is this too crazy to attempt? Has anyone ever successfully resolved a dynamic object at runtime with a deeply-nested complex type while also letting ASP.NET core do its full validation pipeline?
Your Layout class should implement IValidatableObject interface with Validator class:
public class Layout : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
...
Validator.TryValidate(...);
...
}
}
You should have the validation logic with System.ComponentModel namespace, ASP.NET Core will pick it up automatically.

Return only a subset of properties of an object from an API

Say I have a database in which I am storing user details of this structure:
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
}
I have a data access layer that works with this that contains methods such as GetById() and returns me a User object.
But then say I have an API which needs to return a users details, but not sensitive parts such as the PasswordHash. I can get the User from the database but then I need to strip out certain fields. What is the "correct" way to do this?
I've thought of a few ways to deal with this most of which involve splitting the User class into a BaseClass with non sensitive data and a derived class that contains the properties I would want kept secret, and then converting or mapping the object to the BaseClass before returning it, however this feels clunky and dirty.
It feels like this should be a relatively common scenario, so am I missing an easy way to handle it? I'm working with ASP.Net core and MongoDB specifically, but I guess this is more of a general question.
It seems for my purposes the neatest solution is something like this:
Split the User class into a base class and derived class, and add a constructor to copy the required fields:
public class User
{
public User() { }
public User(UserDetails user)
{
this.UserId = user.UserId;
this.Name = user.Name;
this.Email = user.Email;
}
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserDetails : User
{
public string PasswordHash { get; set; }
}
The data access class would return a UserDetails object which could then be converted before returning:
UserDetails userDetails = _dataAccess.GetUser();
User userToReturn = new User(userDetails);
Could also be done using AutoMapper as Daniel suggested instead of the constructor method. Don't love doing this hence why I asked the question but this seems to be the neatest solution and requires the least duplication.
There are two ways to do this:
Use the same class and only populate the properties that you want to send. The problem with this is that value types will have the default value (int properties will be sent as 0, when that may not be accurate).
Use a different class for the data you want to send to the client. This is basically what Daniel is getting at in the comments - you have a different model that is "viewed" by the client.
The second option is most common. If you're using Linq, you can map the values with Select():
users.Select(u => new UserModel { Name = u.Name, Email = u.Email });
A base type will not work the way you hope. If you cast a derived type to it's parent type and serialize it, it still serializes the properties of the derived type.
Take this for example:
public class UserBase {
public string Name { get; set; }
public string Email { get; set; }
}
public class User : UserBase {
public string UserId { get; set; }
public string PasswordHash { get; set; }
}
var user = new User() {
UserId = "Secret",
PasswordHash = "Secret",
Name = "Me",
Email = "something"
};
var serialized = JsonConvert.SerializeObject((UserBase) user);
Notice that cast while serializing. Even so, the result is:
{
"UserId": "Secret",
"PasswordHash": "Secret",
"Name": "Me",
"Email": "something"
}
It still serialized the properties from the User type even though it was casted to UserBase.
If you want ignore the property just add ignore annotation in you model like this, it will skip the property when model is serializing.
[JsonIgnore]
public string PasswordHash { get; set; }
if you want ignore at runtime(that means dynamically).there is build function avilable in Newtonsoft.Json
public class User
{
public string UserId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
//FYI ShouldSerialize_PROPERTY_NAME_HERE()
public bool ShouldSerializePasswordHash()
{
// use the condtion when it will be serlized
return (PasswordHash != this);
}
}
It is called "conditional property serialization" and the documentation can be found here. hope this helps
The problem is that you're viewing this wrong. An API, even if it's working directly with a particular database entity, is not dealing with entities. There's a separation of concerns issue at play here. Your API is dealing with a representation of your user entity. The entity class itself is a function of your database. It has stuff on it that only matters to the database, and importantly, stuff on it that does not matter to your API. Trying to have one class that can satisfy multiple different applications is folly, and will only lead to brittle code with nested dependencies.
More to the point, how are you going to interact with this API? Namely, if your API exposes your User entity directly, then any code that consumes this API either must take a dependency on your data layer so it can access User or it must implement its own class representing a User and hope that it matches up with what the API actually wants.
Now imagine the alternative. You create a "common" class library that will be shared between your API and any client. In that library, you define something like UserResource. Your API binds to/from UserResource only, and maps that back and forth to User. Now, you have completely segregated your data layer. Clients only know about UserResource and the only thing that touches your data layer is your API. And, of course, now you can limit what information on User is exposed to clients of your API, simply by how you build UserResource. Better still, if your application needs should change, User can change without spiraling out as an API conflict for each consuming client. You simply fixup your API, and clients go on unawares. If you do need to make a breaking change, you can do something like create a UserResource2 class, along with a new version of your API. You cannot create a User2 without causing a whole new table to be created, which would then spiral out into conflicts in Identity.
Long and short, the right way to go with APIs is to always use a separate DTO class, or even multiple DTO classes. An API should never consume an entity class directly, or you're in for nothing but pain down the line.

How do I 'inherit' a class properties to use with Entity Framework

I want to add a new property on my class, make it strongly typed so I can use it in my views and controllers, I've tried to inherit the properties, but Entity Framework or C# throws me errors...
I have this class:
public class Patient
{
public int Id { get; set; }
public string Message { get; set; }
public string FirstName { get; set; }
.....
}
which has a lot more properties in it, but shortened here.
I have a razor view, which is uses 'Patient' as it's model
using model Project.Models.Patient
So I had completed my view (or so I thought) and was asked to add functionality in the view. The functionality is to send a POST using a form of a 'Message' (a simple textarea in html). I've already got all the details I want, but this new 'Message'
So I thought, because I don't want this field in the database I could add it like this:
public class Patient
{
public int Id { get; set; }
public string Message { get; set; }
public string FirstName { get; set; }
[NotMapped]
public string Message { get; set; }
.....
}
But I'm not a fan of this, it doesn't relate to the Patient in any other way.
So I thought I could change my model in razor to something like this:
#model Project.Models.DTOs.PatientMessage
and inherit the Patient class and all it's properties (so I don't have to retype and copy past the fields again) and the new PatientMessage class would look like this:
public class PatientMessage : Patient
{
public string Message { get; set; }
}
But when I refresh my application, I receive a message stating the Application Database Context has changed, and I have to update this. I don't want to update my database, and I can't really see why I need to, it's an extra field which I don't want to include in my database.
So then I decided to make this class an 'abstract' class
public abstract class PatientMessage : Patient
{
public string Message { get; set; }
}
When I refreshed my page this time, I saw no need to update the Database, great I thought, and when I went near a page where the model was
#model Project.Models.Patient
I received this message
The abstract type 'Project.Models.DTOs.PatientMessage' has no mapped descendants and so cannot be mapped. Either remove 'Project.Models.DTOs.PatientMessage' from the model or add one or more types deriving from 'Project.Models.DTOs.PatientMessage' to the model.
MY QUESTION
Can I include this one field, without placing it on the Patient class, ideally without having to update models in my razor views, or would I have to change the models in the views and controllers and update the information to include the message and map all the details from a 'PatientMessage' to a 'Patient'
Please let me know if you need any further information.
Regards

How to overwrite the implementation of a property created automatically in a partial class (T4 EF)?

I need to add some logic when setting property DateCreated.
Class is partial and created automatically by a T4 template.
public partial class GaAnalytic
{
public int AnalyticId { get; set; }
public System.DateTime DateCreated { get; set; }
}
I would like to know if is possible overwrite DateCreated implementation, or other possible solutions.
You simply cannot override in strict sense of the C# keyword a property in the same class. But there are workarounds.
Just until recently EF did not support enumeration properties so I needed to map those as integers and expose to our application as enumeration so basically I did kind of 'override' the property by introducing another that converted from int to enum and back.
For that unfortunately you need second name for the same value
//in generated partial
public DateTime DateCreatedDB { get; set; }
//in second file that contains your partial of that class
public DateTime DateCreated
{
get { // return converted DateCreatedDB }
set { // set DateCreatedDB to unconveted value }
}
From database and EF point of view there is a DateCreatedDB column that is mapped and processed. On the other hand you in your application use DateCreated.
If you use interfaces for your data model then it is a little bit simpler as you can provide explicit implementation for your interface property that does the conversion.
The other thing that you can do is to modify T4 template it is a bit of hassle to do it right as tools are not given out of the box, but it is doable and code would be cleaner.
You can overwrite using new keyword.
public class GaAnalyticEx : GaAnalytic
{
public new System.DateTime DateCreated { get; set; }
}

Is there an attribute that I can use with ASP.NET MVC 3 to prevent model fields from being automatically included in my views?

Is there an attribute that I can use with ASP.NET MVC 3 to prevent model fields from automatically showing up in my view? What I mean by this is that I have classes like the following:
public class EntityBase
{
public int ID { get; set; }
//more fields...
}
public class TestEntity : EntityBase
{
public string TestEntityName { get; set; }
//more fields...
}
I know about all of the attributes in System.ComponentModel and System.ComponentModel.DataAnnotations to enforce validation - Required, StringLength, etc. - but is there one I can use that will prevent certain fields from showing up in the view when I create it from Visual Studio? All of my project's model classes inherit from EntityBase, but I don't want any of EntityBase's fields to be visible on the view. I'm using Razor as my ViewEngine, in case it matters.
TIA,
Benjy
Use ScaffoldColumn:
[ScaffoldColumn(false)]

Categories

Resources