如何延长DataAnnotationsModelMetadata [英] How can I extend DataAnnotationsModelMetadata
问题描述
我有一个自定义的 DataAnnotationsModelMetadataProvider
,我想返回从 ModelMetadata code>派生的对象,这样我可以有额外的属性在我的剃须刀模板。
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 code>功能:
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 code>在视图中包含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 code>继承。
So I need a class that inherits from DataAnnotationsModelMetadata
.
我不能随便叫 base.CreateMetadata code>函数,因为它将无法正常投我的派生类。
I can't just call the base.CreateMetadata
function since it won't cast properly to my derived class.
我想过使得由 base.CreateMetadata code>函数返回到我的派生
DataAnnotationsModelMetadata code>的公共属性的副本一流的,但我可以失去信息,这样看起来很安全不。
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>的基础上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 code>,但我找不到在基础供应商,功能...
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 code>及其基类没有做任何事情,但初始化一些私有或保护值,也没有有副作用,这样做的风险。
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屋!