【发布时间】:2014-07-03 06:32:12
【问题描述】:
我开发了一个 MVC 帮助器来生成显示和可编辑表(需要一个 jquery 插件来允许动态添加和删除可编辑表中具有完整回发的行)例如
@Htm.TableDisplayFor(m => m.MyCollection as ICollection)
与属性结合使用将包括页脚中的总计,为查看和编辑链接添加列,为复杂类型呈现超链接等。
[TableColumn(IncludeTotals = true)]
我即将在 CodeProject 上发布它,但在此之前,我想解决一个问题。
助手首先从表达式中获取ModelMetadata,检查它是否实现了ICollection,然后获取集合中的类型(注意以下sn-p来自SO上接受的答案,但如下所述,并不完全正确)
if (collection.GetType().IsGenericType)
{
Type type = collection.GetType().GetGenericArguments()[0]
该类型用于为表头(表中可能没有任何行)和表体中的每一行生成ModelMetadata(以防某些项目是具有附加属性的继承类型,否则会搞砸向上列布局)
foreach (var item in collection)
{
ModelMetadata itemMetadata = ModelMetadataProviders.Current
.GetMetadataForType(() => item, type);
我希望能够使用IEnumerable 而不是ICollection,这样就不需要在linq 表达式上调用.ToList()。
在大多数情况下IEnumerable 工作正常,例如
IEnumerable items = MyCollection.Where(i => i....);
可以,因为.GetGenericArguments() 返回一个仅包含一种类型的数组。
问题是某些查询上的 '.GetGenericArguments()' 返回 2 种或更多类型,并且似乎没有逻辑顺序。例如
IEnumerable items = MyCollection.OrderBy(i => i...);
返回 [0] 集合中的类型,以及 [1] 用于排序的类型。
在这种情况下,.GetGenericArguments()[0] 仍然有效,但是
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
返回[0]原始集合中的类型和[1]AnotherItem的类型
所以.GetGenericArguments()[1] 是我为AnotherItem 呈现表格所需要的。
我的问题是,有没有一种可靠的方法使用条件语句来获取我需要渲染表格的类型?
从我目前的测试来看,使用.GetGenericArguments().Last() 在所有情况下都有效,除了使用OrderBy() 时,因为排序键是最后一种类型。
到目前为止,我尝试过的一些事情包括忽略作为值类型的类型(OrderBy() 经常出现这种情况,但OrderBy() 查询可能使用string(可以检查)甚至更糟糕的是,一个重载 ==、 运算符的类(在这种情况下,我将无法判断哪个是正确的类型),并且我无法找到一种方法来测试该集合是否实现了 IOrderedEnumerable。
【问题讨论】:
-
所以,如果我理解的话,你提供 some
IEnumerable<T>这是一些 arbitrary LINQ 查询的结果(通常)。并且您想可靠地获得IEnumerable<T>的T部分? -
@Chris 是的,虽然它可能只是 IEnumerable(例如 ArrayList)
-
如果是这样,尝试迭代实现的接口,找到
IEnumerable<T>接口并提取T部分:var type = items.GetType().GetInterfaces().Where(t => t.IsGenericType).Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>)).Single().GetGenericArguments()[0];注意我使用Single:如果接口没有实现它失败了,或者如果它实现了两个(或更多)不同的IEnumerable<T>接口,那么它就会失败。您可以将其更改为使用FirstOrDefault或First并根据需要处理这些额外情况。 -
关于您的评论,
ArrayList未输入。您可以提取第一项并调用其GetType()方法。但是,如果它没有物品,那么您就不走运了:无法分辨。也不保证您有一组混合的对象,或者想要使用基类而不是GetType()返回的派生类。如果您愿意,如果它没有实现IEnumerable<T>(只是IEnumerable),那么您可以将其视为object类型? -
@Chris 从目前的单元测试来看,检查实现的接口看起来很有希望。
标签: c# linq modelmetadata