¿Es posible tener dos clases parciales en diferentes ensamblados que representan la misma clase?

128

Tengo una clase llamada 'Artículo' en un proyecto llamado 'MyProject.Data', que actúa como la capa de datos para mi aplicación web.

Tengo un proyecto separado llamado 'MyProject.Admin', que es un sistema de administración basado en la web para ver / editar los datos, y fue construido usando ASP.NET Dynamic Data.

Básicamente, quiero extender la clase Article, usando una clase parcial, para poder aumentar una de sus propiedades con un extensor "UIHint", lo que me permitirá reemplazar el cuadro de texto normal de varias líneas con un control FCKEdit.

Mi clase parcial y extensor se verían así:

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

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

Ahora todo esto funciona bien si la clase parcial está en el mismo proyecto que la clase parcial original, es decir, el proyecto MyProject.Data.

Pero el comportamiento de la interfaz de usuario no debería estar en la capa de datos, sino en la capa de administración. Entonces quiero mover esta clase a MyProject.Admin.

Sin embargo, si hago eso, la funcionalidad se pierde.

Mi pregunta fundamental es: ¿puedo tener 2 clases parciales en proyectos separados, pero ambas se refieren a la misma "clase"?

Si no, ¿hay alguna manera de lograr lo que estoy tratando de hacer, sin mezclar la lógica de la capa de datos con la lógica de la interfaz de usuario?

Jonathan
fuente
1
Esta es precisamente la razón por la cual el concepto de MetadataType apesta. ( en.wikipedia.org/wiki/Code_smell ). Es una solución completamente defectuosa: está intentando crear MVC que separa específicamente el modelo de la vista del controlador y necesita una lógica de vista y validación en las clases de datos. Ridícula. Debería haber una mejor manera de aplicar estos atributos. Debería poder asociar una clase de metadatos con una clase de datos utilizando una API fluida o algo similar. No debe hornearse.
Jim
Algunas otras respuestas mencionan esto: si es una necesidad absoluta, y usted es el propietario del origen de ensamblaje al que se hace referencia, siempre puede incluir los modelos de origen como archivos vinculados (botón dividido en el selector de archivo Agregar elemento existente) para que se creen con el Consumo en lugar de montaje ref. (Estrategia similar a exponer su capa de Modelo / Datos a través de WCF con una Referencia de servicio y extender esas clases de código parcial generadas). Nunca se ve obligado a romper capas: siempre puede subclasificar. Y MetadataTypehace que los modelos se parezcan más a ViewModels.
JoeBrockhaus
Es demasiado tarde para responder, pero he proporcionado una solución aquí
Usman
Sé que es demasiado tarde para responder, pero aquí he presentado una solución.
Usman

Respuestas:

178

No, no puede tener dos clases parciales que se refieran a la misma clase en dos ensamblajes (proyectos) diferentes. Una vez que se compila el ensamblado, los metadatos se incorporan y sus clases ya no son parciales. Las clases parciales le permiten dividir la definición de la misma clase en dos archivos.

Darin Dimitrov
fuente
15

Como se señaló, las clases parciales son un fenómeno de tiempo de compilación, no tiempo de ejecución. Las clases en los ensambles son, por definición, completas.

En términos de MVC, desea mantener el código de vista separado del código del modelo, pero habilitar ciertos tipos de IU basados ​​en las propiedades del modelo. Echa un vistazo a la excelente descripción de Martin Fowler de los diferentes sabores de MVC, MVP y otras cosas: encontrarás muchas ideas de diseño. Supongo que también podría usar la Inyección de dependencias para decirle a la IU qué tipo de controles son viables para entidades y atributos individuales.

Su objetivo de separar las preocupaciones es excelente; pero las clases parciales estaban destinadas a abordar problemas completamente diferentes (principalmente con la generación de código y los lenguajes de modelado en tiempo de diseño).

Pontus Gagge
fuente
8

Los métodos de extensión y ViewModels son la forma estándar de extender objetos de capa de datos en la interfaz de la siguiente manera:

Capa de datos (biblioteca de clases, Person.cs):

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

Display Layer (aplicación 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 (para datos específicos de vista extendida):

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

Controller PersonController.cs:

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

  return View(model);
}

Ver, 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)
8DX
fuente
El comentario de Extensiones tiene bastante sentido, esto se puede desacoplar completamente del objeto Persona mediante una interfaz. ¡Me gusta!
Pale Ale
2

Agregue el archivo base como un archivo vinculado en sus proyectos. Todavía es parcial, pero le permite compartirlo entre ambos proyectos, mantenerlos sincronizados y al mismo tiempo tener un código específico de versión / marco en las clases parciales.

León
fuente
1

He tenido problemas similares con esto. Mantuve mis clases parciales en mi proyecto de datos, así que en su caso el 'MyProject.Data'. MetaDataClasses no debe ir en su proyecto de administración, ya que de lo contrario creará referencias circulares.

Agregué un nuevo proyecto Class Lib para mis MetaDataClasses, por ejemplo, 'MyProject.MetaData' y luego hice referencia a esto desde mi proyecto de datos

Indy
fuente
1

Quizás use una clase de extensión estática.

Braneloc
fuente
Buena idea. ¿Puede proporcionar un ejemplo de lo que cree que proporcionaría suficiente funcionalidad dentro de su respuesta?
pvanhouten
0

Puedo estar equivocado aquí, pero ¿no podría simplemente definir la clase ProjectMetaData en su proyecto MyProject.Admin?

Darragh
fuente
0

Simplemente agregue el archivo de clase como enlace en su nuevo proyecto y mantenga el mismo espacio de nombres en su clase parcial.

nl20121974
fuente
0

Desde 2019, puede tener 2 partes de una clase parcial en diferentes ensamblajes utilizando un truco. Este truco se explica y se demuestra en este artículo:

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

Utiliza en su núcleo la extensión MSBuild.Sdk.Extras para proyectos similares a SDK, que resuelve la limitación de tener todas las partes parciales de una clase en el mismo ensamblaje, mediante el uso de un proyecto con múltiples destinos simultáneos, creando efectivamente ensambles múltiples en una compilación del mismo proyecto.

Softlion
fuente