3

カスタムがあり、かみそりテンプレートに追加のプロパティを含めることができるように、DataAnnotationsModelMetadataProviderから派生したオブジェクトを返したいと思います。ModelMetadata

これまでのところ、私のカスタム プロバイダーはCreateMetadata関数をオーバーライドするだけです。

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

しかし、ほとんどすべての属性は主に、かみそりテンプレートで生成した html 要素に属性を追加するため、追加しModelMetadataたい属性 (およびその他のもの) の辞書をビューに含めたいと思います。

だから私はから継承するクラスが必要DataAnnotationsModelMetadataです。

base.CreateMetadata派生クラスに適切にキャストされないため、関数を呼び出すことはできません。

DataAnnotationsModelMetadata関数によって返されるのパブリック プロパティのコピーを派生クラスに作成することを考えましたbase.CreateMetadataが、情報が失われる可能性があるため、安全ではないようです。

私が考えた別の方法は、のベースコードをコピーして貼り付けCreateMetadataてロジックを追加することですが、それは醜いようです...(そして、mvc3ソースしか持っていないので、mvc4で変更された可能性があります)

また、標準のメタデータ クラスの代わりにカスタム メタデータ クラスを提供できるように、から継承することも考えましViewDataDictionnaryたが、これを行う方法についての手がかりがありません。(また、その特定の質問についてあまり掘り下げなかったことを認めます)

およびプロバイダーに関する多くの記事を見ましたが、DataAnnotations私がやろうとしていることに似たものを見つけることができませんでした。

ここで私のオプションは何ですか?自分のやりたいことに近づくためには、どの方向を探ればよいのでしょうか。

編集:

私はこの質問を見ました(非常に似ています):C#で派生クラスからコピーする「コピーコンストラクター」を実現できますか? しかし、それはプロパティのコピーであり、それを避けたかったのです。この投稿の最後の回答には何かPopulateMetadataがありますが、基本プロバイダーでその機能が見つかりません...

4

2 に答える 2

0

少し考えた後、使用する必要がなくMvcExtensions、基本クラスに含まれるすべての情報を派生クラスにコピーするソリューションを実際に見つけました。

およびその基本クラスは、いくつかのプライベートまたは保護された値を初期化する以外は何もしないためDataAnnotationsModelMetadata、それを行うことによる副作用のリスクはありません。

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>();
    }
}

そして、派生したもので基本クラスのフィールドを取得するための一般的なソリューションが必要で、私と同じような状況にあるために継承を使用できない場合は、次のクラスを使用してください。

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));
    }
}
于 2013-05-21T21:55:58.373 に答える