Jawaban:
Lapisan Repositori memberi Anda tingkat abstraksi tambahan atas akses data. Alih-alih menulis
var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();
untuk mendapatkan satu item dari database, Anda menggunakan antarmuka repositori
public interface IRepository<T>
{
IQueryable<T> List();
bool Create(T item);
bool Delete(int id);
T Get(int id);
bool SaveChanges();
}
dan telepon Get(id)
. Lapisan repositori memperlihatkan operasi CRUD dasar .
Lapisan layanan memperlihatkan logika bisnis, yang menggunakan repositori. Contoh layanan dapat terlihat seperti:
public interface IUserService
{
User GetByUserName(string userName);
string GetUserNameByEmail(string email);
bool EditBasicUserData(User user);
User GetUserByID(int id);
bool DeleteUser(int id);
IQueryable<User> ListUsers();
bool ChangePassword(string userName, string newPassword);
bool SendPasswordReminder(string userName);
bool RegisterNewUser(RegisterNewUserModel model);
}
Sementara List()
metode repositori mengembalikan semua pengguna, ListUsers()
IUserService hanya bisa mengembalikan yang lain, pengguna memiliki akses ke.
Dalam ASP.NET MVC + EF + SQL SERVER, saya memiliki alur komunikasi ini:
Tampilan <- Pengontrol -> Lapisan layanan -> Lapisan repositori -> EF -> SQL Server
Lapisan layanan -> Lapisan repositori -> EF Bagian ini beroperasi pada model.
Tampilan <- Pengontrol -> Lapisan layanan Bagian ini beroperasi pada model tampilan.
EDIT:
Contoh aliran untuk / Pesanan / ByClient / 5 (kami ingin melihat pesanan untuk klien tertentu):
public class OrderController
{
private IOrderService _orderService;
public OrderController(IOrderService orderService)
{
_orderService = orderService; // injected by IOC container
}
public ActionResult ByClient(int id)
{
var model = _orderService.GetByClient(id);
return View(model);
}
}
Ini adalah antarmuka untuk layanan pesanan:
public interface IOrderService
{
OrdersByClientViewModel GetByClient(int id);
}
Antarmuka ini mengembalikan model tampilan:
public class OrdersByClientViewModel
{
CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
IEnumerable<OrderViewModel> Orders { get; set; }
}
Ini adalah implementasi antarmuka. Ini menggunakan kelas model dan repositori untuk membuat model tampilan:
public class OrderService : IOrderService
{
IRepository<Client> _clientRepository;
public OrderService(IRepository<Client> clientRepository)
{
_clientRepository = clientRepository; //injected
}
public OrdersByClientViewModel GetByClient(int id)
{
return _clientRepository.Get(id).Select(c =>
new OrdersByClientViewModel
{
Cient = new ClientViewModel { ...init with values from c...}
Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}
}
);
}
}
IRepository<>
untuk GenericRepository<>
di perpustakaan IOC Anda. Jawaban ini sudah sangat tua. Saya pikir solusi terbaik adalah menggabungkan semua repositori dalam satu kelas yang disebut UnitOfWork
. Itu harus berisi repositori dari setiap jenis dan satu metode yang disebut SaveChanges
. Semua repositori harus berbagi satu konteks EF.
Seperti yang dikatakan Carnotaurus, repositori bertanggung jawab untuk memetakan data Anda dari format penyimpanan ke objek bisnis Anda. Seharusnya menangani kedua cara membaca dan menulis data (menghapus, memperbarui juga) dari dan ke penyimpanan.
Tujuan lapisan layanan di sisi lain adalah untuk merangkum logika bisnis ke satu tempat untuk mempromosikan penggunaan kembali kode dan pemisahan kekhawatiran. Apa artinya ini bagi saya dalam praktik ketika membangun situs MVC Asp.net adalah bahwa saya memiliki struktur ini
[Kontroler] memanggil [Layanan] yang memanggil [repositori]
Satu prinsip yang menurut saya berguna adalah menjaga logika seminimal mungkin dalam pengontrol dan repositori.
Di controller itu karena itu membantu membuat saya KERING. Ini sangat umum bahwa saya perlu menggunakan filter atau logika yang sama di tempat lain dan jika saya meletakkannya di controller saya tidak bisa menggunakannya kembali.
Dalam repositori itu karena saya ingin dapat mengganti penyimpanan saya (atau ORM) ketika ada sesuatu yang lebih baik datang. Dan jika saya memiliki logika di repositori saya harus menulis ulang logika ini ketika saya mengubah repositori. Jika repositori saya hanya mengembalikan IQueryable dan layanan melakukan penyaringan di sisi lain, saya hanya perlu mengganti pemetaan.
Sebagai contoh saya baru-baru ini mengganti beberapa repositori Linq-To-Sql saya dengan EF4 dan yang saya tetap teguh pada prinsip ini bisa diganti dalam hitungan menit. Di mana saya memiliki beberapa logika, itu hanya masalah beberapa jam saja.
onBeforeBuildBrowseQuery
dan dapat menggunakan pembuat kueri untuk mengubah kueri.
Jawaban yang diterima (dan dibalas ratusan kali) memiliki kelemahan besar. Saya ingin menunjukkan ini dalam komentar tetapi itu hanya akan terkubur di sana dalam 30 komentar sesuatu yang menunjukkan di sini.
Saya mengambil alih aplikasi perusahaan yang dibangun seperti itu dan reaksi awal saya adalah WTH ? ViewModels di lapisan layanan? Saya tidak ingin mengubah konvensi karena bertahun-tahun pengembangan telah dilakukan sehingga saya melanjutkan dengan mengembalikan ViewModels. Bocah itu berubah menjadi mimpi buruk ketika kami mulai menggunakan WPF. Kami (tim devs) selalu berkata: ViewModel yang mana? Yang asli (yang kami tulis untuk WPF) atau layanan? Mereka ditulis untuk aplikasi web dan bahkan memiliki bendera IsReadOnly untuk menonaktifkan edit di UI. Mayor, cacat utama dan semuanya karena satu kata: ViewModel !!
Sebelum Anda melakukan kesalahan yang sama, berikut adalah beberapa alasan selain cerita saya di atas:
Mengembalikan ViewModel dari lapisan layanan adalah sangat tidak, tidak. Itu seperti mengatakan:
Jika Anda ingin menggunakan layanan ini, Anda sebaiknya menggunakan MVVM dan di sini adalah ViewModel yang perlu Anda gunakan. Aduh!
Layanan membuat asumsi mereka akan ditampilkan di UI di suatu tempat. Bagaimana jika digunakan oleh aplikasi non UI seperti layanan web atau layanan windows?
Itu bahkan bukan ViewModel nyata. ViewModel nyata memiliki kemampuan untuk diamati, perintah dll. Itu hanya POCO dengan nama yang buruk. (Lihat kisah saya di atas untuk mengapa nama penting.)
Aplikasi yang mengkonsumsi lebih baik menjadi lapisan presentasi (ViewModels digunakan oleh lapisan ini) dan lebih memahami C #. Aduh lagi!
Tolong, jangan lakukan itu!
Biasanya repositori digunakan sebagai perancah untuk mengisi entitas Anda - lapisan layanan akan keluar dan sumber permintaan. Kemungkinan Anda akan meletakkan repositori di bawah lapisan layanan Anda.
Lapisan repositori diimplementasikan untuk mengakses basis data dan membantu memperluas operasi CRUD pada basis data. Sedangkan lapisan layanan terdiri dari logika bisnis aplikasi dan dapat menggunakan lapisan repositori untuk mengimplementasikan logika tertentu yang melibatkan database. Dalam suatu aplikasi, lebih baik memiliki lapisan repositori dan lapisan layanan yang terpisah. Memiliki repositori dan lapisan layanan yang terpisah membuat kode lebih modular dan memisahkan basis data dari logika bisnis.