SOA问题:公开实体

我想在我的3层结构中加入SOA模式。 我在BLL和UI之间创建了一个服务层(WCF主机)。 我的结构设置现在看起来像这样

UI WCF BLL DAL

 

问题是,我将我的实体放在单独的DLL中(并且它在除了UI之外的所有层中都可见)现在,我需要公开它以便我的服务的使用者可以使用它。在这种情况下,UI。 我怎么可能这样做?

Entities.DLL

  namespace Entities { public class Account { public string AcctID { get; set; } public string AcctName { get; set; } } } 

现在,我打算在WCF中使用它

服务接口层

  public class AccountService : IAccountService { public Account GetAccount(string AcctID) { //fetch from DAL through BLL } } 

只是属性我的实体可以吗? (注意,我也在使用DAL和BLL中的实体)

  using System.Runtime.Serialization; namespace Entities { [DataContract] public class Account { [DataMember] public string AcctID { get; set; } [DataMember] public string AcctName { get; set; } } } 

有什么建议吗?

这是适合我们的系统:

您通常应该使用数据传输对象来反映客户端期望的数据。 业务层应该定义这些DTO及其存储库接口。 数据层应实现存储库接口,将数据层实体转换为DTO。 WCF层应该只是您的各种存储库接口方法的外向包装器。

这样,它看起来更像是这样的:

  UI --- 
   |  BLL  -  DAL
  WCF --- /
     [DTO]
     [库]
                  [实体]

在我看来,我认为WCF层是UI层的一部分,所以我觉得让他们都知道业务层中定义的对象。 但是,您可以更进一步,并使WCF层负责将业务对象转换为DTO:

  UI  -  WCF  -  BLL  -  DAL
  [DTO]
          [知识库]
          [Business Objects]
                               [实体]

这样,每个层最多只能识别其每一侧的单个层。 DTO可以注释为序列化或其他任何东西,因为它们实际上仅用于此目的。 只有数据访问层才能识别您的数据实体。

在回复您的评论时:

如果您的实体是在数据访问层中定义的,那么它们实际上不是DTO。 他们正在为您的数据层建模,这不一定直接转换为UI中您需要的对象。

我建议为您的存储库定义接口的原因是您可以使用dependency injection来放松WCF层和业务层之间的耦合。 这也将使您的WCF层可unit testing,因为您可以创建模拟特定情况的假或模拟存储库实现。

在我推荐的第一个模型中,您的WCF方法看起来几乎与您的存储库方法完全相同,因此典型的WCF基本上只是“包装”存储库方法:

 public IEnumerable GetActiveTasks() { return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser); } 

我可以告诉你我是怎么做到的。

我有单独的DTO和实体 – 并不总是1:1的关系。 我真的不喜欢在我的实体中拥有所有属性。 (此外,它打破了封装,因为所有属性都是突然读写的。

如果你想在两者之间轻松转换 – 有一些库可以让它像AutoMapper一样容易(ier)。

如果您将实体用作DTO,您通常会发送太多数据 – 例如,具有多个类型为Order的OpenOrders的帐户的订单。 每当您获取一个订单时,您也将获得该帐户的所有未结订单。

其次 – 我会在UI上使用与在服务层中使用相同的business-dll – 因此我可以在将其发送到服务器之前在客户端进行validation。 这部分当然是可选的 – 你也可以复制逻辑(但我也讨厌重复:-))。

希望这能为你带来一些方向。

我想我使用了AutoMapper。

我最后通过WCF将实体暴露为DTO ..简单的方法..

实体

  namespace Entities { public class Account { public string AccountNo { get; set; } public string AccountName { get; set; } } } 

BLL

  namespace BLL { // This defines the repository interface. public interface IAccountRepository { Account GetAccount(int accountId); } public class AccountRepository { public Account GetAccount(int accountId) { // get the Account object from the data layer. } } // Using an interface makes it easy to swap various implementations. // The implementation above would be the one you'd normally use, but you could // create others like this to help with unit testing and such. public class FakeAccountRepository : IAccountRepository { public Account GetAccount(int accountId) { return new Account { AccountName = "JuanDeLaCruz", AccountNo = "123456" }; } } } 

WCF

 [ServiceContract] public interface IService { [OperationContract] AccountDTO GetAccount(int accountId); } [DataContract] public class AccountDTO { [DataMember] public string AccountNo { get; set; } [DataMember] public string AccountName { get; set; } } public class Service : IService { // Define a Factory in your .svc file to inject a repository implementation. // It's best to use an IoC container like Ninject for this sort of thing. public Service( // no pun intended IAccountRepository accountRepository) { _accountRepository = accountRepository } public AccountDTO GetAccount(int accountId) { Mapper.CreateMap(); var account = _accountRepository.GetAccount(accountId); var accountDto = Mapper.Map(account); return account; } } 

WCF Aspx消费者

  protected void Page_Load(object sender, EventArgs e) { ServiceClient accountService= new ServiceClient(); AccountDTO account = accountService.GetAccount(); Response.Write(account.AccountName); } 

请评论任何建议/更正人.. ^^

感谢斯蒂芬林爵士和哥布林

DTO是非常好的方法,在某些情况下它们是绝对必要的。 其中一些情况是:

  • 大项目 – 与其他场景一起使用。 分离关注很重要。
  • 实体是域对象=包含业务逻辑
  • 实体以某种方式特定于.NET,并且必须从其他平台使用服务
  • 服务层为每个操作公开专用数据类型,而不是CRUDy接口。 例如,用于选择的操作可以返回具有诸如创建日期,最后修改日期等数据的对象。但是用于更新的操作不需要从客户端传输该数据,因此它使用DTO而不是这些字段。 你通常走得更远,你没有纯粹的选择和更新,但有一些真正的业务function。

另一方面,您的架构应该由您的需求和应用程序的预期复杂性和大小驱动。 DTO涉及许多额外工作=额外费用。 对于较小的简单项目,其中您的服务仅由您使用.NET编写的UI客户端使用,在单独的程序集中定义您的实体没有任何错误,标记这些实体具有用于序列化和数据注释的属性(可用于两者的validation)客户端和服务器端)或其他validation属性(例如来自企业库的validation应用程序块)并在包括UI客户端的所有应用程序层之间共享此程序集。 简单第一。

如果您想在客户端公开服务合同,最简单的方法就是这样

 [Serializable] public class Accountd { public string AccountNo { get; set; } public string AccountName { get; set; } } 

如果您需要更多控制哪个成员应该越过边界,那么使用它

 [DataContract] public class Account { [DataMember] public string AccountNo { get; set; } public string AccountName { get; set; } **// client won't see this data.** }