在维护领域驱动设计架构的同时,如何使用WCF服务设置Ninject ?
本文关键字:何使用 WCF 服务 Ninject 设置 维护 | 更新日期: 2023-09-27 18:12:38
我试图实现一个概念的证明,我写了一个模块(让我们说论坛为了这个讨论的目的),遵循领域驱动的设计准则,将有一个可插拔的存储库,整个模块将是可插拔的本地web服务器(本地托管dll)或通过WCF服务。
遵循领域驱动设计准则,我将有一组业务对象,它们将被这样编写:
public class Forum
{
readonly IRepository _forumRepository;
public Forum(IRepository forumRepository)
{
_forumRepository = forumRepository;
}
public string Name { get; set; }
public void Save()
{
_forumRepository.SaveForum(this);
}
}
假设web应用程序需要创建一个新的论坛。
如果模块通过dll文件在本地托管,这一切都很好。web应用程序代码将简单地使用Ninject(或任何DI)实例化业务对象并调用Save方法。
但是,如果实现者想要在两者之间引入服务层呢?假设他们希望在应用程序层和数据库层之间引入一个WCF服务层,因为他们希望物理架构是Web服务器->应用程序服务器->数据库服务器。显然,论坛模块dll将由web服务器和应用程序服务器托管。然而,该模块不再可用。
如果web服务器使用存储库注入实例化对象并将其传递到WCF服务层,即使我允许我的业务对象被序列化,_forumRepository字段也会丢失。
实现者应该能够让应用服务器选择存储库。那么,有了这个需求,在从web服务器接收到已经实例化的对象之后,实现者如何在应用服务器端注入存储库呢?是否有一种方法可以告诉WCF服务在反序列化过程中实例化对象时注入存储库?
我阅读了文章如何在WCF服务中使用依赖注入(Ninject) ,并回顾了示例解决方案,但这只演示了如何将存储库直接注入到服务中。这似乎不能解决我在这里讨论的问题。下面是一个示例,说明如果编写正确,我将如何预测代码。如前所述,这里的主要问题是我不知道如何在服务端将存储库注入域对象。
应用程序代码(Web App或Windows App):
Forum newForum = new Forum();
//Set some properties on newForum.
IKernel kernel = new StandardKernel(); //Instantiate ninject kernel.
//Yes, I know using the using statement means that IService needs to derive from IDisposable. A local service would just have an empty implementation for IDisposable since there would be nothing to clean up and this would allow different service architectures to be plugged into the application.
using (IForumService forumService = kernel.Get<IForumService>()) //Get concrete implementation bound to IForumService via ninject.
{
//Send forum to service layer for local OR WCF processing (on an app server)
forumService.SaveForum(newForum);
}
Services Code(这就是问题所在):
public class WCFForumService : IForumService
{
public void SaveForum(Forum forum)
{
//PROBLEM SCENARIO: How do I now inject the repository I want to use since I already have an instance of forum?
//Can I make WCF inject the repository when it is deserializing the forum object before passing it into this method?
forum.Save();
}
}
如果可能的话,我宁愿不强迫我的论坛模块的实现者为每个域对象创建dto。这违反了DRY原则。如果我可以创建我的领域对象,使它们具有足够的通用性和/或可扩展性,从而可以毫不费力地用作dto本身,那就太好了。这样,我就可以使用数据注释对领域对象进行业务规则验证,而实现者可以在Web Forms、MVC3或Silverlight项目中使用这些对象,而不必手动重新进行所有验证。
编辑:以上是完全错误的方法。编辑以演示更有效的方法。
public class Forum
{
public Forum(string name)
{
Name = name;
}
public string Name { get; set; }
}
public interface IForumRepository : IRepository<Forum>
{
void Add(Forum forum);
Forum this[object id] { get; set; }
}
//Client Code (Called from the WCF service hosting the domain)
public class WCFAppService
{
public void SaveForum(ForumDTO forumInfo)
{
IKernel kernel = StandardKernel();
IForumRepository repository = kernel.Get<IForumRepository>();
Forum forum = repository[forumInfo.ID];
if (forum != null)
{
repository[forumInfo.ID] = forumInfo.CopyTo(forum); //Save the Forum to the db.
}
else
{
repository.Add(ForumFactory.CreateFrom(forumInfo)); //Insert the Forum into the db.
}
}
}
我在尝试这个原型时遇到的很多问题是我过于关注基础设施问题(即。DI,服务器架构等),同时在此过程中尝试学习DDD。我对DDD的了解使我得出这样的结论:当试图弄清楚如何构建一个DDD解决方案时,请忘记架构和技术,直到您很好地掌握了DDD将为您做什么。我这样做是因为在我目前正在进行的实际DDD项目中,DI似乎是一个不必要的复杂性。这就是DDD如何使您的代码变得简单。
通过将您的存储库传递给web服务器,您将跳过层。没有什么可以阻止您调用save there或以业务规则不允许的方式操作数据。此外,您不希望关心web服务器上的数据库细节。这是应用服务器的责任。
应该有一个完全不同的接口之间的应用程序和数据库服务器之间的应用程序和web服务器。应用服务器的界面应该包含你想要在web服务器上执行的操作。
假设你有一个银行账户系统。并且想在两个账户之间进行交易。在这种情况下,在您的解决方案中,您将发送两个相关帐户到web服务器,它将第一个帐户的余额减少转账金额,并将第二个帐户的余额增加相同金额。然后,它将帐户发送回应用服务器进行保存。
但这是错误的。正确的做法是在应用服务器上提供一项服务,该服务提供一种交易方法,该方法获取所涉及的账号和要转账的金额,并在应用服务器上完成完整的操作。
在您的示例中,您应该提供一个方法ChangeForumName(int forumId, string newName)
。现在,您可以确保名称必须满足的规则。不是空的。
如果您真的想在通过网络发送后在域对象中拥有服务,那么将您的服务作为对象的属性,并在"Get"方法中使用某种CommonServiceLocator模式来获取另一端的服务。查看这里获得更多信息http://commonservicelocator.codeplex.com/(即,而不是WCF注入服务,对象调用CommonServiceLocator来获取它需要的服务的实例)。
另一种方法可能是编写一个自定义的wcf行为来注入服务。查看这篇文章了解更多细节- http://msdn.microsoft.com/en-us/magazine/cc163302.aspx但是,您应该更仔细地考虑您的体系结构和您想要实现的目标。如果你想有Web服务器->应用服务器->数据库服务器,那么你真的不应该发送你的对象到你的应用服务器,以便应用服务器可以注入服务,然后在你的对象上调用"保存"方法。更简洁的设计是让应用服务器(在本例中为wcf)提供一个带有保存方法的服务,该方法将"论坛"对象作为参数。然后,该服务将按照您引用的文章将存储库注入其中。
如你所知,当使用WCF时,你实际上只是通过网络发送序列化的数据,而不是实际的对象。物体在另一边重建。许多架构使用数据传输对象来进行WCF调用,然后用这些dto来填充域模型(尽管你可以只使用域对象,记住你只是在发送数据)。
本文对这些主题进行了很好的讨论http://msdn.microsoft.com/en-us/magazine/ee236638.aspx(尽管您的问题不是关于dto的,但该原则也适用于领域对象,并且可能会给您一些关于如何将各个部分组合在一起的思考食物)。也值得看看其他人是如何做类似事情的。这里有一些很棒的示例应用程序,例如http://sankarsan.wordpress.com/2009/04/12/a-layered-aspnet-mvc-application-part-i/(你需要阅读整个系列,因为他在前两部分没有讨论wcf)