É possível ter duas classes parciais em assemblies diferentes, representando a mesma classe?

128

Eu tenho uma classe chamada 'Artigo' em um projeto chamado 'MyProject.Data', que atua como a camada de dados do meu aplicativo Web.

Eu tenho um projeto separado chamado 'MyProject.Admin', que é um sistema de administração baseado na Web para exibir / editar os dados, e foi construído usando o ASP.NET Dynamic Data.

Basicamente, quero estender a classe Article, usando uma classe parcial, para poder aumentar uma de suas propriedades com um extensor "UIHint", o que me permitirá substituir a caixa de texto multilinhas normal por um controle FCKEdit.

Minha classe parcial e extensor ficariam assim:

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

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

Agora, tudo isso funciona bem se a classe parcial estiver no mesmo projeto que a classe parcial original - ou seja, o projeto MyProject.Data.

Mas o comportamento da interface do usuário não deve ficar na camada Dados, mas na camada Admin. Então, eu quero mover essa classe para MyProject.Admin.

No entanto, se eu fizer isso, a funcionalidade será perdida.

Minha pergunta fundamental é: posso ter 2 classes parciais em projetos separados, mas ambas se referindo à mesma "classe"?

Caso contrário, existe uma maneira de realizar o que estou tentando fazer, sem misturar a lógica da camada de dados com a lógica da interface do usuário?

Jonathan
fonte
1
É exatamente por isso que o conceito de MetadataType fede. ( en.wikipedia.org/wiki/Code_smell ). É uma solução completamente falha - Você está tentando criar o MVC que separa especificamente o modelo da visualização do controlador e precisa de lógica de visualização e validação nas classes de dados. Rediculous. Deve haver uma maneira melhor de aplicar esses atributos. Você deve poder associar uma classe de metadados a uma classe de dados usando uma API fluente ou algo semelhante. Ele não deve ser cozido em.
Jim
Algumas outras respostas mencionam o seguinte: Se for uma necessidade absoluta e você possuir a fonte de montagem referenciada, sempre poderá incluir os modelos de origem como arquivos vinculados (botão de divisão no seletor de arquivo Adicionar item existente) para que sejam criados com o consumindo em vez de montagem ref. (Estratégia semelhante à de expor sua camada de Modelo / Dados via WCF com uma Referência de Serviço e estender essas classes parciais de geração de código.) Você nunca é forçado a esmagar camadas - sempre pode subclassificar. E MetadataTypetorna os modelos mais parecidos com o ViewModels.
JoeBrockhaus
Sua tarde demais para responder, mas eu tenho fornecer uma solução aqui
Usman
Sei que é tarde demais para responder, mas aqui apresentei uma solução.
Usman 20/05

Respostas:

178

Não, você não pode ter duas classes parciais referentes à mesma classe em duas montagens diferentes (projetos). Depois que o assembly é compilado, os metadados são inseridos e suas classes não são mais parciais. Classes parciais permitem dividir a definição da mesma classe em dois arquivos.

Darin Dimitrov
fonte
15

Como observado, as classes parciais são um fenômeno em tempo de compilação, não em tempo de execução. As aulas em montagens são, por definição, completas.

Em termos de MVC, você deseja manter o código de exibição separado do código do modelo, mas habilitar certos tipos de interface do usuário com base nas propriedades do modelo. Confira a excelente visão geral de Martin Fowler dos diferentes tipos de MVC, MVP e outros enfeites: você encontrará muitas idéias de design. Suponho que você também possa usar a Injeção de Dependência para informar à interface do usuário que tipo de controle é viável para entidades e atributos individuais.

Seu objetivo de separar as preocupações é ótimo; mas as classes parciais destinavam-se a resolver problemas completamente diferentes (principalmente com a geração de código e as linguagens de modelagem em tempo de design).

Pontus Gagge
fonte
8

Os métodos de extensão e o ViewModels são a maneira padrão de estender os objetos da camada de dados no front-end desta maneira:

Camada de dados (biblioteca de classes, Person.cs):

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

Camada de exibição (aplicativo da 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 dados específicos de exibição estendidos):

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

Controlador PersonController.cs:

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

  return View(model);
}

View, 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
fonte
O comentário sobre extensões faz bastante sentido; isso pode ser completamente dissociado do objeto Person usando uma interface. Eu gosto disso!
Pale Ale
2

Adicione o arquivo base como um arquivo vinculado aos seus projetos. Ainda é parcial, mas permite compartilhar entre os dois projetos, mantê-los sincronizados e, ao mesmo tempo, ter código específico da versão / estrutura nas classes parciais.

leon
fonte
1

Eu tive problemas semelhantes com isso. Eu mantive minhas classes parciais no meu projeto Data, portanto, no seu caso, o 'MyProject.Data'. O MetaDataClasses não deve entrar no seu projeto de administrador, pois você criará uma referência circular de outra maneira.

Adicionei um novo projeto de Classe Lib para minhas MetaDataClasses, por exemplo, 'MyProject.MetaData' e, em seguida, referenciei isso no meu projeto Data

Indy
fonte
1

Talvez use uma classe de extensão estática.

Braneloc
fonte
Boa ideia. Você pode fornecer um exemplo do que você acha que forneceria funcionalidade suficiente em sua resposta?
pvanhouten
0

Posso estar enganado aqui, mas você não pode simplesmente definir a classe ProjectMetaData no seu projeto MyProject.Admin?

Darragh
fonte
0

Basta adicionar o arquivo de classe como link em seu novo projeto e manter o mesmo espaço para nome em sua classe parcial.

nl20121974
fonte
0

Desde 2019, você pode ter 2 partes de uma classe parcial em montagens diferentes usando um truque. Este truque é explicado e demonstrado neste artigo:

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

Ele usa em seu núcleo a extensão MSBuild.Sdk.Extras para projetos do tipo SDK, que resolve a limitação de ter todas as partes parciais de uma classe no mesmo assembly, usando um projeto com vários destinos simultâneos, criando efetivamente vários assemblies em uma compilação do mesmo projeto.

Softlion
fonte