I'm trying to validate a class using Data Annotations but with a metadata class.
[MetadataType(typeof(TestMetaData))]
public class Test
{
public string Prop { get; set; }
internal class TestMetaData
{
[Required]
public string Prop { get; set; }
}
}
[Test]
[ExpectedException(typeof(ValidationException))]
public void TestIt()
{
var invalidObject = new Test();
var context = new ValidationContext(invalidObject, null, null);
context.MemberName = "Prop";
Validator.ValidateProperty(invalidObject.Prop, context);
}
The test fails. If I ditch the metadata class and just decorated the property on the actual class it works fine. WTH am I doing wrong? This is putting me on the verge of insanity. Please help.
Answer
Here is a link to the post that helped me solve this issue. Apparently you have to register the matadata class first.
The metadata class must be public for the external validation to work.
[MetadataType(typeof(TestMetaData))]
public class Test
{
public string Prop { get; set; }
public class TestMetaData
{
[Required]
public string Prop { get; set; }
}
}
I believe defining the metadata class inside of your model class, like you did in your example, should work. Haven't tested it.
Related
I have following:
var type = typeof(ExampleClass);
public abstract class ExampleClass
{
public string Name { get; set; }
public abstract class InternalExampleClass
{
public string InternalName { get; set; }
}
}
How can I get the value of Name, InternalName?
I tried to use type.GetFields() but it doesn't return InternalName
help me, please
I can't answer all points of your question. But I can give you an idea how to start.
You don't have access to constants, but there is a workaround. First, you need an instance of your abstract class in order to use reflection. Since you can't create an object of an abstract class, you need a class which inherits it. This class contains properties set to the value of your constants.
public class InheritedReportAPI : ReportAPI
{
public string constName { get; } = ReportAPI.Name;
public string constSignatureBase { get; } = ReportAPI.SignatureBase;
public string constEventsReportsDeleted { get; } = ReportAPI.Events.ReportsDeleted;
}
Then you can use Reflection to get names and/or values of these properties.
var inheritedReportApi = new InheritedReportAPI();
var propertyList = inheritedReportApi.GetType().GetProperties();
foreach(var property in propertyList)
System.Console.WriteLine($"{property.Name}: {property.GetValue(inheritedReportApi)}");
The result:
constName: reports
constSignatureBase: /report/reports
constEventsReportsDeleted: reports_deleted
i am having around 7 models who have same properties(atributes). On view page i am using a model(name = commonModel) which contains all those properties and a extra property to choose in which model's database i want to save that sent data so i created a valuesRelocate Method that will assign all the properties of commonModel to the choosen model (in this case article).
The code i gave below is working but i am getting a error when assigning value of a property of commonModel to a property of article.
What is the better way to do this.
Error is at tempModel.question
public ActionResult Create([Bind(Include =
"Id,question,ans,ruleApplicable,hint,exception,modelSelector")]
commonModel commonModel)
{
if (ModelState.IsValid)
{
if (commonModel.modelSelector == "article")
{
article model2 = new article();
article model1 = valuesRelocate<article>(commonModel,
model2);
db.articleDb.Add(model1);
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(commonModel);
}
private T valuesRelocate<T>(commonModel commonModel, T tempModel) {
tempModel.question = commonModel.question;
return tempModel;
}
I am using a abstract base class named baseGrammar .code for both the class is shown below
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleApplicable { get; set; }
public string hint { get; set; }
public bool exception { get; set; }
}
the one shown above is base class
and those shown below are derived classes
i use different classes because i wanted to have different classes for different grammatical concepts.
public class article : baseGrammar
{
}
public class commonModel : baseGrammar
{
[Required]
public string modelSelector { get; set; }
}
hope this helps.
You just need to constrain the type parameter T to be derived from your base class:
// Names changed to follow .NET naming conventions
private T RelocateValues<T>(BaseGrammar baseModel, T tempModel)
where T : BaseGrammar
{
tempModel.question = baseModel.question;
return tempModel;
}
However, given that you're modifying the incoming model, you could remove the return value and just change the method to:
private void RelocateValues(BaseGrammar from, BaseGrammar to)
{
to.question = from.question;
}
Then in your calling code:
Article model = new Article();
RelocateValues(model);
db.ArticleDb.Add(model);
There's no need to have two separate variables which will refer to the same object anyway...
My situation is the following:
I'm coding a MVC website on Visual Studio 2013 using database-first approach with Entity Framework.
EF automatically generates the models. But I need to add custom attributes (~NOT~ necessarily for data validation but also for internal processes) and, via reflection, access those custom attributes.
Let's say I have
public partial class Application {
public int AppID {get; set;}
public string Name {get; set;}
//etc...
}
I've tried the following approaches:
• On a different file I continue the partial class:
public partial class Application {
[MyAttributeOne]
public int AppID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
• Use the MetaData class
public class ApplicationMetadata {
[MyAttributeOne]
public int SolutionID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
[MetadataType(typeof(ApplicationMetadata))]
public partial class Application { }
• Inherit the class with attributes:
public class ApplicationMetadata {
[MyAttributeOne]
public int SolutionID { get; set; }
[DataType(DataType.Text)]
[MyAttributeTwo]
public string Name { get; set; }
}
public partial class Application : ApplicationMetadata { }
• And the 'Buddy class' approach where I do basically the previous 2 approaches but instead I define the class with the attributes inside the 'Application' class.
Am I doing something wrong? Or is this simply impossible?
I need to be able to make the following code work:
foreach (PropertyInfo propertyInfo in currentObject.GetType().GetProperties())
{
foreach (CustomAttributeData attrData in propertyInfo.GetCustomAttributesData())
{
if (typeof(attrData) == typeof(MyAttributeOne))
//stuff
else if (typeof(attrData) == typeof(MyAttributeTwo))
//different stuff
else
//yet more stuff
}
}
Thank you very much for the attention!
Regards.
OK, this is a little involved but it's fairly simple. This is also really a bit of a brain dump but it does work and gives you enough to work with. Lets set up with some basics:
//A couple of custom attributes
public class MyAttributeOne : Attribute { }
public class MyAttributeTwo : Attribute { }
//A metadata class where we can use the custom attributes
public sealed class MyEntityMetadata
{
//This property has the same name as the class it is referring to
[MyAttributeOne]
public int SomeProperty { get; set; }
}
//And an entity class where we use System.ComponentModel.DataAnnotations.MetadataType
//to tell our function where the metadata is stored
[MetadataType(typeof(MyEntityMetadata))]
public class MyEntity
{
public int SomeProperty { get; set; }
}
OK, still with me? Now we need a function to process the properties in the same way you did earlier:
public void DoStuff(object currentObject)
{
//Lets see if our entity class has associated metadata
var metaDataAttribute = currentObject.GetType()
.GetCustomAttributes()
.SingleOrDefault(a => a is MetadataTypeAttribute) as MetadataTypeAttribute;
PropertyInfo[] metaProperties = null;
//Cache the metadata properties here
if (metaDataAttribute != null)
{
metaProperties = metaDataAttribute.MetadataClassType.GetProperties();
}
//As before loop through each property...
foreach (PropertyInfo propertyInfo in currentObject.GetType().GetProperties())
{
//Refactored this out as it's called again later
ProcessAttributes(propertyInfo.GetCustomAttributes());
//Now check the metadata class
if (metaProperties != null)
{
//Look for a matching property in the metadata class
var metaPropertyInfo = metaProperties
.SingleOrDefault(p => p.Name == propertyInfo.Name);
if (metaPropertyInfo != null)
{
ProcessAttributes(metaPropertyInfo.GetCustomAttributes());
}
}
}
}
And of course, here is the refactored method to process the attributes:
private void ProcessAttributes(IEnumerable<Attribute> attributes)
{
foreach (var attr in attributes)
{
if (attr is MyAttributeOne)
{
Console.WriteLine("MyAttributeOne found");
}
else if (attr is MyAttributeTwo)
{
Console.WriteLine("MyAttributeTwo found");
}
else
{
}
}
}
Let's say I have a class from a 3rd-party, which is a data-model. It has perhaps 100 properties (some with public setters and getters, others with public getters but private setters). Let's call this class ContosoEmployeeModel
I want to facade this class with an interface (INavigationItem, which has Name and DBID properties) to allow it to be used in my application (it's a PowerShell provider, but that's not important right now). However, it also needs to be usable as a ContosoEmployeeModel.
My initial implementation looked like this:
public class ContosoEmployeeModel
{
// Note this class is not under my control. I'm supplied
// an instance of it that I have to work with.
public DateTime EmployeeDateOfBirth { get; set; }
// and 99 other properties.
}
public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
private ContosoEmployeeModel model;
public FacadedEmployeeModel(ContosoEmployeeModel model)
{
this.model = model;
}
// INavigationItem properties
string INavigationItem.Name { get; set;}
int INavigationItem.DBID { get; set;}
// ContosoEmployeeModel properties
public DateTime EmployeeDateOfBirth
{
get { return this.model.EmployeeDateOfBirth; }
set { this.model.EmployeeDateOfBirth = value; }
}
// And now write 99 more properties that look like this :-(
}
However, it's clear that this will involve writing a huge amount of boilerplate code to expose all the properties , and I'd rather avoid this if I can. I can T4 code-generate this code in a partial class, and will do if there aren't any better ideas, but I though I'd ask here to see if anyone had any better ideas using some super wizzy bit of C# magic
Please note - the API I use to obtain the ContosoEmployeeModel can only return a ContosoEmployeeModel - I can't extend it to return a FacededEmployeeModel, so wrapping the model is the only solution I can think of - I'm happy to be corrected though :)
The other approach may be suitable for you is to use AutoMapper to map base class to your facade here is sample code:
class Program
{
static void Main(string[] args)
{
var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };
Mapper.CreateMap<Model, FacadeForModel>();
var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);
Console.WriteLine(mappedObject);
Console.ReadLine();
}
class Model
{
public string Name { get; set; }
public DateTime Date { get; set; }
public int Count { get; set; }
}
interface INavigationItem
{
int Id { get; set; }
string OtherProp { get; set; }
}
class FacadeForModel : Model, INavigationItem
{
public int Id { get; set; }
public string OtherProp { get; set; }
}
}
Resharper allows the creation of "delegating members", which copies the interface of a contained object onto the containing object and tunnels the method calls/property access through to the contained object.
http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html
Once you've done that, you can then extract an interface on your proxy class.
I tried to assign a custom Attribute to class that comes from a dynamic proxy
System.Data.Entity.DynamicProxies.Login_A2947F53...
Example class Login
public partial class Login
{
[CustomAttribute]
public virtual int Id
{
get;
set;
}
}
Now I try to access the Attribute using Generics and Reflection
public static void Process(TSource source)
{
foreach (PropertyInfo p in target.GetType().GetProperties(flags))
{
object[] attr = p.GetCustomAttributes(true); // <- empty
}
}
But there is no Attribute. Is that due to the DynmaicProxy or what did I do wrong here?
When I use a concrete class without dynamic proxy like this one, then I get the attributes.
public class TestObject
{
[CustomAttribute]
public virtual string Name { get; set; }
[CustomAttribute]
public virtual string Street { get; set; }
public virtual int Age { get; set; }
public virtual string Something { get; set; }
}
OK, this one was obvious after a closer look;
System.Data.Entity.DynamicProxies.Login_A2947F53...
is a dynamicProxy type and know nothing about any Attributes. So I have to use the something like:
foreach (PropertyInfo p in typeof(Login).GetProperties(flags))
instead of the dynamicProxy instance to get the type from. And finaly there are my Attributes.
Use BaseType.
public static void Process(TSource source)
{
foreach (PropertyInfo p in target.GetType().BaseType.GetProperties(flags))
{
object[] attr = p.GetCustomAttributes(true);
}
}