Reinforced.Typings cannot handle attributes on properties - c#

I have an ASP.NET Core project using ASP.NET Identity. Some classes cannot be exported since their properties contain attributes from System.ComponentModel.DataAnnotations. If I ignore such properties, everything works fine.
Example class:
public class LoginViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
Since I don't want to strip out these attributes, what is the recommended course of action?
[assembly: TsGlobal(
CamelCaseForMethods = true,
CamelCaseForProperties = true,
GenerateDocumentation = true,
UseModules = true,
DiscardNamespacesWhenUsingModules = true)]
namespace X
{
public static class ReinforcedTypingsConfiguration
{
public static void Configure(ConfigurationBuilder builder)
{
builder
.ExportAsInterface<LoginViewModel>()
.AutoI(false)
.WithProperty(x => x.RememberMe)
;
}
}
}
Other than that, it seems that UseModules does the opposite of what I want it to do. When set to true, the generated ts file doesn't contain any modules. When set to false, I get module in my ts files.
Also, when dividing types among files, I get strange folder names on mac that contain \ for every . in my namespace. Can I just flatten out the structure? And completely ignore backend namespaces?
In the above configuration (and I use file splitting) I get the annoying error message error MSB3552: Resource file "**/*.resx" cannot be found..

Upgrade to 1.4.2 to fix this issue.

Related

Reuse Model from another project

I've got a Custom Validator Attribute called RequiredIf inside a Model in my main Web Project and I need to reuse that model in another Model from another project. When I look at the disassembled dll from the Main Project inside the subproject, the Custom Attribute just disappears and the Custom Attribute doesn´t work.
Here goes my structure:
MainProject
[RequiredIf( "TipoReagendamentoSco", false, "CodigoTipoReagendamento", null, "Tipo Reagendamento é obrigatório")]
[Display(Name = "Tipo Reagendamento:")]
public int CodigoTipoReagendamento { get; set; }
SubProject
public TipoReagendamentoModel TipoReagendamento { get; set; }
Disassembled MainProject.DLL
[Display(Name = "Tipo Reagendamento:")]
public int CodigoTipoReagendamento { get; set; }
The Custom Validator Attribute (RequiredIf) went away. But when I use 'Required' from DataAnnotations (not a custom attribute), it works like a charm.
Thanks !

EmailAddressAtribute ignored

I have a class which defines the property EmailAddress with the attribute EmailAddressAttribute from System.ComponentModel.DataAnnotations:
public class User : Entity
{
[EmailAddress]
public string EmailAddress { get; set; }
[Required]
public string Name { get; set; }
}
public class Entity
{
public ICollection<ValidationResult> Validate()
{
ICollection<ValidationResult> results = new List<ValidationResult>();
Validator.TryValidateObject(this, new ValidationContext(this), results);
return results;
}
}
When I set the value of EmailAddress to be an invalid email (e.g. 'test123'), the Validate() method tells me the entity is valid.
The RequiredAttribute validation is working (e.g. setting Name to null shows me a validation error).
How do I get EmailAddressAttribute working in my validator?
After playing with the overloads available for each method, I found the following overload which includes a parameter called validateAllProeprties.
When this is set to true the object is property validated.
Validator.TryValidateObject(this, new ValidationContext(this), results, true);
I'm not sure why you wouldn't want to validate all properties, but having this set to false or not set (defaults to false) will only validate required attributes.
This MSDN article explains.
to use Validation with the Data Annotation Validators you should add both references to
Microsoft.Web.Mvc.DataAnnotations.dll assembly and the System.ComponentModel.DataAnnotations.dll assembly.
then you need to register the DataAnnotations Model Binder in the Global.asax file. Add the following line of code to the Application_Start() event handler so that the Application_Start() method looks like this:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new Microsoft.Web.Mvc.DataAnnotations.DataAnnotationsModelBinder();
}
after that you have registered the dataAnnotationsModelBinder as the default model binder for the entire ASP.NET MVC application
then your code should work properly
public class User : Entity
{
[EmailAddress]
public string EmailAddress { get; set; }
[Required]
public string Name { get; set; }
}
refer here for documentation

Can ASP.Net MVC 2 Custom attributes on class level and on property level be active in the same time?

I am working on asp.net mvc 2 web application.
I have model with 3 properties:
[IsCityInCountry("CountryID", "CityID"]
public class UserInfo
{
[Required]
public int UserID { get; set; }
[Required]
public int CountryID { get; set; }
[Required]
public int CityID { get; set; }
}
I have one "required" property attribute, and one attribute on class level :
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class IsCityInCountry : ValidationAttribute
{
public IsCityInCountry(string countryIDProperty, string cityIDProperty)
{
CountryIDProperty = countryIDProperty;
CityIDProperty = cityIDProperty;
}
public string CountryIDProperty { get; set; }
public string CityIDProperty { get; set; }
public override bool IsValid(object value)
{
var properties = TypeDescriptor.GetProperties(value);
var countryID = properties.Find(CountryIDProperty, true).GetValue(value);
var cityID = properties.Find(CityIDProperty , true).GetValue(value);
int countryIDInt;
int.TryParse(countryID.ToString(), out countryIDInt);
int cityIDInt;
int.TryParse(cityID.ToString(), out cityIDInt);
if (CountryBusiness.IsCityInCountry(countryIDInt, cityIDInt))
{
return true;
}
return false;
}
}
When I post the form on my view, and CountryID is not entered, in ModelState dictionary there's an error about that issue. Other attribute is ignored ("IsCityInCountry"). When I choose CountryID and CityID, which is not in selected country, I get appropriate validation message about that, and ModelState has another key (which is ""). I understand that advantage have property attributes and then class attributes. My question; is there any way to get all validation messages at the same time, no matter what kind of attributes are involved (class or property attributes)? Thanks in advance.
ASP.NET MVC won't perform class level validation if there are property level validation errors. Brad Wilson explains this in his blog post:
Earlier today, we committed a change to MVC 2 that converted the
validation system from Input Validation to Model Validation.
What this means is that we will always run all validators on an
object, if that object had at least one value bound into it during
model binding. We run the property-level validators first, and if all
of those succeed, we'll run the model-level validators.
I would recommend you to go ahead and checkout FluentValidation.NET if you want to perform some more advanced validation in an ASP.NET MVC application. Declarative validation simply doesn't fit the bill in advanced validation scenarios.

DisplayName attribute from Resources?

I have a localized application, and I am wondering if it is possible to have the DisplayName for a certain model property set from a Resource.
I'd like to do something like this:
public class MyModel {
[Required]
[DisplayName(Resources.Resources.labelForName)]
public string name{ get; set; }
}
But I can't to it, as the compiler says: "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type" :(
Are there any workarounds? I am outputting labels manually, but I need these for the validator output!
If you use MVC 3 and .NET 4, you can use the new Display attribute in the System.ComponentModel.DataAnnotations namespace. This attribute replaces the DisplayName attribute and provides much more functionality, including localization support.
In your case, you would use it like this:
public class MyModel
{
[Required]
[Display(Name = "labelForName", ResourceType = typeof(Resources.Resources))]
public string name{ get; set; }
}
As a side note, this attribute will not work with resources inside App_GlobalResources or App_LocalResources. This has to do with the custom tool (GlobalResourceProxyGenerator) these resources use. Instead make sure your resource file is set to 'Embedded resource' and use the 'ResXFileCodeGenerator' custom tool.
(As a further side note, you shouldn't be using App_GlobalResources or App_LocalResources with MVC. You can read more about why this is the case here)
How about writing a custom attribute:
public class LocalizedDisplayNameAttribute: DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
// TODO: Return the string from the resource file
}
}
which could be used like this:
public class MyModel
{
[Required]
[LocalizedDisplayName("labelForName")]
public string Name { get; set; }
}
If you open your resource file and change the access modifier to public or internal it will generate a class from your resource file which allows you to create strongly typed resource references.
Which means you can do something like this instead (using C# 6.0).
Then you dont have to remember if firstname was lowercased or camelcased. And you can see if other properties use the same resource value with a find all references.
[Display(Name = nameof(PropertyNames.FirstName), ResourceType = typeof(PropertyNames))]
public string FirstName { get; set; }
Update:
I know it's too late but I'd like to add this update:
I'm using the Conventional Model Metadata Provider which presented by Phil Haacked it's more powerful and easy to apply take look at it :
ConventionalModelMetadataProvider
Old Answer
Here if you wanna support many types of resources:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
private readonly PropertyInfo nameProperty;
public LocalizedDisplayNameAttribute(string displayNameKey, Type resourceType = null)
: base(displayNameKey)
{
if (resourceType != null)
{
nameProperty = resourceType.GetProperty(base.DisplayName,
BindingFlags.Static | BindingFlags.Public);
}
}
public override string DisplayName
{
get
{
if (nameProperty == null)
{
return base.DisplayName;
}
return (string)nameProperty.GetValue(nameProperty.DeclaringType, null);
}
}
}
Then use it like this:
[LocalizedDisplayName("Password", typeof(Res.Model.Shared.ModelProperties))]
public string Password { get; set; }
For the full localization tutorial see this page.
I got Gunders answer working with my App_GlobalResources by choosing the resources properties and switch "Custom Tool" to "PublicResXFileCodeGenerator" and build action to "Embedded Resource".
Please observe Gunders comment below.
Works like a charm :)
public class Person
{
// Before C# 6.0
[Display(Name = "Age", ResourceType = typeof(Testi18n.Resource))]
public string Age { get; set; }
// After C# 6.0
// [Display(Name = nameof(Resource.Age), ResourceType = typeof(Resource))]
}
Define ResourceType of the attribute so it looks for a resource
Define Name of the attribute which is used for the key of resource, after C# 6.0, you can use nameof for strong typed support instead of hard coding the key.
Set the culture of current thread in the controller.
Resource.Culture = CultureInfo.GetCultureInfo("zh-CN");
Set the accessibility of the resource to public
Display the label in cshtml like this
#Html.DisplayNameFor(model => model.Age)

How to define a property with same name on two different types in ROWLEX?

If I have those two classes that have two different properties but with the same name:
[RdfSerializable]
public class Type1
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true), Name = "title"]
public string Title { get; set; }
}
and try to serialize them to RDF and validate them with http://www.w3.org/RDF/Validator/ service. Everything is Okay and they are correct.
But after I try to generate OWL files from those classes with OntologyExtractor.exe tool I get that message:
"Ontology extraction failed. http://test.org/1.0#title is assigned to more than one type."
This is strange message as the upper classes are correct and there are some RDF specifications that has same situation with different classes that have same named properties.
I expect it is a bug in ROWLEX. Your case is a valid one, but I assume I did not prepare for it when I wrote OntologyExtractor. I will try to release a fix as soon as possible.
EDIT: ROWLEX2.1 is released, you can download it from http://rowlex.nc3a.nato.int. Version 2.1 (among others) supports now the shared property functionality. The exact code in the question would still result the same error! To overcome that, you should alter the decoration of your code as follows:
[RdfSerializable]
public class Type1
{
[RdfProperty(true, Name = "title", ExcludeFromOntology=true)]
public string Title { get; set; }
}
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)})]
public string Title { get; set; }
}
Using the OntologyExtractor.exe, this code will result a OWL property of with an anonymous domain class that is the UNION of Type1 and Type2.
While this is technically perfectly correct solution, setting domains on properties limit their possible future reuse. As a solution, you might want to substitute the property domain with local restrictions. You can achieve that as follows:
[RdfSerializable]
public class Type2
{
[RdfProperty(true, Name = "title",
DomainAsType = new Type[]{typeof(Type1), typeof(Type2)},
UseLocalRestrictionInsteadOfDomain = true)]
public string Title { get; set; }
}
Should you leave UseLocalRestrictionInsteadOfDomain not set, ROWLEX chooses between domain and local restriction according to the current context.

Categories

Resources