2013-08-05 34 views
7

Tôi có dịch vụ web sẽ thực hiện những việc với một số dữ liệu được chuyển vào (cụ thể là InfoPath xml từ thư viện tài liệu SharePoint). Tôi hiện đang sử dụng Ninject để xử lý "chiến lược" dữ liệu biểu mẫu nào cần tải. Dưới đây là một số mã (câu hỏi sau):Ràng buộc Ninject dựa trên chuỗi

Web Service (Entry Point)

namespace Web.Services 
{ 
    public bool AddForm(XmlDocument form, string formName) 
    { 
     IKernel kernel = new StandardKernel(new FormsModule()); 
     var ctx = kernel.Get<IPFormDataContext>(formName); 

     return ctx.DoWork(form); 
    } 
} 

Ninject liên quan Những điều

namespace Core.Modules 
{ 
    public class FormsModule : NinjectModule 
    { 
     public override void Load() 
     { 
      Bind<IPFormDataContext>().ToSelf().Named("FormA"); 
      Bind<IPFormDataContext>().ToSelf().Named("FormB"); 
      // Snip 

      Bind<IPFormDataStrategy>().To<FormAStratgey>() 
       .WhenParentNamed("FormA"); 
      Bind<IPFormDataStrategy>().To<FormBStrategy>() 
       .WhenParentNamed("FormB"); 
      // Snip 
     } 
    } 
} 

mẫu liên quan Những điều

namespace Core.Forms 
{ 
    public class IPFormDataContext 
    { 
     private IPFormDataStrategy _ipFormDataStrategy; 

     public IPFormDataContext(IPFormDataStrategy strategy) 
     { 
      _ipFormDataStrategy = strategy; 
     } 

     public bool DoWork(XmlDocument form) 
     { 
      return _ipFormDataStrategy.DoWork(form); 
     } 
    } 

    public abstract class IPFormDataStrategy 
    { 
     public abstract bool DoWork(XmlDocument form); 
    } 
} 

namespace Core.Forms.FormStrategies 
{ 
    class FormAStrategy : IPFormDataStrategy 
    { 
     public override bool DoWork(XmlDocument form) 
     { 
      // Deserialize form using (xsd.exe generated) FormAData 
      // and perform some operation on the resulting data. 
      return resultOfWork; 
     } 
    } 
} 

FormBStrategy được nhiều giống , cũng như 7 chiến lược khác mà tôi không liệt kê. Tôi đang cố gắng tìm một cách để chuyển vào biểu mẫu xml tới webservice và gọi đúng mẫu deserialization dựa trên loại biểu mẫu đang đến.

Đoạn mã trên "hoạt động"; nhưng có vẻ như tôi đang làm một số vị trí dịch vụ ở Ninject, mà từ những gì tôi đang đọc là một điều xấu . Nhưng tôi không thể nghĩ ra một cách thích hợp để thực hiện điều này. Tôi chưa chết khi sử dụng Ninject, hoặc bất kỳ khuôn khổ IOC/DI nào cho vấn đề đó.

Là những gì tôi đang làm ... sai? Tôi có thể chỉ ra đúng hướng không?

+1

Điều này có vẻ như là một nơi tốt cho một chung: bạn có thể làm một cái gì đó như 'IPFormDataContext nơi T: IPFormDataStrategy' để làm sạch nó? Tôi không thực sự quen thuộc với ninject. – lukegravitt

Trả lời

3

Nếu các lớp bạn hiện diện trong mã ví dụ của bạn là chính xác (nghĩa là không có nhiều phương thức và thuộc tính). Sau đó, giải pháp đơn giản nhất có thể có tác dụng và bạn có thể loại bỏ một số lớp/phụ thuộc vào các lớp.

Một giải pháp đơn giản, mà không dựa trên một khuôn khổ/thùng sẽ là:

public static class FormsProcessing 
{ 
    private static ConcurrentDictionary<string, Func<FormProcessor>> _registeredProcessors = new ConcurrentDictionary<string, Func<FormProcessor>>(); 

    public delegate bool FormProcessor(XmlDocument form); 

    public static void RegisterProcessor(string formKey, Func<FormProcessor> formsProcessorFactory) 
    { 
     _registeredProcessors.AddOrUpdate(formKey, formsProcessorFactory, (k, current) => formsProcessorFactory); 
    } 

    public static FormProcessor GetProcessorFor(string formKey) 
    { 
     Func<FormProcessor> processorFactory; 
     if (_registeredProcessors.TryGetValue(formKey, out processorFactory); 
      return processorFactory(); 
     return null; 
    } 

    public static bool Process(string formKey, XmlDocument form) 
    { 
     var processor = GetProcessorFor(formKey); 
     if (null == processor) 
      throw new Exception(string.Format("No processor for '{0}' forms available", formKey)); 
     return processor(form); 
    } 
} 

Cách sử dụng:

namespace Web.Services 
{ 
    public class MyServiceClass 
    { 
     public bool AddForm(XmlDocument form, string formName) 
     { 
      return FormsProcessing.Process(formName, form); 
     } 
    } 
} 

Nó rất đơn giản và rõ ràng, và không cần hoặc tiếp xúc với bất kỳ phụ thuộc trên một số cấu trúc của các lớp học IPFormDataContextIPFormDataStrategy. Sự phụ thuộc rõ ràng duy nhất bạn có là trên một đại biểu có chữ ký FormProcessor.

Tương tự như một container, bạn sẽ cần phải thực hiện việc đăng ký ở đâu đó:

FormsProcessing.RegisterProcessor("FormA",() => new FormAStrategy().DoWork); 
FormsProcessing.RegisterProcessor("FormB",() => new FormBStrategy().DoWork); 

Ngoài ra nó sẽ là dễ dàng để thêm một số hình thức (ước dựa) đăng ký tự động bằng cách quét hội cho ước (ví dụ một chữ ký giao diện).

3

Hai điều tôi không thích:

  1. Tạo một hạt nhân bên trong phương pháp AddForm. Điều này sẽ không bao giờ xảy ra, vì nó đảo ngược mẫu IoC - thay vào đó, lớp mà AddForm thuộc về nên yêu cầu bất kỳ sự phụ thuộc nào mà nó cần.
  2. Sử dụng dây ma thuật. Có vẻ như không đúng khi yêu cầu người tiêu dùng của AddForm() gửi một chuỗi đặt tên cho chiến lược.

Tôi không chắc chắn cách giải quyết vấn đề này; một điều cần lưu ý là thêm một sự phụ thuộc Func<string, IPFormDataStrategy> vào lớp sở hữu AddForm (gọi là lớp X). Tôi đang tưởng tượng một cái gì đó như thế này:

namespace Web.Services 
{ 
    public class X 
    { 
     private readonly Func<string, IPFormDataStrategy> _strategyResolver; 

     public X(Func<string, IPFormDataStrategy> strategyResolver) 
     { 
      _strategyResolver = strategyResolver; 
     } 

     public bool AddForm(XmlDocument form, string formName) 
     { 
      return _strategyResolver(formName).DoWork(form); 
     } 
    } 
} 

Sau đó, bạn có thể sử dụng ToMethod để ràng buộc Func<string, IPFormDataStrategy>:

public class FormsModule : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<FormAStrategy>().ToSelf(); 
     Bind<FormBStrategy>().ToSelf(); 

     Bind<Func<string, IPFormDataStrategy>>().ToMethod(context => 
      new Func<string, IPFormDataStrategy>(formName => { 
       switch (formName) 
       { 
        case "FormA": 
         return context.Kernel.Get<FormAStrategy>(); 
         // Note, could also simply "return new FormAStrategy()" here. 
        case "FormB": 
         return context.Kernel.Get<FormBStrategy>(); 
        default: 
         throw new InvalidOperationException(formName + " is unrecognized"); 
       } 
      }) 
     ); 
    } 
} 

Bạn có thể tìm thấy điều này không cần thiết phức tạp, và có thể nó là ... Tôi thích nó vì nó làm cho sự phụ thuộc của lớp X rõ ràng (có nghĩa là, có được một chiến lược cho một tên biểu mẫu), thay vì cho phép nó truy cập vào toàn bộ hạt nhân. Cách tiếp cận này cũng củng cố logic để đưa một chiến lược vào một câu lệnh chuyển đổi đơn. Nó vẫn dựa vào các dây ma thuật, nhưng tôi không chắc chắn làm thế nào để có được xung quanh đó, mà không biết bối cảnh nhiều hơn ...

+0

Ứng dụng đang gọi dịch vụ web là Trình xử lý sự kiện danh sách SharePoint. Tôi muốn giữ trình xử lý sự kiện càng cơ bản càng tốt, do đó là chuỗi phép thuật. Các yêu cầu của dự án này bao gồm khả năng thêm các hình thức bổ sung với tác động tối thiểu trong tương lai.Tôi không sao với việc cập nhật dịch vụ web theo thời gian, nhưng việc nâng cấp một trình xử lý sự kiện danh sách đã là một nỗi đau. – Slipfish

1

Service Locator là generally an anti-pattern, nhưng điều quan trọng là phải hiểu tại sao nó là một chống mẫu. Những lý do thường liên quan đến tái cấu trúc và an toàn loại. Tôi nghĩ rằng cách tốt nhất để xác định xem bạn đang làm điều gì đó sai là để giảm vấn đề với các yêu cầu đơn giản tuyệt đối của nó, và sau đó đánh giá con đường đơn giản nhất để đến đó.

Theo tôi được biết, yêu cầu của bạn là:

  1. Nhận dữ liệu dạng xml trong một webservice
  2. Dựa vào loại hình thức, gọi là logic thích hợp

thắc mắc của tôi với bạn sẽ be:

  1. Làm cách nào để bạn xác định loại biểu mẫu? Nó là một lĩnh vực trong tài liệu xml? (nếu có, hãy sử dụng)
  2. Các loại biểu mẫu có khả năng thay đổi thường xuyên không?

Khi nhấn vào đó, bạn phải sử dụng một số loại số nhận dạng. Như @McGarnagle đã chỉ ra, các chuỗi ma thuật có thể không đồng bộ với mã. Bạn có thể sử dụng tên Loại của lớp Chiến lược, nhưng nó có cùng một vấn đề 'có thể không đồng bộ'.

Nếu các loại biểu mẫu không có khả năng thay đổi, chỉ cần sử dụng câu lệnh chuyển đổi và tự khởi tạo bản thân Chiến lược. Đơn giản là mẫu thiết kế tốt nhất để tuân theo để bảo trì.

Nếu họ có khả năng thay đổi, Service Locator có thể hoạt động. Nếu việc triển khai Service Locator của bạn chỉ giới hạn ở mã này, nó sẽ không quá khủng khiếp để duy trì.

Và trên Ninject, tôi không chắc chắn nếu điều này benchmark vẫn hợp lệ, nhưng Funq nhanh hơn nhiều và tôi thích cú pháp tốt hơn. (Nếu bạn quyết định sử dụng vùng chứa DI):

Các vấn đề liên quan