不同程序集中是否可以有两个部分类来表示同一个类?

143

我的项目中有一个名为“Article”的类,位于“MyProject.Data”项目中,它作为 Web 应用程序的数据层。

我有一个单独的项目名为“MyProject.Admin”,它是一个基于 Web 的管理系统,用于查看/编辑数据,并使用 ASP.NET Dynamic Data 构建。

基本上,我想要通过使用部分类来扩展 Article 类,这样我就可以使用“UIHint”扩展器来增强其中一个属性,使其能够使用 FCKEdit 控件替换普通的多行文本框。

我的部分类和扩展器如下所示:

[MetadataType(typeof(ProjectMetaData))]
public partial class Project
{
}

public class ProjectMetaData
{
    [UIHint("FCKeditor")]
    public object ItemDetails { get; set; }
}

如果部分类与原始的部分类在同一个项目中(即MyProject.Data项目),那么所有这些都可以正常工作。

但是 UI 行为不应该存在于数据层,而是应该存在于管理员层。因此,我想将这个类移动到 MyProject.Admin。

但是,如果我这样做,功能就会丢失。

我的根本问题是:我可以在不同的项目中拥有两个部分类,但它们都引用同一个“类”吗?

如果不能,是否有一种方法可以实现我正在尝试做的事情,而不混合数据层逻辑和 UI 逻辑?


2
这就是为什么“元数据类型”的概念让人非常不爽的原因 (http://en.wikipedia.org/wiki/Code_smell)。它是一个完全有缺陷的解决方案 - 你试图构建MVC,明确将模型与视图与控制器分开,而你需要在数据类中使用视图和验证逻辑。荒唐。应该有更好的方式来应用这些属性。你应该能够使用流畅的API或类似的东西将元数据类与数据类关联起来,而不是内置在一起。 - Jim
其他答案提到了这一点:如果这是绝对必要的,并且您拥有所引用的程序集源代码,您可以始终将源模型作为链接文件包含(在“添加现有项”文件选择器上的分割按钮),以便它们与使用而不是程序集引用一起构建。(类似于通过服务引用公开您的Model/Data层并扩展那些部分代码生成的类的策略。)您永远不会被迫破坏层次结构 - 您总是可以进行子类化。而MetadataType使Models更像ViewModels。 - JoeBrockhaus
现在回复有些晚了,但我已经提供了一个解决方案这里 - Usman
我知道回复已经太晚了,但是这里我提供了一个解决方案。 - Usman
9个回答

201

不行,你不能在两个不同的程序集(项目)中使用引用同一个类的两个部分类。一旦程序集被编译,元数据就已经固定,你的类也不再是部分类了。部分类允许你将同一类的定义分成两个文件。


17

如前所述,部分类是编译时现象,而不是运行时现象。程序集中的类在定义上是完整的。

在MVC术语中,您希望将视图代码与模型代码分开,同时基于模型属性启用某些类型的UI。请查看Martin Fowler出色的概述,了解不同风味的MVC、MVP等设计思路:您会发现无尽的设计想法。我想您也可以使用依赖注入来告诉UI哪些控件适用于各个实体和属性。

您分离关注点的目标很好;但是部分类旨在解决完全不同的问题(主要是与代码生成和设计时建模语言有关)。


9

扩展方法和ViewModel是在前端扩展数据层对象的标准方法,如下所示:

数据层(类库,Person.cs):

namespace MyProject.Data.BusinessObjects
{
  public class Person
  {
    public string Name {get; set;}
    public string Surname {get; set;}
    public string Details {get; set;}
  }
}

显示层(Web应用程序)PersonExtensions.cs:

using Data.BusinessObjects
namespace MyProject.Admin.Extensions
{
  public static class PersonExtensions
  {
    public static HtmlString GetFormattedName(this Person person)
    {
       return new HtmlString(person.Name + " <b>" + person.Surname</b>);
    }
  }
}

ViewModel(扩展视图特定数据):

using Data.BusinessObjects
namespace MyProject.Admin.ViewModels
{
  public static class PersonViewModel
  {
    public Person Data {get; set;}
    public Dictionary<string,string> MetaData {get; set;}

    [UIHint("FCKeditor")]
    public object PersonDetails { get { return Data.Details; } set {Data.Details = value;} }
  }
}

控制器 PersonController.cs:

public ActionMethod Person(int id)
{
  var model = new PersonViewModel();
  model.Data = MyDataProvider.GetPersonById(id);
  model.MetaData = MyDataProvider.GetPersonMetaData(id);

  return View(model);
}

视图,Person.cshtml:

@using MyProject.Admin.Extensions

<h1>@Model.Data.GetFormattedName()</h1>
<img src="~/Images/People/image_@(Model.MetaData["image"]).png" >
<ul>
  <li>@Model.MetaData["comments"]</li>
  <li>@Model.MetaData["employer_comments"]</li>
</ul>
@Html.EditorFor(m => m.PersonDetails)

扩展注释非常有意义,这可以通过使用接口完全与Person对象解耦。我喜欢它! - Pale Ale

2
将基础文件作为已链接文件添加到您的项目中。虽然它仍然是部分的,但它允许您在两个项目之间共享它,使它们保持同步,并且同时在部分类中具有版本/框架特定的代码。

2
也许可以使用静态扩展类。

1
好主意。你能提供一个例子,说明你认为什么样的答案可以提供足够的功能吗? - pvanhouten

1
我曾经遇到过类似的问题。我将我的部分类保存在我的数据项目中,所以在你的情况下是'MyProject.Data'。MetaDataClasses不应该放在你的管理项目中,否则会创建循环引用。
我为我的MetaDataClasses添加了一个新的类库项目,例如'MyProject.MetaData',然后从我的数据项目引用了它。

1
只需在新项目中将类文件作为链接添加并在部分类中保持相同的命名空间即可。

0

我可能错了,但是你不可以在MyProject.Admin项目中简单地定义ProjectMetaData类吗?


0

从2019年开始,你可以通过一个技巧将部分类的两个部分放在不同的程序集中。本文将解释和演示这个技巧:

https://www.notion.so/vapolia/Secret-feature-Xamarin-Forms-control-s-auto-registration-1fd6f1b0d98d4aabb2defa0eb14961fa

它的核心使用了MSBuild.Sdk.Extras扩展来支持类似SDK的项目,通过使用一个具有多个同时目标的项目来解决将类的所有部分放在同一程序集中的限制,从而有效地在同一编译中创建多个程序集。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接