Ketergantungan injeksi dengan solusi n-tier Entity Framework

12

Saat ini saya sedang merancang solusi n-tier yang menggunakan Entity Framework 5 (.net 4) sebagai strategi akses datanya, tetapi saya khawatir tentang bagaimana memasukkan injeksi ketergantungan untuk membuatnya dapat diuji / fleksibel.

Tata letak solusi saya saat ini adalah sebagai berikut (solusi saya disebut Alcatraz):

Alcatraz.WebUI : Proyek webform asp.net, antarmuka pengguna ujung depan, referensi proyek Alcatraz.Business dan Alcatraz.Data.Models .

Alcatraz.Bisnis : Proyek perpustakaan kelas, berisi logika bisnis, referensi proyek Alcatraz.Data.Access , Alcatraz.Data.Models

Alcatraz.Data.Access : Proyek perpustakaan kelas, menampung AlcatrazModel.edmx dan AlcatrazEntitiesDbContext, referensi proyek Alcatraz.Data.Models .

Alcatraz.Data.Models : Proyek perpustakaan kelas, berisi POCO untuk model Alcatraz, tanpa referensi.

Visi saya tentang bagaimana solusi ini akan bekerja adalah web-ui akan instantiate repositori dalam perpustakaan bisnis, repositori ini akan memiliki ketergantungan (melalui konstruktor) dari string koneksi (bukan sebuah AlcatrazEntitiesinstance). Web-ui akan mengetahui string koneksi database, tetapi tidak berarti bahwa itu adalah string koneksi kerangka kerja entitas.

Dalam proyek Bisnis:

public class InmateRepository : IInmateRepository
{
    private string _connectionString;

    public InmateRepository(string connectionString)
    {
        if (connectionString == null)
        {
            throw new ArgumentNullException("connectionString");
        }

        EntityConnectionStringBuilder connectionBuilder = new EntityConnectionStringBuilder();

        connectionBuilder.Metadata = "res://*/AlcatrazModel.csdl|res://*/AlcatrazModel.ssdl|res://*/AlcatrazModel.msl";
        connectionBuilder.Provider = "System.Data.SqlClient";
        connectionBuilder.ProviderConnectionString = connectionString;

        _connectionString = connectionBuilder.ToString();
    }

    public IQueryable<Inmate> GetAllInmates()
    {
        AlcatrazEntities ents = new AlcatrazEntities(_connectionString);

        return ents.Inmates;
    }
}

Di UI Web:

IInmateRepository inmateRepo = new InmateRepository(@"data source=MATTHEW-PC\SQLEXPRESS;initial catalog=Alcatraz;integrated security=True;");

List<Inmate> deathRowInmates = inmateRepo.GetAllInmates().Where(i => i.OnDeathRow).ToList();

Saya punya beberapa pertanyaan terkait tentang desain ini.

  1. Apakah desain ini bahkan masuk akal dalam hal kemampuan Entity Frameworks? Saya mendengar bahwa kerangka kerja Entity sudah menggunakan pola Unit-of-work, apakah saya hanya menambahkan lapisan abstrak yang tidak perlu?

  2. Saya tidak ingin web-ui saya berkomunikasi secara langsung dengan Entity Framework (atau bahkan merujuknya), saya ingin semua akses database melalui lapisan bisnis karena di masa depan saya akan memiliki beberapa proyek menggunakan lapisan bisnis yang sama (layanan web, aplikasi windows, dll.) dan saya ingin membuatnya mudah dikelola / diperbarui dengan memiliki logika bisnis di satu area pusat. Apakah ini cara yang tepat untuk mencapai ini?

  3. Haruskah lapisan Bisnis bahkan berisi repositori, atau haruskah itu terkandung dalam lapisan Akses? Jika di mana mereka baik-baik saja, apakah meneruskan string koneksi ketergantungan yang baik untuk diasumsikan?

Terima kasih telah meluangkan waktu untuk membaca!

Matius
sumber

Jawaban:

11

Cara Anda melakukan DI salah.

Pertama, string koneksi berada di lapisan data. Atau di file web.config.

Abstraksi berikutnya yang akan Anda hadapi adalah DbContext, bukan string koneksi. Repositori Anda seharusnya tidak tahu tentang string koneksi. Logika bisnis Anda tidak akan tahu tentang DbContext dll.

UI Anda tidak akan memiliki ide dan tidak akan membuat instantiate apapun yang terkait dengan EF.

Jawaban konkret untuk poin Anda:

  1. Jangan menambahkan abstraksi, sampai Anda sangat terbiasa dengan EF. Itu sudah menambahkan abstraksi yang bagus seperti UoW, kueri, menggunakan POCO dll.

  2. Agar DI berfungsi, Anda memiliki Root Komposisi yang mereferensikan semua komponen yang diperlukan. Ini mungkin atau mungkin tidak ada dalam proyek WebUI. Jika tidak, Anda harus berharap bahwa itu tidak merujuk EF atau teknologi terkait data lainnya.

  3. Berhenti di sini. Berhenti menambahkan abstraksi dari abstraksi. Mulailah dengan arsitektur langsung dan 'naif' dan kembangkan dari waktu ke waktu.

Abstraksi adalah alat untuk menangani kompleksitas. Tidak adanya kompleksitas berarti tidak diperlukan abstraksi (belum).

Boris Yankov
sumber
Untuk menjadi jelas bahwa saya mengerti apa yang Anda katakan: Repositori (yang antarmuka ada dalam bisnis, dan konkret ada di Alcatraz.Data.Access?) Menerima DbContextsebagai ketergantungannya. Kelas bisnis memiliki repositori sebagai ketergantungan. Untuk injeksi ketergantungan, saya melakukan ini secara manual (jadi saya mengerti apa yang terjadi). Alasan saya ingin dapat mengatur string koneksi pada DbContextadalah saya menggunakan basis data sharding sehingga dalam kasus-kasus tertentu saya perlu memiliki kerangka kerja untuk terhubung ke database yang berbeda (dari struktur yang sama). Apakah saya mengerti Anda dengan benar?
Matius
Dari kode yang Anda berikan, sepertinya Anda tidak melakukan DI sama sekali. Tujuan utama DI adalah membebaskan Anda dan kode Anda dari mengelola dependensi. Saya tidak dapat membayangkan Anda melakukannya secara manual secara efektif tanpa wadah DI.
Boris Yankov
juga menjaga pikiran Anda terbuka dengan DI. saya sudah, untuk bersenang-senang, mengajukan pertanyaan yang persis sama di sini kemudian di forum lain untuk mendapatkan jawaban yang berlawanan. DI adalah pola, bukan arsitektur. Tergantung pada tujuan Anda, Anda dapat memutuskan untuk menggunakannya atau tidak. Saya menggunakannya tetapi tidak untuk alasan kebanyakan orang mengatakan kepada saya untuk menggunakannya.
Bastien Vandamme
4

Beberapa komentar cepat. Saya pribadi mungkin tidak akan melewatkan string koneksi. Jika ada sesuatu yang saya coba dan buat antarmuka mungkin untuk repositori dan hanya lulus antarmuka di sekitar? Mintalah repositori mengimplementasikan atau mengekspos antarmuka IOW.

Dengan cara ini, itu tidak perlu menjadi database yang mengimplementasikan repositori Anda. bisa berupa cache memori, atau apa pun. Mungkin Anda bisa menggunakan semacam kerangka kerja injeksi ketergantungan untuk membuat instance ini bahkan?

Jadi sebagai jawaban atas beberapa pertanyaan Anda:

  1. Ya, saya pikir tidak apa-apa
  2. Saya masih memiliki referensi UI proyek EF dan antarmuka referensi lapisan bisnis yang diterapkan oleh lapisan repositori EF. Dengan cara itu proyek lain masih bisa menggunakan majelis yang sama tetapi mereka memiliki fleksibilitas untuk bertukar jika diinginkan?
  3. hmmm, Mungkin repositori di lapisan akses tetapi mengimplementasikan definisi antarmuka yang diekspos di lapisan bisnis ??

Ini hanya beberapa pemikiran untuk dipertimbangkan.

dreza
sumber
Tentang poin 2, satu tujuan yang saya coba buat adalah tidak memiliki CRUD langsung di dalam layer ui. Maksud saya adalah saya ingin memastikan hanya CRUD yang bisa terjadi dengan melalui lapisan bisnis, dengan cara itu dikelola.
Matius