Apakah mungkin untuk memiliki dua kelas parsial dalam majelis yang berbeda mewakili kelas yang sama?

128

Saya memiliki kelas yang disebut 'Artikel' dalam proyek yang disebut 'MyProject.Data', yang bertindak sebagai lapisan data untuk aplikasi web saya.

Saya memiliki proyek terpisah yang disebut 'MyProject.Admin', yang merupakan sistem admin berbasis web untuk melihat / mengedit data, dan dibangun menggunakan ASP.NET Dynamic Data.

Pada dasarnya saya ingin memperluas kelas Artikel, menggunakan kelas parsial, sehingga saya dapat menambah salah satu propertinya dengan ekstensi "UIHint", yang akan memungkinkan saya untuk mengganti kotak teks multi-baris normal dengan kontrol FCKEdit.

Kelas parsial dan ekstender saya akan terlihat seperti ini:

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

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

Sekarang ini semua berfungsi dengan baik jika kelas parsial dalam proyek yang sama dengan kelas parsial asli - yaitu proyek MyProject.Data.

Tapi perilaku UI seharusnya tidak duduk di lapisan Data, melainkan di lapisan Admin. Jadi saya ingin memindahkan kelas ini ke MyProject.Admin.

Namun, jika saya melakukan itu, fungsinya hilang.

Pertanyaan mendasar saya adalah: dapatkah saya memiliki 2 kelas parsial dalam proyek terpisah, tetapi keduanya merujuk pada "kelas" yang sama?

Jika tidak, apakah ada cara untuk mencapai apa yang saya coba lakukan, tanpa mencampur logika data-layer dengan logika UI?

Jonathan
sumber
1
Inilah mengapa konsep MetadataType berbau. ( en.wikipedia.org/wiki/Code_smell ). Ini adalah solusi yang benar-benar cacat - Anda mencoba membangun MVC yang secara khusus memisahkan model dari tampilan dari pengontrol dan Anda memerlukan logika tampilan dan validasi di kelas data. Beraneka ragam. Seharusnya ada cara yang lebih baik untuk menerapkan atribut-atribut ini. Anda harus dapat mengaitkan kelas metadata dengan kelas data menggunakan API yang lancar atau yang serupa. Seharusnya tidak dipanggang masuk
Jim
Beberapa jawaban lain menyebutkan ini: Jika ini merupakan keharusan mutlak, dan Anda memiliki sumber rakitan yang direferensikan, Anda selalu dapat menyertakan model sumber sebagai file tertaut (tombol terpisah pada pemilih file Add-Existing-Item) sehingga mereka dibuat dengan mengkonsumsi alih-alih perakitan ref. (Strategi serupa untuk mengekspos lapisan Model / Data Anda melalui WCF dengan Referensi Layanan dan memperluas kelas kode gen parsial itu.) Anda tidak pernah dipaksa untuk menghancurkan lapisan - Anda selalu dapat subkelas. Dan MetadataTypemembuat Model lebih seperti ViewModels.
JoeBrockhaus
Sudah terlambat untuk merespon, tetapi saya telah memberikan solusi di sini
Usman
Saya tahu sudah terlambat untuk merespons, tetapi di sini saya telah menyajikan solusi.
Usman

Jawaban:

178

Tidak, Anda tidak dapat memiliki dua kelas parsial yang merujuk ke kelas yang sama dalam dua majelis (proyek) yang berbeda. Setelah perakitan dikompilasi, meta-data dimasukkan, dan kelas Anda tidak lagi parsial. Kelas parsial memungkinkan Anda untuk membagi definisi kelas yang sama menjadi dua file.

Darin Dimitrov
sumber
15

Seperti dicatat, kelas parsial adalah fenomena waktu kompilasi, bukan runtime. Kelas dalam majelis menurut definisi selesai.

Dalam istilah MVC, Anda ingin memisahkan kode tampilan dari kode model, namun mengaktifkan jenis UI tertentu berdasarkan properti model. Lihatlah ikhtisar Martin Fowler yang sangat baik tentang berbagai rasa MVC, MVP, dan yang lainnya: Anda akan menemukan banyak ide desain. Saya kira Anda juga bisa menggunakan Dependency Injection untuk memberi tahu UI kontrol seperti apa yang layak untuk entitas dan atribut individual.

Tujuan Anda untuk memisahkan masalah adalah luar biasa; tetapi sebagian kelas dimaksudkan untuk mengatasi masalah yang sama sekali berbeda (terutama dengan pembuatan kode dan bahasa pemodelan desain-waktu).

Pontus Gagge
sumber
8

Metode ekstensi dan ViewModels adalah cara standar untuk memperluas objek lapisan data di frontend seperti ini:

Lapisan Data (perpustakaan kelas, Person.cs):

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

Lapisan Tampilan (aplikasi 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 (untuk data khusus tampilan diperluas):

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

Pengontrol PersonController.cs:

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

  return View(model);
}

Lihat, 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
sumber
Komentar Extensions cukup masuk akal, ini dapat sepenuhnya dipisahkan dari objek Person dengan menggunakan antarmuka. Saya suka itu!
Pale Ale
2

Tambahkan file dasar sebagai file yang ditautkan ke proyek Anda. Itu masih sebagian tetapi karena memungkinkan Anda untuk membagikannya di antara kedua proyek, tetap disinkronkan dan pada saat yang sama memiliki kode versi / kerangka kerja di kelas parsial.

leon
sumber
1

Saya punya masalah serupa dengan ini. Saya menjaga kelas parsial saya di proyek Data saya sehingga dalam kasus Anda 'MyProject.Data'. MetaDataClasses tidak boleh masuk dalam proyek Admin Anda karena Anda akan membuat referensi melingkar bijaksana.

Saya menambahkan proyek Class Lib baru untuk MetaDataClasses saya mis. 'MyProject.MetaData' dan kemudian merujuk ini dari proyek Data saya

Indy
sumber
1

Mungkin menggunakan kelas ekstensi statis.

Braneloc
sumber
Ide bagus. Bisakah Anda memberikan contoh apa yang menurut Anda akan memberikan fungsionalitas yang cukup dalam jawaban Anda?
pvanhouten
0

Saya mungkin salah di sini, tetapi tidak bisakah Anda mendefinisikan kelas ProjectMetaData di proyek MyProject.Admin Anda?

Darragh
sumber
0

Cukup tambahkan file kelas sebagai tautan di proyek baru Anda dan simpan namespace yang sama di kelas parsial Anda.

nl20121974
sumber
0

Sejak 2019 Anda dapat memiliki 2 bagian kelas parsial di majelis yang berbeda menggunakan trik. Trik ini dijelaskan dan diperlihatkan dalam artikel ini:

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

Ini menggunakan pada intinya ekstensi MSBuild.Sdk.Extras untuk proyek-proyek seperti SDK, yang memecahkan keterbatasan memiliki semua bagian parsial dari sebuah kelas dalam perakitan yang sama, dengan menggunakan satu proyek dengan target simultan yang banyak, secara efektif menciptakan majelis multipel dalam satu kompilasi dari proyek yang sama.

Softlion
sumber