Validate Property as [Required], but don't validate the value itself. Is it possible with Attributes and conventions in .net core 2 or higher? - asp.net-core

public class Post {
// ... other properties here
public Author { get; set; }
}
public class Author {
// ... other properties here
public List<Post> Posts { get; set; }
}
This is an overly-simplistic example. What I basically want is that the Author property in the Post class is never null, it MUST have a value. But because I'm pulling Author from a database, I am 100% sure that it's valid, so I want to skip the validation of the value of the Author property's value
In .net core 3 preview 9, recursive validation is causing unnecessarily long hold-ups where join entities for many-to-many relationships are involved.
I assume one workaround is to use [ValidateNever] in conjunction with the Validate() method just to check that the Author is not null. But I don't know if one of them will override the other.
But I am ideally looking to achieve this with Attributes and/or conventions

You could write your custom validation attribute.When the value is null, it returns an Validation error.Otherwise, it returns ValidationResult.Success.
public class CustomRequiredAttribute :ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if(value != null)
{
return ValidationResult.Success;
}
else
{
var propertyName = validationContext.DisplayName;
var type = validationContext.ObjectType;
return new ValidationResult("Could not be null for " + propertyName);
}
}
}
Model:
public class Post {
// ... other properties here
[CustomRequired]
public Author { get; set; }
}

Related

ASPNET Core ActionResult property not serialize

I have this object
[DataContract]
public class FilterList<T> : List<T>
{
[DataMember]
public int Total { get; set; }
}
In my controller:
public ActionResult<FilterList<MyPOCO>> GetFilteredResult(string filter)
{
var l = new FilterList<MyPOCO>();
l.Total = 123456;
// Continue to add many MyPOCO objects into the list
return l;
}
I can get back the MyPOCO list at the client side, but the l.Total is NOT serialize. May I know what I had done wrongly?
Here is a workaround , you could try to use [JsonObject] attribute . But the items will not be serialized, because a JSON container can have properties, or items -- but not both. If you want both, you will need to add a synthetic list property to hold the items.
[JsonObject] will also cause base class properties such as Capacity to be serialized, which you likely do not want. To suppress base class properties, use MemberSerialization.OptIn. Thus your final class should look something like:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class FilterList<T> : List<T>
{
[JsonProperty]
public int Total { get; set; }
[JsonProperty]
List<T> Items
{
get
{
return this.ToList();
}
set
{
if (value != null)
this.AddRange(value);
}
}
}
Result:

Why model binding with a struct doesn't work?

I'm trying to understand why this binding doesn't work.
The binding didn't work until I changed the type from struct to class.
Is this by design or am I missing something?
I'm using asp.net core 2.2 MVC
View Models
Not working
public class SettingsUpdateModel
{
public DeviceSettingsStruct DeviceSettings { get; set; }
}
Working
public class SettingsUpdateModel
{
public DeviceSettingsClass DeviceSettings { get; set; }
}
public class DeviceSettingsClass
{
public bool OutOfScheduleAlert { get; set; }
// other fields removed for brevity
}
public struct DeviceSettingsStruct
{
public bool OutOfScheduleAlert { get; set; }
// other fields removed for brevity
}
Controller
[HttpPost]
public IActionResult Update(SettingsUpdateModel newSettings)
{
// newSettings.DeviceSettings.OutOfScheduleAlert always false on struct but correct on class
return Index(null);
}
View
<input class="form-check-input" type="checkbox" id="out_of_schedule_checkbox" asp-for="DeviceSettings.OutOfScheduleAlert">
Expected: DeviceSettings.OutOfScheduleAlert to bind to a struct the same as class
Actual: only the class parameter was binded
It is by design in complex type model bindings. A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory.
In ComplexTypeModelBinder.cs , the CanUpdateReadOnlyProperty method will mark the properties of value-type model as readonly due to value types have copy-by-value semantics, which prevents us from updating
internal static bool CanUpdatePropertyInternal(ModelMetadata propertyMetadata)
{
return !propertyMetadata.IsReadOnly || CanUpdateReadOnlyProperty(propertyMetadata.ModelType);
}
private static bool CanUpdateReadOnlyProperty(Type propertyType)
{
// Value types have copy-by-value semantics, which prevents us from updating
// properties that are marked readonly.
if (propertyType.GetTypeInfo().IsValueType)
{
return false;
}
// Arrays are strange beasts since their contents are mutable but their sizes aren't.
// Therefore we shouldn't even try to update these. Further reading:
// http://blogs.msdn.com/ericlippert/archive/2008/09/22/arrays-considered-somewhat-harmful.aspx
if (propertyType.IsArray)
{
return false;
}
// Special-case known immutable reference types
if (propertyType == typeof(string))
{
return false;
}
return true;
}
Reference here for more details .
BTY ,if you want to bind struct type model , you could try to send the json data by using ajax request from the view.

Can you access the metadata of other properties for ASP.NET Core 2.1 MVC client side validation?

I am looking at implementing some pretty simple client side validation by implementing the IClientModelValidator interface. Specifically I am creating a NotEqualTo (and later an EqualTo) validation attribute that will compare the value of one input to another.
To provide a nice UX I want to use the display name of both inputs in the error messages: "Password cannot be the same as Email" for example.
This is has obviously been done a million times and there are plenty of example around, but they are either for previous versions of MVC or are not using the display name of the other property.
Below is what I have so far. I have managed to grab the display name via the Display attribute in the server side IsValid(...) method, but I can't work out how to do similar for the client side AddValidation(...) method.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NotEqualToAttribute : ValidationAttribute, IClientModelValidator
{
private const string defaultErrorMessage = "{0} cannot be the same as {1}.";
public string OtherProperty { get; private set; }
public NotEqualToAttribute(string otherProperty) : base(defaultErrorMessage)
{
this.OtherProperty = otherProperty;
}
public override string FormatErrorMessage(string name)
{
string.Format(base.ErrorMessageString, name, this.OtherProperty);
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-notequalto", this.FormatErrorMessage(context.ModelMetadata.GetDisplayName());
context.Attributes.Add("data-val-notequalto-otherproperty", this.otherProperty);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return ValidationResult.Success;
PropertyInfo otherProperty = validationContext.ObjectInstance.GetType().GetProperty(this.OtherProperty);
object otherValue = otherProperty.GetValue(validationContext.ObjectInstance, null);
if (!value.Equals(otherValue))
return ValidationResult.Success;
DisplayAttribute display = otherProperty.GetCustomAttribute<DisplayAttribute>();
string otherName = display?.GetName() ?? this.OtherProperty;
return new ValidationResult(string.Format(defaultErrorMessage, validationContext.DisplayName, otherName));
}
}
Typically I solved this myself after taking a break, just going to leave this here in case it helps someone else (or there is a better solution):
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
string otherName =
context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this.OtherProperty)
.GetDisplayName();
context.Attributes.Add("data-val-notequalto",
string.Format(defaultErrorMessage, context.ModelMetadata.GetDisplayName(), otherName)
);
}
You can get to the meta data for the other properties via ClientModelValidationContext.ModelMetadata.ContainerMetadata.Properties

WEBAPI ActionContext Request.Properties.Add for store sensitive information

I would like to pass information from the action filter (database) to the Action function.
Is it secure to use ActionContext Request.Properties.Add to store the data?
is there any chance that the information will be seen by the WEBAPI client or its safe as it safe to store information in the Cache\Session?
Is it a better way to do it?
The client will not see request properties unless you explicitly serialize them. They completely remain on the server side.
To answer your followup question here are two other ways to do it. There is no "Best" way per se. It all depends on how far you want the information to flow, and how generic you want your filter to be. My personal preference is using the controller object, but again it is just a preference.
For the sample here is a simple values controller and a POCO class:
[MyActionfilter]
public class ValuesController : ApiController
{
public string Foo { get; set; }
public User Get(User user)
{
if (Foo != null && user != null)
{
user.FamilyName = Foo;
}
return user;
}
}
public class User
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
}
The action filter below is naively implementing access to the controller object or the method parameters. Note that it's up to you to either apply the filter sparingly or do type checks/dictionary checks.
public class MyActionfilter : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
controller = actionContext.ControllerContext.Controller;
// Not safe unless applied only to controllers deriving
// from ValuesController
((ValuesController)controller).Foo = "From filter";
// Not safe unless you know the user is on the signature
// of the action method.
actionContext.ActionArguments["user"] = new User()
{
FirstName = "From filter"
};
}
}

NHibernate second level cache not working when using composite primary key?

What's the proper way to use a composite primary key in NHibernate so it will be amenable for caching?
I isolate the composite primary key similar to the last part of this post: http://devlicio.us/blogs/anne_epstein/archive/2009/11/20/nhibernate-and-composite-keys.aspx
But the second level cache isn't caching it.
If for surrogate key, it is caching, e.g.
var q = from p in session.Query<Product>()
select p;
q.Cacheable().ToList(); // hit database
// this doesn't hit the database, Get get its value from cache(via last query)
var px = secondSession.Get<Product>(1);
But when using composite primary key, the Get doesn't get its value from the cache:
var q = from pl in session.Query<ProductLanguage>()
select pl;
q.Cacheable().ToList(); // hit the database
// this hits the database, Get didn't get its value from cache(via last query)
var plx = secondSession.Get<ProductLanguage>(
new ProductLanguageCompositeKey { ProductId = 1, LanguageCode = "en" });
Is composite key(ProductLanguageCompositeKey here), even isolated it its own class(with Serializable attribute, Equals and GetHashCode) doesn't get cached?
How can we make an entity accessed via composite key cacheable?
For those suspecting if NHibernate's second level cache is not working when using composite primary key (caching works), check if your composite primary key's values are in pristine form. Solution to my caching problem:
SQL Server conversion fidelity from nvarchar to varbinary, then from varbinary to nvarchar
For caching a unique cache key is generated for ProductLanguage. This cache key is built from the composite key, which depends on the Product entity's hash code. If you use cross-session queries, NHibernate might return proxied or unproxied versions of Product, which will cause different hash codes and cause the cache lookup to miss the cached ProductLanguage entity.
The solution is to override the Equals and GetHashCode methods in order to return consistent values. The simplest way is to inherit the popular EntityBase class for all entities that have a surrogate Id key.
public abstract class EntityBase<T>
where T : EntityBase<T>
{
public virtual int Id { get; protected set; }
protected bool IsTransient { get { return Id == 0; } }
public override bool Equals(object obj)
{
return EntityEquals(obj as EntityBase<T>);
}
protected bool EntityEquals(EntityBase<T> other)
{
if (other == null)
{
return false;
}
// One entity is transient and the other is not.
else if (IsTransient ^ other.IsTransient)
{
return false;
}
// Both entities are not saved.
else if (IsTransient && other.IsTransient)
{
return ReferenceEquals(this, other);
}
else
{
// Compare transient instances.
return Id == other.Id;
}
}
// The hash code is cached because a requirement of a hash code is that
// it does not change once calculated. For example, if this entity was
// added to a hashed collection when transient and then saved, we need
// the same hash code or else it could get lost because it would no
// longer live in the same bin.
private int? cachedHashCode;
public override int GetHashCode()
{
if (cachedHashCode.HasValue) return cachedHashCode.Value;
cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
return cachedHashCode.Value;
}
// Maintain equality operator semantics for entities.
public static bool operator ==(EntityBase<T> x, EntityBase<T> y)
{
// By default, == and Equals compares references. In order to
// maintain these semantics with entities, we need to compare by
// identity value. The Equals(x, y) override is used to guard
// against null values; it then calls EntityEquals().
return Object.Equals(x, y);
}
// Maintain inequality operator semantics for entities.
public static bool operator !=(EntityBase<T> x, EntityBase<T> y)
{
return !(x == y);
}
}
and implemented:
public class Blog : EntityBase<Blog>
{
public virtual string Name { get; set; }
// This would be configured to lazy-load.
public virtual IList<Post> Posts { get; protected set; }
public Blog()
{
Posts = new List<Post>();
}
public virtual Post AddPost(string title, string body)
{
var post = new Post() { Title = title, Body = body, Blog = this };
Posts.Add(post);
return post;
}
}
public class Post : EntityBase<Post>
{
public virtual string Title { get; set; }
public virtual string Body { get; set; }
public virtual Blog Blog { get; set; }
public virtual bool Remove()
{
return Blog.Posts.Remove(this);
}
}
void Main(string[] args)
{
var post = session.Load<Post>(postId);
// If we didn't override Equals, the comparisons for
// "Blog.Posts.Remove(this)" would all fail because of reference equality.
// We'd end up be comparing "this" typeof(Post) with a collection of
// typeof(PostProxy)!
post.Remove();
// If we *didn't* override Equals and *just* did
// "post.Blog.Posts.Remove(post)", it'd work because we'd be comparing
// typeof(PostProxy) with a collection of typeof(PostProxy) (reference
// equality would pass!).
}
More info at https://stackoverflow.com/a/20110265/179494