none
Constructor injection wird issue ResolutionFailedException RRS feed

  • Question

  • Dear all,

    I am building a wrapper around a WCF service client which is based on Service Reference

    WHn registering my claas and interface using unity I am doing it as below :

    container.RegisterType<ITestPocDataService, WcfTestPocDataService>(new InjectionConstructor(
                    new object[] { new ServiceReference.DataServiceClient("SecureTcpTranportMsgUsingCustomUser") }));

    At the time I try to reolve the created instance using code below

    var _instance=container.Resolve<WcfTestPocDataService>();

    I get an excpetion saying  :

    Unity.Exceptions.ResolutionFailedException: 'Resolution of the dependency failed, type = 'Client.Factory.WcfTestPocDataService', name = '(none)'.
    Exception occurred while: while resolving.
    Exception is: InvalidOperationException - The type DataServiceClient has multiple constructors of length 2. Unable to disambiguate.
    -----------------------------------------------
    At the time of the exception, the container was:
      Resolving Client.Factory.WcfTestPocDataService,(none)
      Resolving parameter 'client' of constructor Client.Factory.WcfTestPocDataService(Client.ServiceReference.DataServiceClient client)
        Resolving Client.ServiceReference.DataServiceClient,(none)
    '

    And inner exception :
    InvalidOperationException: The type DataServiceClient has multiple constructors of length 2. Unable to disambiguate.

    Any idea how to selved  ? as I am specifying teh constructor 2 used I wonder why I get that error

    regards

    Tuesday, October 2, 2018 9:56 AM

Answers

  • You're using the container incorrectly. The purpose of a container is to allow the bulk of your code to get instances of stuff without the need for knowing the underlying type. You do that by (generally) using interfaces in your constructors. The container is configured that, for a given interface, return an instance of a type that was configured to be used for it. So in your example WcfTestPocDataService is created and returned whenever the code requests an ITestPocDataService interface.

    The problem is that you are registering 2 different types for the same interface. The container doesn't know which one to return because it has two options. This is an error. It isn't up to the container to decide which instance to return because it has no way of knowing this. You should register 1 and only 1 type for each interface. It is up to your startup code to figure which type you want to use, not the container.

    Different containers do support variations, it depends upon your container. I have no knowledge of Unity so I cannot answer for it though. In many cases a container will allow you to register multiple types for the same interface but you also have to provide a "hint" (generally a name or key). Later when you try to use the instance (in a constructor call) you have to specify the hint as to which variant you want. This is container-specific so that means your (decoupled) code is now coupled to the container you're using. It sort of defeats the purpose in my book but whatever. Example:

    //During container setup
    
    //The default instance
    container.RegisterType<MyService>().As<IService>();
    
    //Register a "keyed" instance
    container.RegisterType<YourService>("special").As<IService>();
    
    //Later in your code
    public class MyClient
    {
       //Nothing specified so using the default container
       //registeration which is MyService
       public MyClient ( IService service ){ }
    }
    
    public class YourClient
    {
       //using the container-specific attribute to specify you
       //want the implementation with the name special - this
       //would return the YourService instance
       public Yourclient ( [Key("special")] IService service ) { }
    }

    Ultimately, you get 1 implementation per interface. 

    Note, the part where you say it works is because you are no longer says "for interface ITestPocDataService use WcfTestPocDataService". By removing the interface reference you are instead saying "if someone accepts a WcfTestPocDataService in their constructor then return the instance that I'm configuring here". They are completely different things. You'll find, with this configuration, that any requests for ITestPocDataService you always get back your HttpDataService class.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by wakefun Wednesday, October 3, 2018 7:34 AM
    Tuesday, October 2, 2018 2:40 PM
    Moderator
  • Hi,

    it looks like you registered the interface but you are trying to resolve the class behind it. So the container has no idea how to get this class. Try to resolve the interface instead of the class.

    var _instance = container.Resolve<ITestPocDataService>();

    Greetings, Chris

    • Marked as answer by wakefun Wednesday, October 3, 2018 7:41 AM
    Tuesday, October 2, 2018 8:13 PM
  • But then if you hosted your service under IIS for intance, how do you call your WcfServiceFactory?

    The WCF service through the WCF service startup accesses the factory, which is established off of Service1.svc ServiceHost using VS's View Markup.

    <%@ ServiceHost Language="C#" Debug="true" Service="WcfService.Service1" Factory="WCFSrvcFactory.WcfServiceFactory" %>

    • Marked as answer by wakefun Wednesday, October 3, 2018 1:40 PM
    Wednesday, October 3, 2018 11:26 AM

All replies

  • I manage to isolate the issue but I am confuse on why it behaves ike this.

    Let me explain I have a classe wich is define as below :

    public class WcfTestPocDataService : ITestPocDataService
        {
            private ServiceReference.DataServiceClient _client;
    
            //Define the constructors you need for your application
            //This one would work for a typical DI container   
            public WcfTestPocDataService()
            {
                //No parameters means it uses the matching client endpoint in the config file
                _client = new DataServiceClient();
            }
    
            //Another ctor that allows you to configure it if you need to
          
            public WcfTestPocDataService(string endpointName)
            {
                _client = new DataServiceClient(endpointName);
            }
    
            //My personal favorite is to have the client configured outside the wrapper and the wrapper just use it
    
            public WcfTestPocDataService(DataServiceClient client)
            {
                _client = client;
            }
    
    
            public List<TestPocData> GetData()
            {
                var response = _client.GetDataJson();
    
                return ConvertToTestPocData(response);
    
    
            }
            private List<TestPocData> ConvertToTestPocData(DataResponse response)
            {
                //TODO: Implement the logic here
                return new List<TestPocData>();
            }
    
        }

    And an other class implementing teh same interface ITestPocDataService as below

     public class HttpDataService : ITestPocDataService
        {
            private readonly HttpClient _client;
    
            public HttpDataService(HttpClient client)
            {
                _client = client;
            }
    
            public List<TestPocData> GetData()
            {
                throw new NotImplementedException();
            }
        }


    Then when I register my class in UNity container I was doing as below :

    container.RegisterType<ITestPocDataService, WcfTestPocDataService>(
                    new InjectionConstructor(new object[] { client }));
                
                container.RegisterType<ITestPocDataService, HttpDataService>(
                    new InjectionConstructor(new object[] { new HttpClient() }));

    By doing so I have same interface which is register with 2 different classes. SO far so good.

    But at the time of resolving it as below I get the exception mentionned above in this post :

    _inst= container.Resolve<WcfTestPocDataService>();


    IMPORTANT: If I remove the Interface from the RegistrationType as below :

    container.RegisterType<WcfTestPocDataService>(
                    new InjectionConstructor(new object[] { client }));
    

    Then it works and I can get my instance.

    Could you pleaqse why by passing the Interface type it fails and what is the correct way to do ?

    regards


    Tuesday, October 2, 2018 11:54 AM
  • WCF is at the WCF forum.

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=wcf

    Tuesday, October 2, 2018 1:29 PM
  • You're using the container incorrectly. The purpose of a container is to allow the bulk of your code to get instances of stuff without the need for knowing the underlying type. You do that by (generally) using interfaces in your constructors. The container is configured that, for a given interface, return an instance of a type that was configured to be used for it. So in your example WcfTestPocDataService is created and returned whenever the code requests an ITestPocDataService interface.

    The problem is that you are registering 2 different types for the same interface. The container doesn't know which one to return because it has two options. This is an error. It isn't up to the container to decide which instance to return because it has no way of knowing this. You should register 1 and only 1 type for each interface. It is up to your startup code to figure which type you want to use, not the container.

    Different containers do support variations, it depends upon your container. I have no knowledge of Unity so I cannot answer for it though. In many cases a container will allow you to register multiple types for the same interface but you also have to provide a "hint" (generally a name or key). Later when you try to use the instance (in a constructor call) you have to specify the hint as to which variant you want. This is container-specific so that means your (decoupled) code is now coupled to the container you're using. It sort of defeats the purpose in my book but whatever. Example:

    //During container setup
    
    //The default instance
    container.RegisterType<MyService>().As<IService>();
    
    //Register a "keyed" instance
    container.RegisterType<YourService>("special").As<IService>();
    
    //Later in your code
    public class MyClient
    {
       //Nothing specified so using the default container
       //registeration which is MyService
       public MyClient ( IService service ){ }
    }
    
    public class YourClient
    {
       //using the container-specific attribute to specify you
       //want the implementation with the name special - this
       //would return the YourService instance
       public Yourclient ( [Key("special")] IService service ) { }
    }

    Ultimately, you get 1 implementation per interface. 

    Note, the part where you say it works is because you are no longer says "for interface ITestPocDataService use WcfTestPocDataService". By removing the interface reference you are instead saying "if someone accepts a WcfTestPocDataService in their constructor then return the instance that I'm configuring here". They are completely different things. You'll find, with this configuration, that any requests for ITestPocDataService you always get back your HttpDataService class.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by wakefun Wednesday, October 3, 2018 7:34 AM
    Tuesday, October 2, 2018 2:40 PM
    Moderator
  • Here is example of WCF using Unity and constructor injection throughout the backend.

    using Microsoft.Practices.Unity;
    using Unity.Wcf;
    using WcfService;
    using Repository;
    using DAL.DAO;
    
    namespace WCFSrvcFactory
    {
    	public class WcfServiceFactory : UnityServiceHostFactory
        {
            protected override void ConfigureContainer(IUnityContainer container)
            {
    			// register all your components with the container here
                container
                    .RegisterType<IService1, Service1>()
                    .RegisterType<IStudentRepo, StudentRepo>()
                    .RegisterType<IEnrollmentRepo, EnrollmentRepo>()
                    .RegisterType<IDAOStudent, DAOStudent>()
                    .RegisterType<IDAOCourse, DAOCourse>()
                    .RegisterType<IDAOEnrollment, DAOEnrollment>();
            }
        }    
    }

    using System;
    using System.Collections.Generic;
    using Entities;
    using Repository;
    
    namespace WcfService
    {
        public class Service1 : IService1
        {
            private IStudentRepo _studentRepo;
            private IEnrollmentRepo _enrollmentRepo;
            private DTOStudent dto;
            public Service1(IStudentRepo studentRepo, IEnrollmentRepo enrollmentRepo)
            {
                _studentRepo = studentRepo;
                _enrollmentRepo = enrollmentRepo;
    
            }
            public DTOStudent GetStudentById(Int32 id)
            {
                try
                {
                    return _studentRepo.GetStudentById(id);
                }
                catch (Exception e)
                {
                    dto = new DTOStudent();
    
                    dto.DtoResponse.Message = e.Message;
                    if (e.InnerException != null) dto.DtoResponse.InnerException = e.InnerException.Message;
                    dto.DtoResponse.StackTrace = e.StackTrace;
    
                    return dto;
                }
            }
            public List<DTOStudent> GetStudents()
            {
                try
                {
                    return _studentRepo.GetStudents();
                }
                catch (Exception e)
                {
                    var dtos = new List<DTOStudent>();
                    dto = new DTOStudent();
    
                    dto.DtoResponse.Message = e.Message;
                    if (e.InnerException != null) dto.DtoResponse.InnerException = e.InnerException.Message;
                    dto.DtoResponse.StackTrace = e.StackTrace;
         
                    dtos.Add(dto);
                    return dtos;
                } 
            }
    
            public DTOStudent CreateStudent(DTOStudent dto)
            {
                try
                {
                    _studentRepo.CreateStudent(dto);
                    return null;
                }
                catch (Exception e)
                {
                    dto = new DTOStudent();
    
                    dto.DtoResponse.Message = e.Message;
                    if (e.InnerException != null) dto.DtoResponse.InnerException = e.InnerException.Message;
                    dto.DtoResponse.StackTrace = e.StackTrace;
    
                    return dto;
                }
            }
            public DTOStudent UpdateStudent(DTOStudent dto)
            {
                try
                {
                    _studentRepo.UpdateStudent(dto);
                    return null;
                }
                catch (Exception e)
                {
                    dto = new DTOStudent();
    
                    dto.DtoResponse.Message = e.Message;
                    if (e.InnerException != null) dto.DtoResponse.InnerException = e.InnerException.Message;
                    dto.DtoResponse.StackTrace = e.StackTrace;
    
                    return dto;
                }
            }
            public DTOStudent DeleteStudent(Int32 id)
            {
                try
                {
                    _studentRepo.DeleteStudent(id);
                    return null;
                }
                catch (Exception e)
                {
                    dto.DtoResponse.Message = e.Message;
                    if (e.InnerException != null) dto.DtoResponse.InnerException = e.InnerException.Message;
                    dto.DtoResponse.StackTrace = e.StackTrace;
    
                    return dto;
                }
            }
            public DTOEnrollment GetEnrollmentById(Int32 id)
            {
                return _enrollmentRepo.GetEnrollmentById(id);
            }
            public List<DTOEnrollment> GetEnrollments()
            {
                return _enrollmentRepo.GetEnrollments();
            }
            public void CreateEnrollment(DTOEnrollment dto)
            {
                _enrollmentRepo.CreateEnrollment(dto);
            }
            public void UpdateEnrollment(DTOEnrollment dto)
            {
                _enrollmentRepo.UpdateEnrollment(dto);
            }
            public void DeleteEnrollment(Int32 id)
            {
                _enrollmentRepo.DeleteEnrollment(id);
            }
        }
    }
    

    using System;
    using System.Collections.Generic;
    using Entities;
    using DAL.DAO;
    
    namespace Repository
    {    public class StudentRepo : IStudentRepo
        {
            private IDAOStudent _daoStudent;
    
            public StudentRepo(IDAOStudent daoStudent)
            {
                _daoStudent = daoStudent;
            }
            public DTOStudent GetStudentById(int id)
            {
               return _daoStudent.GetStudentById(id);
            }
            public List<DTOStudent> GetStudents()
            {
                return _daoStudent.GetStudents();
            }
            public void CreateStudent(DTOStudent dto)
            {
                _daoStudent.CreateStudent(dto);
            }
            public void UpdateStudent(DTOStudent dto)
            {
                _daoStudent.UpdateStudent(dto);
            }
            public void DeleteStudent(int id)
            {
                _daoStudent.DeleteStudent(id);
            }
        }
    }
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Data.Entity;
    using System.Data.Entity.Core.EntityClient;
    using System.Data.Entity.Core.Objects;
    using System.Data.Entity.Infrastructure;
    using System.Data.SqlClient;
    using Entities;
    using DAL.Model;
    
    namespace DAL.DAO
    {
        public class DAOStudent : IDAOStudent
        {
            public DTOStudent GetStudentById(Int32 id)
            {
                var dto = new DTOStudent();
                using (var context = new CUDataEntities())
                {
                    var student = (context.Students.Where(a => a.StudentID == id)).SingleOrDefault();
    
                    if (student != null)
                    {
                        dto.StudentID = student.StudentID;
                        dto.FirstName = student.FirstName;
                        dto.LastName = student.LastName;
                        dto.EnrollmentDate = student.EnrollmentDate;
    
                        var enrolllments =  new DAOEnrollment().GetEntrollmentsByStudentId(id).ToList();
                        var courses = new DAOCourse().GetCoursesByStudentCourseId(student.StudentID).ToList();
    
                        dto.EnrollsandCourses = (from a in enrolllments
                                      join b in courses on a.CourseID equals b.CourseID
                        select new  DTOEnrollandCourse()
                         { Title = b.Title, Credits = b.Credits, Grade = a.Grade }).ToList();
                    }
                }
    
                return dto;
            }
            public void CreateStudent(DTOStudent dto)
            {
                using (var context = new CUDataEntities())
                {
                    var student = new Student
                    {
                        FirstName = dto.FirstName,
                        LastName = dto.LastName,
                        EnrollmentDate = dto.EnrollmentDate
                    };
    
                    context.Students.Add(student);
                    context.SaveChanges();
                }
            }
    
            public void DeleteStudent(int id)
            {
                Student student;
                using (var context = new CUDataEntities())
                {
                    student = (context.Students.Where(a => a.StudentID == id)).SingleOrDefault();
                }
    
                using (var newContext = new CUDataEntities())
                {
                    newContext.Entry(student).State = System.Data.Entity.EntityState.Deleted;
                    newContext.SaveChanges();
                }
            }
    
            public List<DTOStudent> GetStudents()
            {
               
                var dtos = new List<DTOStudent>();
    
                using (var context = new CUDataEntities())
                {
                    //var adapter = (IObjectContextAdapter)context;
                    //var objectContext = adapter.ObjectContext;
                    
                    //var entityConn = objectContext.Connection as EntityConnection;
                    //var dbConn = entityConn.StoreConnection as SqlConnection;
    
                    //dbConn.Open();
    
                    var students = context.Students.ToList();
    
                    foreach(var stud in students)
                    {
                        var dto = new DTOStudent
                        {
                            StudentID = stud.StudentID,
                            FirstName = stud.FirstName,
                            LastName = stud.LastName,
                            EnrollmentDate = stud.EnrollmentDate
                        };
    
                        dtos.Add(dto);
                    }
                }
    
                return dtos;
            }
    
            public void UpdateStudent(DTOStudent dto)
            {
                var student = new Student();
    
                using (var context = new CUDataEntities())
                {
                    student = (context.Students.Where(a => a.StudentID == dto.StudentID)).SingleOrDefault();
                }
    
                if (student != null)
                {
                    student.FirstName = dto.FirstName;
                    student.LastName = dto.LastName;
                    student.EnrollmentDate = dto.EnrollmentDate;
                } 
    
                using (var dbcontext = new CUDataEntities())
                {
                    if (student != null)
                    {
                        dbcontext.Entry(student).State = EntityState.Modified;
                        dbcontext.SaveChanges();
                    }
                }
            }
        }
    }
    

    Tuesday, October 2, 2018 3:02 PM
  • Hi,

    it looks like you registered the interface but you are trying to resolve the class behind it. So the container has no idea how to get this class. Try to resolve the interface instead of the class.

    var _instance = container.Resolve<ITestPocDataService>();

    Greetings, Chris

    • Marked as answer by wakefun Wednesday, October 3, 2018 7:41 AM
    Tuesday, October 2, 2018 8:13 PM
  • Thnaks cool dad fully clear now
    Wednesday, October 3, 2018 7:34 AM
  • Dear Chri, thanks for the info. yes if I resolve the interface it not not show the erorr and works.
    Doing this return the last register instance with the interface.

    But all is fine now

    Wednesday, October 3, 2018 7:41 AM
  • Dear Da924x, in your sample provide, your service represented by Service1 need to get injected interfaces already register into Unity.

    For that at which level do you use your WcfServiceFactory  to initate the type registration in containers ?

    because it is fully depending on how the serives is hosted I guess.

    If it is self hosted you can can your WcfServiceFactory at init of the host as long as you have access to the init process like in a Windows Service where you can do it in the startup of it.

    But then if you hosted your service under IIS for intance, how do you call your WcfServiceFactory ?

    regards

    Wednesday, October 3, 2018 8:42 AM
  • But then if you hosted your service under IIS for intance, how do you call your WcfServiceFactory?

    The WCF service through the WCF service startup accesses the factory, which is established off of Service1.svc ServiceHost using VS's View Markup.

    <%@ ServiceHost Language="C#" Debug="true" Service="WcfService.Service1" Factory="WCFSrvcFactory.WcfServiceFactory" %>

    • Marked as answer by wakefun Wednesday, October 3, 2018 1:40 PM
    Wednesday, October 3, 2018 11:26 AM