Pastikan bahwa pengontrol memiliki kesalahan konstruktor publik tanpa parameter

105

Saya telah mengikuti tutorial ini yang berhasil dengan baik, sampai saya memodifikasi saya DbContextuntuk memiliki konstruktor tambahan. Saya sekarang mengalami masalah dengan resolusi dan tidak yakin apa yang harus dilakukan untuk memperbaikinya. Apakah ada cara mudah untuk memaksanya mengambil konstruktor tanpa parameter atau saya melakukan pendekatan yang salah?

DbContext dengan dua konstruktor:

public class DashboardDbContext : DbContext
{
    public DashboardDbContext() : base("DefaultConnection") { }

    public DashboardDbContext(DbConnection dbConnection, bool owns)
        : base(dbConnection, owns) { }
}

SiteController konstruktor:

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}

Gudang:

DashboardDbContext _context;

public DashboardRepository(DashboardDbContext context)
{
    _context = context;
}

UnityResolver kode:

public class UnityResolver : IDependencyResolver
{
    private readonly IUnityContainer _container;

    public UnityResolver(IUnityContainer container)
    {
        _container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return _container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = _container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

WebApiConfig:

var container = new UnityContainer();
container.RegisterType<IDashboardRepository, DashboardRepository>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);

Kesalahan dari Panggilan WebApi:

System.InvalidOperationException: Terjadi kesalahan saat mencoba membuat pengontrol berjenis 'SiteController'. Pastikan bahwa pengontrol memiliki konstruktor publik tanpa parameter.

at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) 
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()

InnerException: System.ArgumentException: Jenis 'Dashboard.Web.Controllers.SiteController' tidak memiliki konstruktor default.

at System.Linq.Expressions.Expression.New(Type type) 
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) 
at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)

Tutorialnya sangat bagus dan telah bekerja dengan baik untuk saya sampai saya menambahkan konstruktor kedua.

scarpacci
sumber
2
Kesalahan memberi tahu Anda bahwa SiteControlleritulah yang harus memiliki konstruktor tanpa parameter, bukan DashboardDbContext.
Neil Smith
Hai Smith.h.Neil, tetapi itu hanya melempar kesalahan itu ketika konstruktor tambahan ditambahkan ke dbcontext. Jika saya menghapusnya atau mengomentarinya (konstruktor kedua) itu berfungsi dengan baik.
scarpacci
Dapatkah saya melihat konstruktornya SiteController?
Neil Smith
Dan saya menduga Anda menyuntikkannya DbContextke dalam repositori?
Neil Smith
@scarpacci Apakah Anda yakin satu - satunya perubahan yang Anda buat adalah menghapus konstruktor kedua dari DbContext? Kecuali jika Anda mengabaikan instantiasi pengontrol Anda dengan tidak memiliki konstruktor DbContext kedua, tidak masuk akal jika kesalahan bergantung pada konstruktor DbContext.
Asad Saeeduddin

Jawaban:

130

Apa yang terjadi adalah Anda digigit oleh masalah ini . Pada dasarnya, yang terjadi adalah Anda tidak mendaftarkan pengontrol Anda secara eksplisit di penampung Anda. Unity mencoba menyelesaikan jenis beton yang tidak terdaftar untuk Anda, tetapi karena Unity tidak dapat menyelesaikannya (disebabkan oleh kesalahan dalam konfigurasi Anda), ia mengembalikan null. Itu dipaksa untuk mengembalikan nol, karena API Web memaksanya untuk melakukannya karena IDependencyResolverkontrak. Karena Unity mengembalikan null, API Web akan mencoba membuat pengontrol itu sendiri, tetapi karena tidak memiliki konstruktor default, ia akan menampilkan pengecualian "Pastikan pengontrol memiliki konstruktor publik tanpa parameter". Pesan pengecualian ini menyesatkan dan tidak menjelaskan penyebab sebenarnya.

Anda akan melihat pesan pengecualian yang lebih jelas jika Anda mendaftarkan pengontrol Anda secara eksplisit, dan itulah mengapa Anda harus selalu mendaftarkan semua jenis root secara eksplisit.

Tapi tentu saja, kesalahan konfigurasi berasal dari Anda menambahkan konstruktor kedua ke file DbContext. Unity selalu mencoba untuk memilih konstruktor dengan argumen terbanyak, tetapi tidak tahu bagaimana menyelesaikan konstruktor khusus ini.

Jadi, penyebab sebenarnya adalah Anda mencoba menggunakan kemampuan kabel otomatis Unity untuk membuat file DbContext. DbContextadalah tipe khusus yang tidak boleh terhubung secara otomatis. Ini adalah jenis kerangka kerja dan oleh karena itu Anda harus mundur untuk mendaftarkannya menggunakan delegasi pabrik :

container.Register<DashboardDbContext>(
    new InjectionFactory(c => new DashboardDbContext())); 
Steven
sumber
HARAP DICATAT ketika Anda membangun kembali proyek Anda, Anda dapat mengatur ulang kredensial masuk Anda ... sebelum mencoba menerapkan solusi ini, harap: buat ulang proyek Anda, keluar sendiri, lalu masuk lagi, baru kemudian - segarkan halaman Anda dan amati apakah masalah tetap ada
ymz
Terima kasih - dingleberry di tim backend saya ini melanggar banyak aturan dengan konfigurasi unity. Mulai bertanya-tanya apakah itu cara setiap tim menggunakan kontainer IOC.
Dagrooms
@Dagrooms: Banyak pengembang yang menggigit ini, tapi ini bukan masalah yang ada di semua DI Containers. Simple Injector misalnya, akan selalu memastikan bahwa kesalahan ekspresif diminta jika hal seperti itu terjadi. Tip bagus lainnya: Jangan gunakan custom IDependencyResolvertapi hanya gunakan custom IControllerActivatorsaja.
Steven
46

Dalam kasus saya, itu karena pengecualian di dalam konstruktor ketergantungan saya yang disuntikkan (dalam contoh Anda - di dalam konstruktor DashboardRepository). Pengecualian tertangkap di suatu tempat di dalam infrastruktur MVC. Saya menemukan ini setelah saya menambahkan log di tempat yang relevan.

Illidan
sumber
7
Ini adalah jawaban yang sangat penting. Sangat mudah untuk jatuh ke dalam perangkap mengejar masalah set-up dengan Unity ketika melihat Make sure that the controller has a parameterless public constructor.tetapi sangat mungkin dependensi sudah diatur tetapi pengecualian jauh di dalam perut telah menghentikannya diselesaikan.
Phil Cooper
2
Ini. Sejuta kali! Saya lupa menambahkan peta ketergantungan ke konfigurasi Ninject saya.
Travo
Pengecualian deep in the bowels saya adalah jenis properti 'string' padahal seharusnya 'DateTime?'. Tidak akan mencari itu seandainya saya tidak melihat jawaban ini. Terima kasih banyak.
Jazzy
Lainnya seperti LousyErrorMessageException ()
Simon_Weaver
Di tempat relevan apa Anda menempatkan log? Saya menjalankan dari debugger tetapi tidak mendapat pengecualian, bahkan ketika saya memeriksa semua pengecualian CLR. Saya harus menambahkan penyelesaian konstruktor manual dan baru kemudian saya mendapat kesalahan. Itu memberi tahu saya untuk menambahkan Diagnostik untuk mendapatkan kesalahan yang dapat digunakan, yang akhirnya memberi saya sesuatu untuk dikerjakan
Arjan
6

Saya memiliki masalah yang sama dan saya mengatasinya dengan membuat perubahan pada file UnityConfig.cs Untuk menyelesaikan masalah ketergantungan pada file UnityConfig.cs Anda harus menambahkan:

public static void RegisterComponents()    
{
    var container = new UnityContainer();
    container.RegisterType<ITestService, TestService>();
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
befree2j
sumber
4

Terkadang karena Anda menyelesaikan antarmuka Anda di ContainerBootstraper.cs, sangat sulit untuk menangkap kesalahannya. Dalam kasus saya, ada kesalahan dalam menyelesaikan implementasi antarmuka yang telah saya masukkan ke pengontrol api. Saya tidak dapat menemukan kesalahan karena saya telah menyelesaikan antarmuka di bootstraperContainer saya seperti ini: container.RegisterType<IInterfaceApi, MyInterfaceImplementaionHelper>(new ContainerControlledLifetimeManager());
maka saya telah menambahkan baris berikut di wadah bootstrap saya: container.RegisterType<MyController>(); jadi ketika saya menyusun proyek, kompilator mengeluh dan berhenti di baris di atas dan menunjukkan kesalahan .

Amir978
sumber
4

Saya memiliki masalah yang sama. Saya mencarinya di Google selama dua hari. Akhirnya saya secara tidak sengaja memperhatikan bahwa masalahnya adalah pengubah akses dari konstruktor Controller. Saya tidak meletakkan publickata kunci di belakang konstruktor Pengendali.

public class MyController : ApiController
    {
        private readonly IMyClass _myClass;

        public MyController(IMyClass myClass)
        {
            _myClass = myClass;
        }
    }

Pengalaman ini saya tambahkan sebagai jawaban lain mungkin orang lain melakukan kesalahan serupa.

Bobs
sumber
0

Jika Anda memiliki antarmuka di pengontrol Anda

public myController(IXInterface Xinstance){}

Anda harus mendaftarkannya ke wadah Injeksi Ketergantungan.

container.Bind<IXInterface>().To<XClass>().InRequestScope();
Ahmet Arslan
sumber
0

Saya mendapat kesalahan ini ketika saya secara tidak sengaja mendefinisikan properti sebagai tipe objek tertentu, alih-alih tipe antarmuka yang telah saya definisikan di UnityContainer.

Sebagai contoh:

Mendefinisikan UnityContainer:

var container = new UnityContainer();
container.RegisterInstance(typeof(IDashboardRepository), DashboardRepository);
config.DependencyResolver = new UnityResolver(container);

SiteController (cara yang salah - perhatikan jenis repo):

private readonly DashboardRepository _repo;

public SiteController(DashboardRepository repo)
{
    _repo = repo;
}

SiteController (dengan cara yang benar):

private readonly IDashboardRepository _repo;

public SiteController(IDashboardRepository repo)
{
    _repo = repo;
}
Hasil karya
sumber
0

Jika Anda menggunakan UnityConfig.cs untuk mendaftarkan ulang pemetaan tipe Anda seperti di bawah ini.

public static void RegisterTypes(IUnityContainer container)
    {
     container.RegisterType<IProductRepository, ProductRepository>();
    }

Anda harus memberi tahu **webApiConfig.cs**tentang Container

config.DependencyResolver = new Unity.AspNet.WebApi.UnityDependencyResolver(UnityConfig.Container);
Vinay Patel
sumber
0

Dalam kasus saya, Unity ternyata adalah ikan haring merah. Masalah saya adalah hasil dari proyek berbeda yang menargetkan versi berbeda dari .NET. Unity telah disiapkan dengan benar dan semuanya telah didaftarkan dengan container dengan benar. Semuanya dikompilasi dengan baik. Tapi jenisnya berada di perpustakaan kelas, dan perpustakaan kelas ditetapkan ke target .NET Framework 4.0. Proyek WebApi menggunakan Unity ditetapkan untuk menargetkan .NET Framework 4.5. Mengubah perpustakaan kelas ke target 4,5 juga memperbaiki masalah saya.

Saya menemukan ini dengan mengomentari konstruktor DI dan menambahkan konstruktor default. Saya mengomentari metode pengontrol dan meminta mereka membuang NotImplementedException. Saya mengonfirmasi bahwa saya dapat menjangkau pengontrol, dan melihat NotImplementedException saya memberi tahu saya bahwa itu membuat instance pengontrol baik-baik saja. Selanjutnya, di konstruktor default, saya membuat instance rantai dependensi secara manual daripada mengandalkan Unity. Itu masih dikompilasi, tetapi ketika saya menjalankannya pesan kesalahan muncul kembali. Ini menegaskan bagi saya bahwa saya masih mendapatkan kesalahan bahkan ketika Unity tidak ada dalam gambar. Akhirnya, saya mulai dari bagian bawah rangkaian dan melanjutkan ke atas, mengomentari satu baris pada satu waktu dan menguji ulang sampai saya tidak lagi mendapatkan pesan kesalahan. Ini mengarahkan saya ke arah kelas yang melanggar, dan dari sana saya menemukan bahwa kelas itu diisolasi ke satu majelis.

Charlie Kilian
sumber