我似乎遇到了一个奇怪的问题,经过数小时的搔痒,我似乎已将问题缩小到partial classes和virtual properties的组合。当我覆盖部分类中的属性,坐在单独的文件中时,MVC会复制我视图中的字段。我正在使用Visual Studio 2013,可以通过以下步骤复制该问题:
Models
文件夹并创建一个名为MyModel.cs
的新类。 将这些行添加到新文件中:
public abstract partial class MyOriginalModel
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public partial class MyModel : MyOriginalModel
{
}
Models
文件夹并创建另一个名为MyModelCustom.cs
的新类。将这些行添加到文件中:
public partial class MyModel
{
[System.ComponentModel.DisplayName("First Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string FirstName
{
get
{
return base.FirstName;
}
set
{
base.FirstName = value;
}
}
[System.ComponentModel.DisplayName("Last Name")]
[System.ComponentModel.DataAnnotations.Required]
public override string LastName
{
get
{
return base.LastName;
}
set
{
base.LastName = value;
}
}
}
Controllers
文件夹并添加一个新控制器。选择"带有读/写操作的MVC 5控制器"并称之为NamesController
。右键单击Create方法,然后转到" Add View"。在模板下拉列表中,选择Create
,然后在“模型类”下拉列表中选择MyModel
。 MVC创建模板后,您会看到它会两次添加First Name
和Last Name
。该问题似乎与部分类有关,因为如果我将MyModelCustom.cs
的内容移动到MyModel.cs
,一切正常。但是,它不仅仅是部分类。如果我在分部类中创建一个新属性(而不是重载一个),则它不会复制该属性。所以它似乎是部分类和重写虚拟属性的组合。
有人可以确认这是一个错误还是我做错了什么?
答案 0 :(得分:3)
这两者都有。是否存在错误,如果MVC脚手架不正确,您将不得不经常对抗框架或改变您对问题的处理方法。
作为一般规则,我发现当你必须对抗MVC框架以使其按照你想要的方式运行时,那么改变你对问题的处理方法要容易得多。否则,你将最终反复战斗,直到你最终遵守。从那些以艰难的方式吸取教训的人那里拿走它。
考虑到更简单的方法,您可以尝试以下几种方法:
如果要覆盖很多属性,请使用属性的常用名称(FirstName,LastName)创建单独的类。然后使用Best way to clone properties of disparate objects编组对象之间的数据。
您还可以使用Fody PropertyChange侦听器来处理这些值更改时所需的逻辑,从而完全不需要部分覆盖。
最后一个选项是override the scaffolding templates跳过被覆盖的属性。不知道你怎么会发现它。
答案 1 :(得分:2)
查看MvcScaffolding的EnvDTETypeLocator.cs
的CodePlex来源 /// <summary>
/// Out of a set of CodeType instances, some of them may be different partials of the same class.
/// This method filters down such a set so that you get only one partial per class.
/// </summary>
private static List<CodeType> PickArbitraryRepresentativeOfPartialClasses(IEnumerable<CodeType> codeTypes)
{
var representatives = new List<CodeType>();
foreach (var codeType in codeTypes) {
var codeClass2 = codeType as CodeClass2;
if (codeClass2 != null) {
var matchesExistingRepresentative = (from candidate in representatives.OfType<CodeClass2>()
let candidatePartials = candidate.PartialClasses.OfType<CodeClass2>()
where candidatePartials.Contains(codeClass2)
select candidate).Any();
if (!matchesExistingRepresentative)
representatives.Add(codeType);
} else {
// Can't have partials because it's not a CodeClass2, so it can't clash with others
representatives.Add(codeType);
}
}
return representatives;
}
}
1) PickArbitraryRepresentativeOfPartialClasses
,该方法使用Linq any()
确认codeType as CodeClass2
有成员。
CodeClass2 是 EnvDTE 的部分类类型,Visual Studio的核心自动化库负责IDE代码生成(设计)时间反思)。
2)如果投放为CodeClass2
的类确实有成员,则该类会添加到representatives
3)评估部分类时,将在不同的上下文中访问每个文件(通常会导致应该覆盖的元素的合并)
运行时反射和设计时反思之间的一个有趣区别: sic
ASP.NET控件具有两组不同的功能,用于在页面内的运行时执行或在主机设计器内的设计时使用。运行时功能根据配置确定控件输出的标记。相反,设计时功能受益于可视化设计器,例如Microsoft Visual Studio 2005.设计时功能让页面作者在声明和WYSIWYG中配置运行时控件(你看到的是什么) - 你得到的方式。
<强>结论:强>
MVC Scaffolding确实使用了反射,但它的设计时间反射却不那么可靠。
设计时间反射与运行时反射不同。完全编译的类是继承解析和部分组合和优先级解析的最终结果。设计时间反射可以最好地猜测如何使用复杂的多部件类型。
如果你想依靠脚手架,最好不要限制它。当你遇到这样的错误时,请尝试简化你的ViewModel: