【问题标题】:DI in Service Contract WCF服务合同 WCF 中的 DI
【发布时间】:2011-11-22 11:42:30
【问题描述】:

请在下面找到我的代码。 Employee类实现了IEmployee接口。

    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;
           }
       }
    }

可见,上面的代码确实可以编译,但由于无法创建接口实例而无法工作。我怎样才能在这里实现 DI(依赖注入)...如果我写..

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

那么这个类将不仅依赖于接口,还依赖于类...假设一个晴天Employee类变成Employee2.会有两个地方的代码更改.. 1)[ServiceKnownType(typeof(MiddleWare.Classes.Employee2))]

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

我想避免这种情况。我们可以在 IOperationBehavior 的实现中做点什么吗?或者是否有一种 Ninject 方法可以实现这一点,或者我是否试图实现不可能?

【问题讨论】:

    标签: wcf service dependency-injection ninject contract


    【解决方案1】:

    考虑更改设计 - 使用工厂模式创建员工实例。

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

    并从你的中间件中引入对 Factory 的依赖,因此创建一个新的 IEmployee 变为:

    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;
    }
    

    然后您可以将您的 EmployeeFactory 注入 Miscellaneous。如果有一天 Employee 被弃用而 Employee2 出现,只需更换工厂!

    【讨论】:

    • ok. 然后代码变成 ....IEmployee TempObject = EmployeeFactory.CreateEmployee();所以你的建议是我们需要为我们拥有的每个实体都有工厂..(Employee, Material,Location,Project) 并通过各自的工厂调用它们...看起来不麻烦..
    • :(基于参数的构造函数在实现基本http绑定并作为webservice使用时抛出错误..
    【解决方案2】:

    正如rich.okelly 在另一个答案中指出的那样,IEmployeeFactory 应该用于创建IEmployee 接口since IEmployee isn't a Service, but an Entity 的实例。

    另一方面,IEmployeeFactory 接口是一个服务,因此应该使用 构造函数注入 将其注入到服务类中。 Here's a write-up of enabling Constructor Injection in WCF.

    【讨论】:

      【解决方案3】:

      在团队内部进行了讨论。

      1) 基于构造函数的实现并不舒适。该服务将由 IIS 托管并作为 Web 参考使用。不能要求客户端系统在 Miscellaneous 类调用中提供 FactoryImplementatedObjects。

      2) 基于实体的工厂也不是绝对准确的。如果我碰巧在我的项目中有 20 个特定实体,例如 Employee、Material、Project、Location、Order,那么我需要有 20 个工厂。Miscellaneous 类也将有几个自定义构造函数来支持特定的合约调用..

      我已经准备了一个正在运行的系统,并且 DI 达到了很高的水平,但我觉得我在作弊 OOPS..内心感觉不正确..但不能否认是错误的..请检查并让我了解你的 cmets。

      我现在有一个 IEntity 接口,它是所有其他实体的基础。

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

      从此以后大家都会实现这个。

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

      等等..(接口实现接口..绝对荒谬,作弊但有效)

      接下来,一个 Enum 类型被声明为具有所有实体的列表...

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

      创建了一个 DI Helper 类,该类今后将被视为业务模型的一部分,对它的任何更改(实施、命名..)都将被视为业务转变。因此,如果 DIHelper 类必须成为 DIHelper2,那么这是比如BIG。(这也可以避免吗??)

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

      功能不言自明...

      所以现在终于,合同和实施...

      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;
              }
          }
      }
      

      你说什么?? 不过我的团队很高兴 :)

      【讨论】:

        【解决方案4】:

        从您更新的要求来看,这个问题中没有与 DI 相关的内容...

        因此,要根据服务合同的服务已知类型创建类型,您可以使用:

        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);
                }
            }
        

        然后可以像这样使用:

            [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;
                }
            }
        

        显然,上述代码假定您的所有服务实体都将包含公共无参数构造函数,并且只有一个实现每个接口的 ServiceKnownType。

        【讨论】:

        • 点了。答案是我真正想要的,但无法正确提问。谢谢。 :-) 这就是我想要的...将对此进行更多探索并在需要时返回...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-18
        • 1970-01-01
        相关资源
        最近更新 更多