Czy jest możliwe, aby dwie klasy częściowe w różnych zestawach reprezentowały tę samą klasę?

130

Mam klasę o nazwie „Artykuł” w projekcie o nazwie „MyProject.Data”, która działa jako warstwa danych dla mojej aplikacji internetowej.

Mam osobny projekt o nazwie „MyProject.Admin”, który jest internetowym systemem administracyjnym do przeglądania / edycji danych i został zbudowany przy użyciu ASP.NET Dynamic Data.

Zasadniczo chcę rozszerzyć klasę Article, używając klasy częściowej, aby móc rozszerzyć jedną z jej właściwości o rozszerzenie „UIHint”, które pozwoli mi zastąpić zwykłe wielowierszowe pole tekstowe kontrolką FCKEdit.

Moja klasa częściowa i ekstender wyglądałyby tak:

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

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

Teraz wszystko działa dobrze, jeśli klasa częściowa znajduje się w tym samym projekcie co oryginalna klasa częściowa - tj. Projekt MyProject.Data.

Jednak zachowanie interfejsu użytkownika nie powinno znajdować się w warstwie danych, ale raczej w warstwie administracyjnej. Dlatego chcę przenieść tę klasę do MyProject.Admin.

Jeśli jednak to zrobię, utracę funkcjonalność.

Moje podstawowe pytanie brzmi: czy mogę mieć 2 klasy cząstkowe w oddzielnych projektach, ale obie odnoszące się do tej samej „klasy”?

Jeśli nie, czy istnieje sposób, aby osiągnąć to, co próbuję zrobić, bez mieszania logiki warstwy danych z logiką interfejsu użytkownika?

Jonathan
źródło
1
Właśnie dlatego koncepcja MetadataType śmierdzi. ( en.wikipedia.org/wiki/Code_smell ). Jest to całkowicie wadliwe rozwiązanie - próbujesz zbudować MVC, który oddziela model od widoku od kontrolera i potrzebujesz logiki widoku i walidacji w klasach danych. Śmieszne. Powinien istnieć lepszy sposób stosowania tych atrybutów. Powinieneś być w stanie skojarzyć klasę metadanych z klasą danych za pomocą płynnego interfejsu API lub czegoś podobnego. Nie należy go upiec.
Jim,
Niektóre inne odpowiedzi wspominają o tym: Jeśli jest to absolutnie konieczne i jesteś właścicielem źródła zespołu, do którego się odwołujesz, zawsze możesz dołączyć modele źródłowe jako pliki połączone (przycisk podziału na selektorze plików Dodaj istniejący element), aby były one budowane za pomocą zużywający zamiast montażu ref. (Podobna strategia do ujawniania warstwy modelu / danych za pośrednictwem WCF z odwołaniem do usługi i rozszerzania tych klas częściowych wygenerowanych kodu). Nigdy nie jesteś zmuszony do niszczenia warstw - zawsze możesz podklasę. I MetadataTypesprawia, że ​​modele bardziej przypominają ViewModels.
JoeBrockhaus
Za późno na odpowiedź, ale mam rozwiązanie tutaj
Usman
Wiem, że jest za późno na odpowiedź, ale tutaj przedstawiłem rozwiązanie.
Usman

Odpowiedzi:

179

Nie, nie można mieć dwóch klas częściowych odwołujących się do tej samej klasy w dwóch różnych zespołach (projektach). Po skompilowaniu zestawu metadane są zapisywane, a Twoje klasy nie są już częściowe. Klasy częściowe umożliwiają podzielenie definicji tej samej klasy na dwa pliki.

Darin Dimitrov
źródło
15

Jak już wspomniano, klasy częściowe są zjawiskiem czasu kompilacji, a nie środowiska wykonawczego. Klasy w zespołach są z definicji kompletne.

W terminologii MVC chcesz, aby kod widoku był oddzielony od kodu modelu, ale włączał określone rodzaje interfejsu użytkownika na podstawie właściwości modelu. Sprawdź doskonały przegląd różnych smaków MVC, MVP i tak dalej Martina Fowlera : znajdziesz mnóstwo pomysłów na projekt. Przypuszczam, że można również użyć funkcji Dependency Injection, aby powiedzieć interfejsowi użytkownika, jakiego rodzaju kontrolki są wykonalne dla poszczególnych jednostek i atrybutów.

Twój cel oddzielenia obaw jest świetny; ale klasy częściowe były przeznaczone do rozwiązywania zupełnie innych problemów (głównie z generowaniem kodu i językami modelowania w czasie projektowania).

Pontus Gagge
źródło
8

Metody rozszerzeń i modele widoków są standardowym sposobem rozszerzania obiektów warstwy danych w interfejsie użytkownika w następujący sposób:

Warstwa danych (biblioteka klas, Person.cs):

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

Warstwa wyświetlania (aplikacja internetowa) 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 (dla rozszerzonych danych specyficznych dla widoku):

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

Kontroler PersonController.cs:

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

  return View(model);
}

Widok, 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
źródło
Komentarz Rozszerzenia ma trochę sensu, można go całkowicie oddzielić od obiektu Person za pomocą interfejsu. Lubię to!
Pale Ale
2

Dodaj plik podstawowy jako plik połączony do swoich projektów. Jest to nadal częściowe, ale pozwala na udostępnianie go między obydwoma projektami, utrzymywanie ich zsynchronizowania i jednocześnie posiadanie kodu specyficznego dla wersji / platformy w klasach częściowych.

leon
źródło
1

Miałem z tym podobne problemy. Zachowałem klasy częściowe w moim projekcie Data, czyli w twoim przypadku „MyProject.Data”. MetaDataClasses nie powinny trafiać do projektu administratora, ponieważ w inny sposób utworzysz cykliczne odwołania.

Dodałem nowy projekt Class Lib dla moich MetaDataClasses, np. „MyProject.MetaData”, a następnie odwołałem się do tego z mojego projektu Data

Indy
źródło
1

Być może użyj statycznej klasy rozszerzenia.

Braneloc
źródło
Dobry pomysł. Czy możesz podać przykład tego, co Twoim zdaniem zapewniłoby wystarczającą funkcjonalność w Twojej odpowiedzi?
pvanhouten
1

Po prostu dodaj plik klasy jako łącze w nowym projekcie i zachowaj tę samą przestrzeń nazw w klasie częściowej.

nl20121974
źródło
0

Mogę się mylić, ale czy nie mógłbyś po prostu zdefiniować klasy ProjectMetaData w swoim projekcie MyProject.Admin?

Darragh
źródło
0

Od 2019 możesz mieć 2 części częściowej klasy w różnych zespołach za pomocą sztuczki. Ta sztuczka została wyjaśniona i zademonstrowana w tym artykule:

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

Wykorzystuje w swej istocie rozszerzenie MSBuild.Sdk.Extras do projektów podobnych do SDK, które rozwiązuje ograniczenia związane z posiadaniem wszystkich częściowych części klasy w tym samym zestawie, używając jednego projektu z wieloma jednoczesnymi celami, skutecznie tworząc zestawy wielokrotne w jednej kompilacji tego samego projektu.

Softlion
źródło