Est-il possible que deux classes partielles dans des assemblys différents représentent la même classe?

128

J'ai une classe appelée «Article» dans un projet appelé «MyProject.Data», qui sert de couche de données pour mon application Web.

J'ai un projet séparé appelé «MyProject.Admin», qui est un système d'administration basé sur le Web pour afficher / modifier les données, et a été construit à l'aide de ASP.NET Dynamic Data.

Fondamentalement, je veux étendre la classe Article, en utilisant une classe partielle, afin que je puisse augmenter l'une de ses propriétés avec un prolongateur "UIHint", qui me permettra de remplacer la zone de texte multiligne normale par un contrôle FCKEdit.

Ma classe partielle et mon extension ressembleraient à ceci:

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

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

Maintenant, tout fonctionne correctement si la classe partielle est dans le même projet que la classe partielle d'origine - c'est-à-dire le projet MyProject.Data.

Mais le comportement de l'interface utilisateur ne doit pas se trouver dans la couche de données, mais plutôt dans la couche d'administration. Je souhaite donc déplacer cette classe vers MyProject.Admin.

Cependant, si je fais cela, la fonctionnalité est perdue.

Ma question fondamentale est: puis-je avoir 2 classes partielles dans des projets séparés, mais les deux faisant référence à la même «classe»?

Sinon, existe-t-il un moyen d'accomplir ce que j'essaie de faire, sans mélanger la logique de la couche de données avec la logique de l'interface utilisateur?

Jonathan
la source
1
C'est précisément pourquoi le concept de MetadataType pue. ( en.wikipedia.org/wiki/Code_smell ). C'est une solution complètement imparfaite - Vous essayez de créer MVC qui sépare spécifiquement le modèle de la vue du contrôleur et vous avez besoin d'une logique de vue et de validation dans les classes de données. Ridicule. Il devrait y avoir une meilleure façon d'appliquer ces attributs. Vous devriez pouvoir associer une classe de métadonnées à une classe de données en utilisant une API fluide ou quelque chose de similaire. Il ne devrait pas être cuit.
Jim
Certaines autres réponses mentionnent ceci: s'il s'agit d'un must absolu et que vous possédez la source d'assemblage référencée, vous pouvez toujours inclure les modèles source en tant que fichiers liés (bouton de division sur le sélecteur de fichier Add-Existing-Item) afin qu'ils soient construits avec le consommer au lieu d'assembler réf. (Stratégie similaire à l'exposition de votre couche modèle / données via WCF avec une référence de service et à l'extension de ces classes partielles générées par code.) Vous n'êtes jamais obligé de casser des couches - vous pouvez toujours sous-classer. Et MetadataTyperend les modèles plus similaires aux ViewModels.
JoeBrockhaus
Il est trop tard pour répondre, mais j'ai fourni une solution ici
Usman
Je sais qu'il est trop tard pour répondre, mais ici, j'ai présenté une solution.
Usman

Réponses:

178

Non, vous ne pouvez pas avoir deux classes partielles faisant référence à la même classe dans deux assemblys différents (projets). Une fois l'assembly compilé, les méta-données sont intégrées et vos classes ne sont plus partielles. Les classes partielles vous permettent de diviser la définition de la même classe en deux fichiers.

Darin Dimitrov
la source
15

Comme indiqué, les classes partielles sont un phénomène au moment de la compilation, et non à l'exécution. Les classes dans les assemblages sont par définition complètes.

En termes MVC, vous souhaitez conserver le code de vue séparé du code de modèle, tout en activant certains types d'interface utilisateur en fonction des propriétés du modèle. Découvrez l'excellent aperçu de Martin Fowler des différentes saveurs de MVC, MVP et autres: vous trouverez de nombreuses idées de design. Je suppose que vous pouvez également utiliser l' injection de dépendances pour indiquer à l'interface utilisateur quels types de contrôles sont viables pour les entités et attributs individuels.

Votre objectif de séparer les préoccupations est grand; mais les classes partielles étaient destinées à résoudre des problèmes entièrement différents (principalement avec la génération de code et les langages de modélisation au moment du design).

Pontus Gagge
la source
8

Les méthodes d'extension et ViewModels sont le moyen standard d'étendre les objets de couche de données dans l'interface comme ceci:

Couche de données (bibliothèque 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;}
  }
}

Display Layer (application 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 (pour les données spécifiques à la vue étendue):

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

Contrôleur PersonController.cs:

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

  return View(model);
}

Vue, 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
la source
Le commentaire Extensions a un peu de sens, cela peut être complètement découplé de l'objet Person en utilisant une interface. Je l'aime!
Pale Ale
2

Ajoutez le fichier de base en tant que fichier lié dans vos projets. C'est toujours partiel, mais cela vous permet de le partager entre les deux projets, de les garder synchronisés et en même temps d'avoir du code spécifique à la version / cadre dans les classes partielles.

Léon
la source
1

J'ai eu des problèmes similaires avec cela. J'ai gardé mes classes partielles dans mon projet Data donc dans votre cas le 'MyProject.Data'. Les MetaDataClasses ne devraient pas aller dans votre projet Admin car vous créerez des références circulaires autrement.

J'ai ajouté un nouveau projet Class Lib pour mes MetaDataClasses, par exemple 'MyProject.MetaData', puis je l'ai référencé à partir de mon projet Data

Indy
la source
1

Utilisez peut-être une classe d'extension statique.

Braneloc
la source
Bonne idée. Pouvez-vous donner un exemple de ce que vous pensez fournirait une fonctionnalité suffisante dans votre réponse?
pvanhouten
0

Je peux me tromper ici, mais ne pourriez-vous pas simplement définir la classe ProjectMetaData dans votre projet MyProject.Admin?

Darragh
la source
0

Ajoutez simplement le fichier de classe comme lien dans votre nouveau projet et conservez le même espace de noms dans votre classe partielle.

nl20121974
la source
0

Depuis 2019, vous pouvez avoir 2 parties d'une classe partielle dans différents assemblages à l'aide d'une astuce. Cette astuce est expliquée et illustrée dans cet article:

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

Il utilise à la base l'extension MSBuild.Sdk.Extras aux projets de type SDK, ce qui résout la limitation d'avoir toutes les parties partielles d'une classe dans le même assembly, en utilisant un projet avec plusieurs cibles simultanées, créant efficacement plusieurs assemblys dans une compilation du même projet.

Softlion
la source