Click here to Skip to main content
15,887,676 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Dears,

I want to do some weird job which is:
adding attributes dynamically to my model class without adding these attributes directly on each property in the model class.

the whole story is:
I have an MVC project using Code first with domain driven design so i have the following:
Core Class : Employee exists in DDD.Core Project
Model Class : EmployeeModel exists in DDDApp Project [main App]
Now, i wants Model class [EmployeeModel] to have the mapped attributes of core class [Employee].
Note:
To not break down the design, i should not add reference of DDD.Core in DDDApp.


What I have tried:

Actually i made a good progress in that:
In global.asax File i have loaded all of the DDD.Core classes
C#
string webRootPath = HttpContext.Current.Server.MapPath("~");
            string docPath = Path.GetFullPath(Path.Combine(webRootPath, "..\\DDD.Core\\bin\\Debug\\DDD.Core.dll"));
            Assembly DDDCore = Assembly.LoadFile(docPath);
            Application["DDDCoreAssembly"] = DDDCore;

In DataAnnotationsModelMetadataProvider class which i inherit, i did the following code:
C#
public class MetadataProvider : DataAnnotationsModelMetadataProvider
    {
        protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
        {
            //All DDD.Core Asembly which contains the core classes
            Assembly DDDCoreAssembly = HttpContext.Current.Application["DDDCoreAssembly"] as Assembly;
            if (containerType != null)
            {
                //Get the Core Class Employee for example.
                Type coreModelType = DDDCoreAssembly.GetTypes().Single(x => x.Name == containerType.Name.Replace("Model", ""));
                //Get Employee Class Property
                MemberInfo coreClassProperty = coreModelType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                if (coreClassProperty != null)
                {
                    //Get All Attributes of CoreClass Property
                    object[] coreClassPropertyAttributes = coreClassProperty.GetCustomAttributes(true);
                    foreach (var item in coreClassPropertyAttributes)
                    {
                        if (item.GetType() == typeof(RequiredAttribute))
                        {
                            RequiredAttribute ra = new RequiredAttribute();
                            //#########################
                            //Now, I can't add this attribute to the property dynamically (Any Ideas)
                            //#########################
                        }
                        else if (item.GetType() == typeof(StringLengthAttribute))
                        {
                            StringLengthAttribute sla = new StringLengthAttribute(((System.ComponentModel.DataAnnotations.StringLengthAttribute)(item)).MaximumLength);
                        sla.MinimumLength = ((System.ComponentModel.DataAnnotations.StringLengthAttribute)(item)).MinimumLength;
                            sla.ErrorMessage = "el length ya ged3an";
                            //#########################
                            //Now, I can't add this attribute to the property dynamically (Any Ideas)
                            //######################### 
                        }
                        else if (item.GetType() == typeof(RegularExpressionAttribute))
                        {

                        }
                    }
                }
            }
            var result = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

            //*************************
            //This Part of Code is used for localization
            //*************************
            if (string.IsNullOrEmpty(result.DisplayName) && containerType != null && propertyName != null)
            {
                var keyForDisplayValue = string.Format("{0}_{1}", containerType.Name, propertyName);
                var translatedValue = MCITemplateV4.Properties.Resources.ResourceManager.GetObject(keyForDisplayValue) as string;

                if (!string.IsNullOrEmpty(translatedValue))
                {
                    result.DisplayName = translatedValue;
                }
            }

            return result;
        }
    }


I just can't add the final step which is adding attributes to the properties dynamically?

So Can any one help me with that?!!!!
Posted
Updated 14-Apr-16 1:27am
v4
Comments
_ProgProg_ 14-Apr-16 5:45am    
Any Ideas???

1 solution

Sounds impossible - at least I can't think about a way to do it...
Anyway some thoughts:

What do you mean you can't add a reference to your core dll? But you crate a "hardcoded" reference (better dependency) to it anyway when reflecting, don't you?

Why don't you use any of the "Standard Patterns" for this kind of Problem?
* create a wrapper and add the attributes there (would be more code than adding the attributes to the original classes, if you can)
* Use some binary rewriter and AOP (not yet available in native .NET, but I heard about some 3rd Party products (codecontracts etc.))
* Generate the classes with the attributes to save typing work (I think this is what most Tools do, use T4 to define the code Generator)
* Write some VS (IDE) Extension to add or (pre-)process the model files and add the attributes before compilation.
* If only you are consuming the attributes (I don't think this is the case, it seems you plan use by a validator) you can manipulate the TypeDescriptor for your model types (where Attributes can be added dynamically AFAIK)

so maybe you can find another way (I did once solve a similar problem with T4 generated templates for the whole model class)

Good luck with your Project!
 
Share this answer
 
Comments
_ProgProg_ 14-Apr-16 7:41am    
Ok, I appreciate your answer alot.
It's a code design principle in my company not to add a reference of Core project in WebApp.
Actually Core Classes Maps DB Tables with its attributes [i am using code first] so what do you mean by creating a wrapper and adding attributes to it.

Just to clarify why did i thought about this idea, if i have a property called EmployeeName which is mapped to a column [EmployeeName nvarchar 200] in Employees Table and i wanted to change column capacity of the column to be 300 i have to the following changes:
First, i have to change the string length property of EmployeeName in CORE Class to be 300.
Second, Run Migration to update database.
Third, Update EmployeeName property stringlength attribute to be 300 in EmployeeModel in WebApp to validate the property in UI.

All of what i want is to remove step NO 3 and when i update CORE Class attribute, the Employee model attribute will be created in runtime from the core class.
johannesnestler 14-Apr-16 8:05am    
So I would just generate the model classes from the information in the core classes. Have a look at an OR-Mapper like EntityFramework, how they do it (with T4 templates). As I said it would be easiest to use a Generator for the whole class if deterministic enough (should be true for simple model-classes)...
johannesnestler 14-Apr-16 8:18am    
just to mention: your idea now would just solve the Attribute Problem, what you do if new columns are added to the database and core-classes? You have to manually Change your model code anyway - a Generator should generate the whole class - all properties, attributes, Interface implementations, ... better invest some time in that, than in your "fragile" reflection code - reflecting on not referenced types - won't go into that anymore....
johannesnestler 14-Apr-16 8:09am    
about reference - do whatever you think is clever - I don't agree because: You HAVE A DEPENDENCY and there is a good reason to express this by giving a reference to the WebApp IF you relay on it anyway.... Desing principle not to add a reference you need sounds a little (...) - also the Project name "Core" would lead me to the opposite "prinicple" - always add a reference to it ;)
_ProgProg_ 14-Apr-16 7:45am    
So, isn't there is anyway to create attributes on class properties dynamic?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900