Dapatkah saya meneruskan parameter konstruktor ke metode Resolve () Unity?

92

Saya menggunakan Unity Microsoft untuk injeksi ketergantungan dan saya ingin melakukan sesuatu seperti ini:

IDataContext context = _unityContainer.Resolve<IDataContext>();
var repositoryA = _unityContainer.Resolve<IRepositoryA>(context); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(context); //Same instance of context

IDataContext context2 = _unityContainer.Resolve<IDataContext>(); //New instance
var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(context2);

RepositoryAdan RepositoryBkeduanya memiliki konstruktor yang mengambil IDataContextparameter, dan saya ingin Unity menginisialisasi repositori dengan konteks yang saya berikan. Perhatikan juga bahwa IDataContexttidak terdaftar dengan Unity (Saya tidak ingin 3 contoh IDataContext).

NotDan
sumber

Jawaban:

71

Sampai hari ini mereka telah menambahkan fungsi ini:

Ini ada di drop terbaru di sini:

http://unity.codeplex.com/SourceControl/changeset/view/33899

Diskusi di sini:

http://unity.codeplex.com/Thread/View.aspx?ThreadId=66434

Contoh:

container.Resolve<IFoo>(new ParameterOverrides<Foo> { { "name", "bar" }, { "address", 42 } });"
Ada
sumber
1
Lihat juga stackoverflow.com/questions/2813322/…
Michael Freidgeim
2
"Kelas 'Microsoft.Practices.Unity.ParameterOverrides' tidak memiliki parameter jenis". Saya menggunakan Unity 3.5; apakah kode ini hanya berlaku untuk versi lama Unity?
Thomas Levesque
Ini bekerja untuk saya. Catatan: Kelas Anda harus memiliki konstruktor parametrized dengan parameter "nama" dan parameter "alamat". Foo(string name, int address) { ... }
adun
Menggunakan Unity 2.1: container.Resolve<IFoo>(new ParameterOverrides { { "name", "bar" }, { "address", 42 } });
mrfelis
38

<2 sen>

Bagaimana jika Anda kemudian memutuskan untuk menggunakan layanan berbeda yang membutuhkan lebih atau kurang dari sekedar konteks?

Masalah dengan parameter konstruktor dan IoC adalah bahwa parameter pada akhirnya terikat pada jenis beton yang digunakan, bukan menjadi bagian dari kontrak yang ditentukan oleh antarmuka layanan.

Saran saya adalah Anda menyelesaikan konteksnya juga, dan saya yakin Unity harus memiliki cara bagi Anda untuk menghindari pembuatan 3 instance darinya, atau Anda harus mempertimbangkan layanan pabrik yang memiliki cara bagi Anda untuk membuat objek.

Misalnya, bagaimana jika Anda kemudian memutuskan untuk membangun repositori yang tidak bergantung pada database tradisional sama sekali, melainkan menggunakan file XML untuk menghasilkan dummy-data untuk pengujian? Bagaimana Anda akan memberi makan konten XML ke konstruktor itu?

IoC didasarkan pada kode decoupling, dengan mengikat tipe dan semantik argumen ke tipe konkret, Anda benar-benar belum melakukan decoupling dengan benar, masih ada ketergantungan.

"Kode ini dapat berbicara dengan semua jenis repositori mungkin, selama mengimplementasikan antarmuka ini .... Oh, dan menggunakan konteks data".

Sekarang, saya tahu bahwa kontainer IoC lain memiliki dukungan untuk ini, dan saya juga memilikinya di versi pertama saya, tetapi menurut saya, itu tidak termasuk dalam langkah resolusi.

</ 2 sen>

Lasse V. Karlsen
sumber
3
Saya mengerti maksud Anda dan setuju dengan Anda, namun saya masih membutuhkan instance RepositoryA dan RepositoryB untuk memiliki IDataContext yang sama, yang harus berbeda dari RepositoryC. Perhatikan juga bahwa IRepositoryA dan IRepositoryB memiliki properti untuk IDataContext. Saya akan mengupdate kode sampel sedikit.
NotDan
2
Poin yang bagus. Saya akan menambahkan parameter string ke konstruktor, tetapi setelah melihat poin ini, saya memutuskan untuk menjadikannya objek yang lengkap. Ini hanya terdiri dari string pada saat ini, tetapi saya sudah dapat melihat bagaimana saya dapat menambahkan properti yang lebih berguna ke dalamnya
Santosh Benjamin
9

Terima kasih teman-teman ... milikku mirip dengan kiriman oleh "Exist". Lihat di bawah:

        IUnityContainer container = new UnityContainer();
        container.LoadConfiguration();

        _activeDirectoryService = container.Resolve<IActiveDirectoryService>(new ResolverOverride[]
        {
            new ParameterOverride("activeDirectoryServer", "xyz.adserver.com")
        });
Kwex
sumber
5

Anda dapat menggunakan InjectionConstructor / InjectionProperty / InjectionMethod bergantung pada Arsitektur Injeksi Anda dalam ResolvedParameter <T> ("name") untuk mendapatkan instance dari Objek yang telah didaftarkan sebelumnya di penampung.

Dalam kasus Anda, Objek ini harus terdaftar dengan sebuah Nama, dan untuk insance yang sama Anda memerlukan ContainerControlledLifeTimeManager () sebagai LifeTimeManager.

_unityContainer.RegisterType<IDataContext,DataContextA>("DataContextA", new ContainerControlledLifeTimeManager());
_unityContainer.RegisterType<IDataContext,DataContextB>("DataContextB");

  var repositoryA = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryB = _unityContainer.Resolve<IRepositoryB>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextA")));

  var repositoryA2 = _unityContainer.Resolve<IRepositoryA>(new InjectionConstructor(
new ResolvedParameter<IDataContext>("DataContextB")));
Trecenti
sumber
4
Apakah Anda yakin dengan kode ini? Itu tidak mengkompilasi ... Resolvemengambil koleksi ResolverOverride, dan InjectionConstructorbukan ResolverOverride.
Thomas Levesque
Yup Sepertinya salah. Meskipun persatuan seharusnya dirancang seperti itu. Jika nama parameter mengubah semuanya rusak
Frank Q.
3

Jawaban yang sangat singkat adalah: tidak. Unity saat ini tidak memiliki cara untuk melewatkan parameter ke konstruktor yang tidak konstan atau diinjeksi, yang dapat saya temukan. IMHO itulah satu-satunya hal terbesar yang hilang, tapi saya pikir itu adalah desain daripada kelalaian.

Seperti yang dicatat oleh Jeff Fritz, secara teori Anda dapat membuat pengelola seumur hidup khusus yang mengetahui contoh konteks mana yang akan dimasukkan ke dalam berbagai jenis, tetapi itu adalah tingkat pengkodean keras yang tampaknya meniadakan tujuan penggunaan Unity atau DI di tempat pertama.

Anda bisa mundur selangkah dari DI penuh dan membuat implementasi repositori Anda bertanggung jawab untuk menetapkan konteks datanya sendiri. Konteks contoh masih bisa diselesaikan dari wadah tetapi logika untuk memutuskan mana yang akan digunakan harus pergi ke dalam pelaksanaan repositori. Ini tidak murni, tentu saja, tapi itu akan menghilangkan masalah.

Neil Hewitt
sumber
1

Alternatif lain yang dapat Anda gunakan (tidak benar-benar tahu apakah ini praktik yang baik atau tidak) adalah membuat dua kontainer dan mendaftarkan satu contoh untuk masing-masing:

IDataContext context = _unityContainer.Resolve<IDataContext>();
_unityContainer.RegisterInstance(context);
var repositoryA = _unityContainer.Resolve<IRepositoryA>(); //Same instance of context
var repositoryB = _unityContainer.Resolve<IRepositoryB>(); //Same instance of context


//declare _unityContainer2
IDataContext context2 = _unityContainer2.Resolve<IDataContext>(); //New instance
_unityContainer2.RegisterInstance(context2);
var repositoryA2 = _unityContainer2.Resolve<IRepositoryA>(context2); //will retrieve the other instance

semoga ini membantu juga

Samuel Carrijo
sumber
0

NotDan, saya pikir Anda mungkin telah menjawab pertanyaan Anda sendiri di komentar ke lassevk.

Pertama, saya akan menggunakan LifetimeManager untuk mengelola siklus hidup dan jumlah instance IDataContext yang dibuat oleh Unity.
http://msdn.microsoft.com/en-us/library/cc440953.aspx

Sepertinya ContainerControlledLifetimeManagerobjek tersebut akan memberi Anda manajemen instance yang Anda butuhkan. Dengan LifetimeManager tersebut di tempat, Unity harus menambahkan instance IDataContext yang sama ke semua objek yang memerlukan dependensi IDataContext.

Jeff Fritz
sumber