如何延长DataAnnotationsModelMetadata [英] How can I extend DataAnnotationsModelMetadata

查看:133
本文介绍了如何延长DataAnnotationsModelMetadata的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义的 DataAnnotationsModelMetadataProvider ,我想返回从 ModelMetadata 派生的对象,这样我可以有额外的属性在我的剃须刀模板。

I have a custom DataAnnotationsModelMetadataProvider and I'd like to return an object derived from ModelMetadata so that I can have extra properties in my razor templates.

到目前为止,我的自定义提供商只覆盖了 CreateMetadata 功能:

So far my custom provider only overrides the CreateMetadata function:

protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
    var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);

    ModelMetadataAttribute mma;            
    foreach (Attribute a in attributes)
    {
        mma = a as ModelMetadataAttribute;
        if (mma != null)
            mma.Process(modelMetadata);                
    }

    return modelMetadata;
}        

这样从 ModelMetadataAttribute 导出每个属性都可以做一些自定义操作(实际上只增加了 AdditionalValues​​

so that every attribute derived from ModelMetadataAttribute can do some custom actions (actually it only adds AdditionalValues)

不过,因为几乎所有我的属性主要属性添加到HTML元素我在剃刀模板生成,我想在 ModelMetadata 在视图中包含dictionnary我想补充的属性(和一些其他的东西)的。

But since nearly all of my attributes mainly add attributes to the html elements I generate in my razor template, I'd like the ModelMetadata in the view to contain a dictionnary of the attributes I want to add (and some other things).

所以,我需要一个类从 DataAnnotationsModelMetadata 继承。

So I need a class that inherits from DataAnnotationsModelMetadata.

我不能随便叫 base.CreateMetadata 函数,因为它将无法​​正常投我的派生类。

I can't just call the base.CreateMetadata function since it won't cast properly to my derived class.

我想过使得由 base.CreateMetadata 函数返回到我的派生 DataAnnotationsModelMetadata 的公共属性的副本一流的,但我可以失去信息,这样看起来很安全不。

I thought about making a copy of public properties of DataAnnotationsModelMetadata returned by the base.CreateMetadata function into my derived class, but I can loose information so it doesn't seem safe.

我想到了另一种方式是复制/粘贴 CreateMetadata 的基础上code,并添加我的逻辑,但似乎丑...(只有我有MVC3源,所以它可能在mvc4已经改变)

Another way I thought of is to copy/paste the base code of CreateMetadata and add my logic, but it seems ugly... (and I only have the mvc3 sources, so it might have changed in mvc4)

也想过从 ViewDataDictionnary 继承的,这样我可以提供我的自定义元数据类,而不是标准之一,但我没有一个关于如何做到这一点的线索。 (还我承认我没挖多少就特定问题)

Also thought of inheriting from ViewDataDictionnary so that I can provide my custom metadata class instead of the standard one, but I don't have a clue about how to do this. (also I admit I didn't dig much on that particular question)

我看了好多篇关于 DataAnnotations 和供应商也没有找到类似的什么,我试图做一些事情。

I looked at a lot of articles about DataAnnotations and providers but couldn't find something similar to what I'm trying to do.

那么,什么是我选择这里?在哪个方向,我可以搜索得到接近我想要做什么?

So what are my options here? In which direction could I search to get closer to what I want to do?

编辑:

我看了看这个问题(很相似):<一href=\"http://stackoverflow.com/questions/9923608/can-i-achieve-a-copy-constructor-in-c-sharp-that-copies-from-a-derived-class?rq=1\">Can我实现了C#中的拷贝构造函数,从派生类副本?
但它所做的工作属性的副本,我想避免这种情况。
在这篇文章的最后答案有一些关于 PopulateMetadata ,但我找不到在基础供应商,功能...

I looked at this question (quite similar): Can I achieve a 'copy constructor' in C# that copies from a derived class? but what it does is a copy of properties and I wanted to avoid that. In the last answer in this post there's something about PopulateMetadata but I can't find that function in the base provider...

推荐答案

在一些思考,我竟然发现,这并不需要使用 MvcExtensions 和复制的解决方案包含在基类派生的一个的所有信息。

After some thinking, I actually found a solution that doesn't need to use MvcExtensions and that copies all the information contained in the base class to the derived one.

由于 DataAnnotationsModelMetadata 及其基类没有做任何事情,但初始化一些私有或保护值,也没有有副作用,这样做的风险。

Since DataAnnotationsModelMetadata and its base classes don't do anything but initialize some private or protected values, there is no risk of having side effects to do that.

public class ArtifyModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
    private static List<Tuple<FieldInfo, FieldInfo>> _fieldsMap;

    static ArtifyModelMetaDataProvider()
    {
        _fieldsMap = new List<Tuple<FieldInfo, FieldInfo>>();
        foreach (FieldInfo customFI in GetAllFields(typeof(ArtifyModelMetadata)))
            foreach (FieldInfo baseFI in GetAllFields(typeof(DataAnnotationsModelMetadata)))
                if (customFI.Name == baseFI.Name)
                    _fieldsMap.Add(new Tuple<FieldInfo, FieldInfo>(customFI, baseFI));
    }

    private static List<FieldInfo> GetAllFields(Type t)
    {
        List<FieldInfo> res = new List<FieldInfo>();

        while (t != null)
        {
            foreach (FieldInfo fi in t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                if (!fi.IsLiteral)
                    res.Add(fi);
            t = t.BaseType;
        }

        return res;
    }

    private static void CopyToCustomMetadata(ModelMetadata baseMetadata, ArtifyModelMetadata customMetadata)
    {
        foreach (Tuple<FieldInfo, FieldInfo> t in _fieldsMap)
            t.Item1.SetValue(customMetadata, t.Item2.GetValue(baseMetadata));
    }

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
    {
        ArtifyModelMetadata modelMetadata = new ArtifyModelMetadata(this, containerType, modelAccessor, modelType, propertyName);
        CopyToCustomMetadata(base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName), modelMetadata);

        ModelMetadataAttribute mma;
        Dictionary<string, string> htmlAttributes;
        object tmp;
        foreach (Attribute a in attributes)
        {
            mma = a as ModelMetadataAttribute;
            if (mma != null)
            {
                mma.Process(modelMetadata);
                htmlAttributes = mma.GetAdditionnalHtmlAttributes();

                if (htmlAttributes != null)
                {
                    foreach (KeyValuePair<string, string> p in htmlAttributes)
                    {
                        tmp = null;
                        tmp = modelMetadata.AdditionnalHtmlAttributes.TryGetValue(p.Key, out tmp);
                        if (tmp == null)
                            modelMetadata.AdditionnalHtmlAttributes.Add(p.Key, p.Value);
                        else
                            modelMetadata.AdditionnalHtmlAttributes[p.Key] = tmp.ToString() + " " + p.Value;
                    }
                }
            }
            if (mma is TooltipAttribute)
                modelMetadata.HasToolTip = true;
        }

        return modelMetadata;
    }
}

public class ArtifyModelMetadata : DataAnnotationsModelMetadata
{

    public bool HasToolTip { get; internal set; }

    public Dictionary<string, object> AdditionnalHtmlAttributes { get; private set; }

    public ArtifyModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
        : base(provider, containerType, modelAccessor, modelType, propertyName, null)
    {
        AdditionnalHtmlAttributes = new Dictionary<string, object>();
    }
}

如果你想有一个通用的解决方案来获得基类的字段在派生一个,你不能只用继承,因为你在同一种情况我是,只要使用这个类:

And if you want a generic solution to get base class fields in your derived one, and you can't just use inheritance because you're in the same kind of situation I was, just use this class:

public abstract class GenericBaseCopy<Src, Dst> where Dst : Src
{
    private static List<Tuple<FieldInfo, FieldInfo>> _fieldsMap;

    static GenericBaseCopy()
    {
        _fieldsMap = new List<Tuple<FieldInfo, FieldInfo>>();
        foreach (FieldInfo customFI in GetAllFields(typeof(Dst)))
            foreach (FieldInfo baseFI in GetAllFields(typeof(Src)))
                if (customFI.Name == baseFI.Name)
                    _fieldsMap.Add(new Tuple<FieldInfo, FieldInfo>(customFI, baseFI));
    }

    private static List<FieldInfo> GetAllFields(Type t)
    {
        List<FieldInfo> res = new List<FieldInfo>();

        while (t != null)
        {
            foreach (FieldInfo fi in t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                if (!fi.IsLiteral)
                    res.Add(fi);
            t = t.BaseType;
        }

        return res;
    }

    public static void Copy(Src baseClassInstance, Dst dstClassInstance)
    {
        foreach (Tuple<FieldInfo, FieldInfo> t in _fieldsMap)
            t.Item1.SetValue(dstClassInstance, t.Item2.GetValue(baseClassInstance));
    }
}

这篇关于如何延长DataAnnotationsModelMetadata的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆