Customizing model metadata data annotations - asp.net-mvc

Is there any way to add a custom data annotation for metadata? I find than [DefaultValue] doesn't work
namespace PROJECT.Common.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class MyDefaultValueAttribute : Attribute, IMetadataAware
{
public string DefaultValue;
private dynamic _DefaultValue;
public MyDefaultValueAttribute(string m_value_tx)
{
_DefaultValue = m_value_tx;
}
public MyDefaultValueAttribute(bool m_default_yn)
{
_DefaultValue = m_default_yn;
}
public MyDefaultValueAttribute(Int32 m_default_no)
{
_DefaultValue = m_default_no;
}
public MyDefaultValueAttribute(DateTime m_default_dt)
{
_DefaultValue = m_default_dt;
}
public MyDefaultValueAttribute(decimal m_defaul_tx)
{
_DefaultValue = m_defaul_tx;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["DefaultValue"] = _DefaultValue;
}
}
and on i created a model binder but the problem is, the data annotation that i make only works for string..
i am passing dynamic on my constructor...
any help is appreciated... I clearly want custom annotations
THANKS

Related

Polymorphism with swagger not working as expected

I am using springfox version 2.9.2 and swagger annotations 1.5.x. The ApiModel annotations support the discriminator, subTypes and parent attribute which are required to make polymorphism work but I am not seeing the correct apidocs generated to enable polymorphism.
Here is my annotated code.
#RestController
#RequestMapping("/api/vehicles")
public class VehicleController {
private static final Logger LOGGER = LoggerFactory.getLogger(VehicleController.class);
#PostMapping(consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})
void post(#RequestBody Vehicle anyVehicle) {
LOGGER.info("Vehicle : {}", anyVehicle);
}
}
#ApiModel(discriminator = "type", subTypes = {Car.class, Bike.class})
public class Vehicle {
String brand;
String type;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
#ApiModel(parent = Vehicle.class)
public class Car extends Vehicle {
int noOfDoors;
boolean powerWindows;
public int getNoOfDoors() {
return noOfDoors;
}
public void setNoOfDoors(int noOfDoors) {
this.noOfDoors = noOfDoors;
}
public boolean isPowerWindows() {
return powerWindows;
}
public void setPowerWindows(boolean powerWindows) {
this.powerWindows = powerWindows;
}
}
#ApiModel(parent = Vehicle.class)
public class Bike extends Vehicle {
boolean pillion;
public boolean isPillion() {
return pillion;
}
public void setPillion(boolean pillion) {
this.pillion = pillion;
}
}
When the docs get generated is basically shows one endpoint which handles a POST request and takes in a Vehicle as the model.
Is what I am doing here supposed to work? Can someone point me to a working example of this with SpringFox that I can look at?
Support for discriminator is not available in Swagger UI yet. You can follow these issues for status updates:
Discriminator does not switch schema
subTypes not displayed in model

Castle.Windsor - How to implement TypedFactoryFacility

Recently, I developed a component , using factory pattern. However, I did a research. on how to improve it using TypedFactoryFacility, since we are using Castle.WIndsor.
Can you please provide a simple complete example? I have read few of them but still can't really fully understand . SO far, my code looks like that :
public class DynamoStoreService : IDynamoStoreService
{
private IDynamoStoreFactory _dynamoStoreFactory;
public DynamoStoreService(IDynamoStoreFactory dynamoStoreFactory)
{
_dynamoStoreFactory=dynamoStoreFactory;
}
public IDynamoStore GetProductDataDynamoStore(string storageAccount)
{
return _dynamoStoreFactory.Create(storageAccount);
}
}
public class DynamoStoreFactory : IDynamoStoreFactory
{
private IStorageAccountSelector _storageAccountSelector;
public DynamoStoreFactory(IStorageAccountSelector storageAccountSelector)
{
_storageAccountSelector = storageAccountSelector;
}
public IDynamoStore Create(string storageAccount)
{
return new AzureKeyValueStore(_storageAccountSelector.GetCredentials(storageAccount).StorageAccount, "pointerfiles");
}
}
public class StorageAccountSelector : IStorageAccountSelector
{
private readonly IConfigurationSettings _settings;
public StorageAccountSelector(IConfigurationSettings settings)
{
_settings = settings;
}
BlobCredentials IStorageAccountSelector.GetCredentials(string storageAccount)
{
return new BlobCredentials()
{
Container = string.Empty,
StorageAccount = GetStorageAccount(storageAccount)
};
}
private string GetStorageAccount(string storageAccount)
{
switch (storageAccount)
{
case "CustomerPolarisingCategoryBlobStorageAccountKey":
return _settings.CustomerPolarisingCategoryBlobStorageAccount;
case "CustomerPolarisingSegmentBlobStorageAccountKey":
return _settings.CustomerPolarisingSegmentBlobStorageAccount;
case "P2ProductSimilarityBlobStorageAccountKey":
return _settings.P2ProductSimilarityBlobStorageAccount;
case "ProductPolarisingCategoryBlobStorageAccountKey":
return _settings.ProductPolarisingCategoryBlobStorageAccount;
case "ProductPolarisingSegmentBlobStorageAccountKey":
return _settings.ProductPolarisingSegmentBlobStorageAccount;
case "SignalBlobStorageAccountKey":
return _settings.SignalBlobStorageAccount;
}
return string.Empty;
}
}
}
So basically, the IDynamostore , whenvever called, we need to be able to pass a different connection string. I have figured out the above design.. could this be improved using TypedFactoryFacility?
Thanks
Maybe the code below can give you an idea about how to use the TypedFactoryFacility. If you have studied it and have questions about it, please let me know.
Kind regards,
Marwijn.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
namespace ConsoleApplication3
{
public class TypedFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
private readonly StorageAccountSelector _storageAccountSelector;
public TypedFactoryComponentSelector(StorageAccountSelector storageAccountSelector)
{
_storageAccountSelector = storageAccountSelector;
}
protected override System.Collections.IDictionary GetArguments(MethodInfo method, object[] arguments)
{
var dictionary = new Dictionary<string, object>();
dictionary.Add("mappedStorageAccount", _storageAccountSelector.GetCredentials((string)arguments[0]).StorageAccount);
dictionary.Add("files", "pointerfiles");
return dictionary;
}
}
public interface IDynamoStore
{
}
public class AzureKeyValueStore : IDynamoStore
{
public AzureKeyValueStore(string mappedStorageAccount, string files)
{
Console.WriteLine(mappedStorageAccount);
Console.WriteLine(files);
}
}
public class BlobCredentials
{
public string Container { get; set; }
public string StorageAccount { get; set; }
}
public interface IDynamoStoreFactory
{
IDynamoStore Create(string storageAccount);
}
public class StorageAccountSelector
{
public BlobCredentials GetCredentials(string storageAccount)
{
return new BlobCredentials()
{
Container = string.Empty,
StorageAccount = GetStorageAccount(storageAccount)
};
}
public string GetStorageAccount(string storageAccount)
{
return storageAccount + "Mapped";
return string.Empty;
}
}
class Program
{
static void Main(string[] args)
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IDynamoStoreFactory>().AsFactory(new TypedFactoryComponentSelector(new StorageAccountSelector())),
Component.For<IDynamoStore>().ImplementedBy<AzureKeyValueStore>()
);
var factory = container.Resolve<IDynamoStoreFactory>();
factory.Create("storageAccount");
}
}
}

Intranet Application - Data Access in separate project

I have an MVC intranet application which uses EF 6. I have setup the DataAccess project in a separate class library which has EF 6 referenced. I have an entity which implements an interface:
public interface IAuditable
{
DateTime CreatedDateTime { get; set; }
string CreatedBy { get; set; }
}
public class Collection : IAuditable
{
// Properties
}
However, in the SaveChanges method I obviously don't have access to HttpContext.Current.User.Identity.Name as it is in a separate class library, so I was wondering how one would set this in SaveChanges?
public override int SaveChanges()
{
var addedEntries = ChangeTracker.Entries().Where(x => x.State == EntityState.Added);
foreach (var dbEntityEntry in addedEntries)
{
var entity = dbEntityEntry.Entity as IAuditable;
if (entity != null)
{
entity.CreatedDateTime = DateTime.Now;
// how do I set entity.CreatedBy = HttpContext.Current.User.Identity.Name?
}
}
return base.SaveChanges();
}
Edit
Following on from #CodeCaster solution, I have the following:
[BreezeController]
public class BreezeController : ApiController
{
private readonly BTNIntranetRepository _repository;
public BreezeController(BTNIntranetRepository repository)
{
_repository = repository;
_repository.LoggedInUser = HttpContext.Current.User.Identity.Name;
}
// Methods
}
But HttpContext.Current.User is null
This can be solved in many ways.
You're not really showing relevant code, but you can for example give the library class you expose a public string LoggedInUser (or ActingUser or give it a name) property which you set when instantiating it:
public class SomeController : Controller
{
private IDataSource _dataSource;
public SomeController(IDataSource dataSource)
{
_dataSource = dataSource;
_dataSource.LoggedInUser = HttpContext.Current.User.Identity.Name
}
}
You can then simply use that property in your IDataSource.SaveChanges() method:
public override int SaveChanges()
{
// ...
entity.CreatedBy = this.LoggedInUser;
}

Get custom attribute for parameter when model binding

I've seen a lot of similar posts on this, but haven't found the answer specific to controller parameters.
I've written a custom attribute called AliasAttribute that allows me to define aliases for parameters during model binding. So for example if I have: public JsonResult EmailCheck(string email) on the server and I want the email parameter to be bound to fields named PrimaryEmail or SomeCrazyEmail I can "map" this using the aliasattribute like this: public JsonResult EmailCheck([Alias(Suffix = "Email")]string email).
The problem: In my custom model binder I can't get a hold of the AliasAttribute class applied to the email parameter. It always returns null.
I've seen what the DefaultModelBinder class is doing to get the BindAttribute in reflector and its the same but doesn't work for me.
Question: How do I get this attribute during binding?
AliasModelBinder:
public class AliasModelBinder : DefaultModelBinder
{
public static ICustomTypeDescriptor GetTypeDescriptor(Type type)
{
return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = base.BindModel(controllerContext, bindingContext);
var descriptor = GetTypeDescriptor(bindingContext.ModelType);
/*************************/
// this next statement returns null!
/*************************/
AliasAttribute attr = (AliasAttribute)descriptor.GetAttributes()[typeof(AliasAttribute)];
if (attr == null)
return null;
HttpRequestBase request = controllerContext.HttpContext.Request;
foreach (var key in request.Form.AllKeys)
{
if (string.IsNullOrEmpty(attr.Prefix) == false)
{
if (key.StartsWith(attr.Prefix, StringComparison.InvariantCultureIgnoreCase))
{
if (string.IsNullOrEmpty(attr.Suffix) == false)
{
if (key.EndsWith(attr.Suffix, StringComparison.InvariantCultureIgnoreCase))
{
return request.Form.Get(key);
}
}
return request.Form.Get(key);
}
}
else if (string.IsNullOrEmpty(attr.Suffix) == false)
{
if (key.EndsWith(attr.Suffix, StringComparison.InvariantCultureIgnoreCase))
{
return request.Form.Get(key);
}
}
if (attr.HasIncludes)
{
foreach (var include in attr.InlcludeSplit)
{
if (key.Equals(include, StringComparison.InvariantCultureIgnoreCase))
{
return request.Form.Get(include);
}
}
}
}
return null;
}
}
AliasAttribute:
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class AliasAttribute : Attribute
{
private string _include;
private string[] _inlcludeSplit = new string[0];
public string Prefix { get; set; }
public string Suffix { get; set; }
public string Include
{
get
{
return _include;
}
set
{
_include = value;
_inlcludeSplit = SplitString(_include);
}
}
public string[] InlcludeSplit
{
get
{
return _inlcludeSplit;
}
}
public bool HasIncludes { get { return InlcludeSplit.Length > 0; } }
internal static string[] SplitString(string original)
{
if (string.IsNullOrEmpty(original))
{
return new string[0];
}
return (from piece in original.Split(new char[] { ',' })
let trimmed = piece.Trim()
where !string.IsNullOrEmpty(trimmed)
select trimmed).ToArray<string>();
}
}
Usage:
public JsonResult EmailCheck([ModelBinder(typeof(AliasModelBinder)), Alias(Suffix = "Email")]string email)
{
// email will be assigned to any field suffixed with "Email". e.g. PrimaryEmail, SecondaryEmail and so on
}
Gave up on this and then stumbled across the Action Parameter Alias code base that will probably allow me to do this. It's not as flexible as what I started out to write but probably can be modified to allow wild cards.
what I did was make my attribute subclass System.Web.Mvc.CustomModelBinderAttribute which then allows you to return a version of your custom model binder modified with the aliases.
example:
public class AliasAttribute : System.Web.Mvc.CustomModelBinderAttribute
{
public AliasAttribute()
{
}
public AliasAttribute( string alias )
{
Alias = alias;
}
public string Alias { get; set; }
public override IModelBinder GetBinder()
{
var binder = new AliasModelBinder();
if ( !string.IsNullOrEmpty( Alias ) )
binder.Alias = Alias;
return binder;
}
}
which then allows this usage:
public ActionResult Edit( [Alias( "somethingElse" )] string email )
{
// ...
}

attribute dependent on another field

In a model of my ASP.NET MVC application I would like validate a textbox as required only if a specific checkbox is checked.
Something like
public bool retired {get, set};
[RequiredIf("retired",true)]
public string retirementAge {get, set};
How can I do that?
Thank you.
Take a look at this: http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx
I've modded the code somewhat to suit my needs. Perhaps you benefit from those changes as well.
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentUpon { get; set; }
public object Value { get; set; }
public RequiredIfAttribute(string dependentUpon, object value)
{
this.DependentUpon = dependentUpon;
this.Value = value;
}
public RequiredIfAttribute(string dependentUpon)
{
this.DependentUpon = dependentUpon;
this.Value = null;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{ }
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
// no client validation - I might well blog about this soon!
return base.GetClientValidationRules();
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentUpon);
if (field != null)
{
// get the value of the dependent property
var value = field.GetValue(container, null);
// compare the value against the target value
if ((value != null && Attribute.Value == null) || (value != null && value.Equals(Attribute.Value)))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
Then use it:
public DateTime? DeptDateTime { get; set; }
[RequiredIf("DeptDateTime")]
public string DeptAirline { get; set; }
Just use the Foolproof validation library that is available on Codeplex:
https://foolproof.codeplex.com/
It supports, amongst others, the following "requiredif" validation attributes / decorations:
[RequiredIf]
[RequiredIfNot]
[RequiredIfTrue]
[RequiredIfFalse]
[RequiredIfEmpty]
[RequiredIfNotEmpty]
[RequiredIfRegExMatch]
[RequiredIfNotRegExMatch]
To get started is easy:
Download the package from the provided link
Add a reference to the included .dll file
Import the included javascript files
Ensure that your views references the included javascript files from within its HTML for unobtrusive javascript and jquery validation.
Using NuGet Package Manager I intstalled this: https://github.com/jwaliszko/ExpressiveAnnotations
And this is my Model:
using ExpressiveAnnotations.Attributes;
public bool HasReferenceToNotIncludedFile { get; set; }
[RequiredIf("HasReferenceToNotIncludedFile == true", ErrorMessage = "RelevantAuditOpinionNumbers are required.")]
public string RelevantAuditOpinionNumbers { get; set; }
I guarantee you this will work!
I have not seen anything out of the box that would allow you to do this.
I've created a class for you to use, it's a bit rough and definitely not flexible.. but I think it may solve your current problem. Or at least put you on the right track.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
namespace System.ComponentModel.DataAnnotations
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' is required";
private readonly object _typeId = new object();
private string _requiredProperty;
private string _targetProperty;
private bool _targetPropertyCondition;
public RequiredIfAttribute(string requiredProperty, string targetProperty, bool targetPropertyCondition)
: base(_defaultErrorMessage)
{
this._requiredProperty = requiredProperty;
this._targetProperty = targetProperty;
this._targetPropertyCondition = targetPropertyCondition;
}
public override object TypeId
{
get
{
return _typeId;
}
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _requiredProperty, _targetProperty, _targetPropertyCondition);
}
public override bool IsValid(object value)
{
bool result = false;
bool propertyRequired = false; // Flag to check if the required property is required.
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
string requiredPropertyValue = (string) properties.Find(_requiredProperty, true).GetValue(value);
bool targetPropertyValue = (bool) properties.Find(_targetProperty, true).GetValue(value);
if (targetPropertyValue == _targetPropertyCondition)
{
propertyRequired = true;
}
if (propertyRequired)
{
//check the required property value is not null
if (requiredPropertyValue != null)
{
result = true;
}
}
else
{
//property is not required
result = true;
}
return result;
}
}
}
Above your Model class, you should just need to add:
[RequiredIf("retirementAge", "retired", true)]
public class MyModel
In your View
<%= Html.ValidationSummary() %>
Should show the error message whenever the retired property is true and the required property is empty.
Hope this helps.
Try my custom validation attribute:
[ConditionalRequired("retired==true")]
public string retirementAge {get, set};
It supports multiple conditions.

Resources