Pregunta

Please find below my code. Employee class implements IEmployee interface.

    namespace MiddleWare.ServiceContracts

    {
        [ServiceContract(Namespace = "http://mywebsite.com/MyProject")]

        public interface IMiscellaneous
        {
           [OperationContract]
            [ServiceKnownType(typeof(MiddleWare.Classes.Employee))]
            IEnumerable<IEmployee> Search_Employee
            (string SearchText);

    }


    namespace MiddleWare.ServiceClasses
    {
       public class Miscellaneous : IMiscellaneous
        {
           public IEnumerable<IEmployee> Search_Employee
            (string SearchText)
            {
               List<IEmployee> emp = new List<IEmployee>();

               IEmployee TempObject = (IEmployee)Activator.CreateInstance(typeof(IEmployee));  
               TempObject.EmployeeId = "12345678";

               emp.Add(TempObject);
              return emp;
           }
       }
    }

As is visible the above code does compile but wont work because interface instance cannot be created.How can I achive DI(Dependency Injection) here...If I write..

IEmployee TempObject = (IEmployee)Activator.CreateInstance(typeof(Employee));

Then this class will be dependent not only on the Interface but also the class...assuming that one fine day Employee class becomes Employee2.There will be code changes at two places.. 1)[ServiceKnownType(typeof(MiddleWare.Classes.Employee2))]

2)IEmployee TempObject = (IEmployee)Activator.CreateInstance(typeof(Employee2));

I want to avoid that. Can we do something at implementation of IOperationBehavior or is there a Ninject way of achieving this or am I trying to achieve impossible?

¿Fue útil?

Solución

From your updated requirements, there is nothing related to DI in this question...

So, to create a type based on the service known types of a service contract you can use:

public class EntityLoader<TServiceContract>
    {
        private static readonly HashSet<Type> ServiceKnownTypes = new HashSet<Type>();
        static EntityLoader()
        {
            var attributes = typeof(TServiceContract).GetMethods().SelectMany(m => m.GetCustomAttributes(typeof(ServiceKnownTypeAttribute), true)).Cast<ServiceKnownTypeAttribute>();
            foreach (var attribute in attributes)
            {
                ServiceKnownTypes.Add(attribute.Type);
            }
        }

        public TEntity CreateEntity<TEntity>()
        {
            var runtimeType = ServiceKnownTypes.Single(t => typeof(TEntity).IsAssignableFrom(t));
            return (TEntity)Activator.CreateInstance(runtimeType);
        }
    }

Which is then useable like so:

    [ServiceContract(Namespace = "http://mywebsite.com/MyProject")]
    public interface IMiscellaneous
    {
        [OperationContract]
        [ServiceKnownType(typeof(Employee))]
        IEnumerable<IEmployee> SearchEmployee(string SearchText);
    }

    public class Miscellaneous : IMiscellaneous
    {
        private readonly EntityLoader<IMiscellaneous> _entityLoader = new EntityLoader<IMiscellaneous>();
        public IEnumerable<IEmployee> SearchEmployee(string SearchText)
        {
            List<IEmployee> employees = new List<IEmployee>();

            IEmployee employee = _entityLoader.CreateEntity<IEmployee>();
            employee.EmployeeId = "12345678";

            employees.Add(employee);
            return employees;
        }
    }

Obviously, the above code assumes that ALL of your service entities will contain public parameterless constructors and that there will only be one ServiceKnownType that implements each interface.

Otros consejos

Consider a design change - Use the factory pattern to create an instance of your employee.

public EmployeeFactory : IEmployeeFactory 
{
  public IEmployee CreateEmployee() 
  {
    return new Employee();
  }
}

And introduce a dependency on the Factory from your middleware, so creating a new IEmployee becomes:

public class Miscellaneous : IMiscellaneous 
{ 
    private readonly IEmployeeFasctory _employeeFactory;

    public class Miscellaneous(IEmployeeFactory employeeFactory)
    {
        _employeeFactory = employeeFactory;
    }

    public IEnumerable Search_Employee (string searchText) 
    { 
       List employees = new List();
       IEmployee employee = _employeeFactory.CreateEmployee();  
       employee.EmployeeId = "12345678";
       employees.Add(TempObject);
       return employees;
}

And then you can inject your EmployeeFactory into Miscellaneous. And should Employee one day become deprecated and Employee2 comes along, just change the factory!

As rich.okelly points out in another answer, IEmployeeFactory should be used to create instances of the IEmployee interface, since IEmployee isn't a Service, but an Entity.

The IEmployeeFactory interface, on the other hand, is a Service, so should be injected into the service class using Constructor Injection. Here's a write-up of enabling Constructor Injection in WCF.

Had a discussion within the team.

1) Constructor based implementation is not comfortable..The service would be IIS hosted and consumed as a web-reference.Cannot ask client systems to provide FactoryImplementatedObjects in Miscellaneous class call.

2) Entity based factories is also not absolutely accurate.If I happen to have say 20 specific entities in my project like Employee,Material,Project,Location,Order then I need to have 20 Factories.Also the Miscellaneous class will have several custom constructors to support specific contract calls..

I have prepared a system which is working and DI is achieved to a great level but I feel like I am cheating OOPS..Doesnt feel correct at heart..but cannot be refuted to be wrong..Please check and let me know your comments.

I now have a IEntity Interface which is the base for all other Entities.

namespace BusinessModel.Interfaces
{
    public interface IEntity
    {
        string EntityDescription { get; set; }
    }
}

Hence forth all will implement this.

namespace BusinessModel.Interfaces
{
    public interface IEmployee : IEntity
    {
        string EmployeeId { get; set ; } 
    }
}

namespace BusinessModel.Interfaces
{
    public interface IProject : IEntity
    {
        string ProjectId { get; set; }
    }
}

and so on..(Interface implementing interface..absolutely ridiculous,cheating but working)

Next,An Enum type is declared to have a list of all Entities...

namespace MiddleWare.Common
{
    internal enum BusinessModel
    {
        IEmployee,
        IProject
    }
}

A DI Helper class is created which will henceforth be considered a part of Business Model and any changes to it (Implementation,Naming..) would be taken as a Business Shift.So if DIHelper class has to become DIHelper2 then this is like BIG.(Can this also be avoided??)

namespace MiddleWare.Common
{
    internal sealed class DIHelper
    {
        internal static IEntity GetRequiredIEntityBasedObject(BusinessModel BusinessModelObject)
        {
            switch (BusinessModelObject)
            {
                case BusinessModel.IEmployee:
                    return new Employee();
            }

            return null;
        }
    }
}

Function is Self Explanatory...

So now finally,the contract and implementation...

namespace MiddleWare.ServiceContracts
{
[ServiceContract(Namespace = "http://mywebsite.com/MyProject")] 
public interface IMiscellaneous
    {
    [OperationContract]
    [ServiceKnownType(typeof(MiddleWare.Classes.Employee))]
    IEnumerable<IEmployee> Search_Employee
        (string SearchText);
    }
}

namespace MiddleWare.ServiceClasses
{
    public class Miscellaneous : IMiscellaneous
    {
        public IEnumerable<IEmployee> Search_Employee
            (string SearchText)
        {
            List<IEmployee> IEmployeeList = new List<IEmployee>();

            IEmployee TempObject = (IEmployee)DIHelper.GetRequiredIEntityBasedObject(MiddleWare.Common.BusinessModel.IEmployee);
            TempObject.EmployeeId = "12345678";

            IEmployeeList.Add(TempObject);
            return IEmployeeList;
        }
    }
}

What do you say?? My Team is happy though :)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top