2010-10-07 50 views
1

我想将SOA模式合并到我的3层结构中。我在BLL和UI之间创建了一个服务层(WCF主机)。 我的结构设置是现在看起来是这样SOA问题:公开实体

UI <> WCF <> BLL <> DAL

<---[Entities] ---> 

的问题是,我有我在单独的DLL实体(和它在除用户界面外的所有图层中都可见) 现在,我需要公开它,以便我的服务的使用者可以使用它。在这种情况下,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; } 
     } 
    } 

任何建议家伙?

回答

2

下面是为我们工作的系统:

您应该通常使用反映你期待需要在客户端的数据的数据传输对象。业务层应该定义这些DTO以及它们的存储库接口。数据层应该实现存储库接口,将数据层实体转换为DTO。 WCF层应该简单地成为各种存储库接口方法的向外包装器。

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

 
UI ---\ 
    |  BLL -- DAL 
WCF---/ 
    [  DTO ] 
    [Repositories] 
       [Entities] 

在我的脑海里,我看到了WCF层作为UI层的一部分,所以我觉得他俩都知道没事在业务层中定义的对象。但是,你可以把它一步,使WCF层负责业务转换的对象为DTO的:

 
UI -- WCF -- BLL -- DAL 
[ DTOs ] 
     [  Repositories  ] 
     [ Business Objects ] 
           [Entities] 

通过这种方式,每一层只知道最多每侧单层的。 DTO可以注释为序列化或其他,因为它们实际上只是为此目的而设计的。只有数据访问层了解您的数据实体。

在响应到您的评论:

如果你的实体在你的数据访问层定义,那么他们真的是没有DTO的。他们正在为您的数据层建模,该数据层不一定会直接转换为您在UI中需要的对象。

我建议为存储库定义接口的原因是,您可以使用依赖注入来放松WCF层和业务层之间的耦合。这也将使您的WCF图层单元可测试,因为您可以创建模拟特定情况的假或模拟存储库实现。

在我推荐的第一款车型,您的WCF方法看起来几乎完全一样你的资料库的方法,所以一个典型的WCF将基本上只是“包装”一库方法:

public IEnumerable<Task> GetActiveTasks() { 
    return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser); 
} 
+0

感谢瘦长为详细的说明。我还没有尝试实体到DTO方法的映射,所以我有很多的研究要做,我认为这是要走的路。但我只关注DTO和实体,ENL对BLL和DAL可见,而DTO将用于从WCF到UI的数据传输。 WCF将处理映射.. – CSharpNoob 2010-10-07 15:58:27

+0

这是一个很好的开始。我知道,看起来几乎完全一样的DTO和实体听起来有点多余,但如果您的项目变得非常庞大,它将带来收益。 – StriplingWarrior 2010-10-07 16:05:15

+0

你可以在我的答案中加入存储库吗?我会在哪里放?谢谢先生.. – CSharpNoob 2010-10-07 16:42:55

1

我可以告诉你我是怎么做到这一点。

我有单独的DTO和实体 - 它并不总是1:1的关系。我真的不喜欢在我的实体中具有所有的属性。 (另外,如果你想在两者之间轻松转换打破封装的所有属性都突然读写

- 。有图书馆,以方便(IER),如AutoMapper

如果使用实体作为DTO,您经常会发送太多的数据 - 例如,一个订单中有一个账户拥有多个订单类型的OpenOrders。每次获取一笔订单时,您也将获得该账户的所有未结订单。将在UI上使用与我在服务层中使用的相同的Business-dll - 因此我可以在将它发送到服务器之前在客户端进行验证。当然,此部分是可选的 - 您也可以复制lo gic(但我讨厌重复:-))。

希望这会让你有点方向。

+0

对我来说听起来多余(映射ENtity-DTO)并且还有额外的编码。有没有将DTO和ENtity合为一体的方法? – CSharpNoob 2010-10-07 15:46:36

+0

这不是多余的 - 它是关注点的分离 - 将所有责任放在一个大块头上是创造维护噩梦的一个可靠方法。 – Goblin 2010-10-07 17:41:03

1

我想我使用AutoMapper得到了IT。

我最后通过WCF ..the简单的方式暴露实体作为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<Account, AccountDTO>(); 
    var account = _accountRepository.GetAccount(accountId); 
    var accountDto = Mapper.Map<Account, AccountDTO>(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); 
} 

的任何建议/更正家伙请评论.. ^^

多亏了Stiffling爵士和地精

+0

按照要求,我修改了这个以包含存储库接口。 – StriplingWarrior 2010-10-07 17:32:25

+0

似乎听起来对我来说:-)只是一个细节 - 你可能只想运行Mapper.CreateMap ();一旦。每次创建它要么花费太高 - 要么第二次调用它时会抛出。 – Goblin 2010-10-07 19:40:18

+0

你能否回答http://stackoverflow.com/questions/9480869/alternatives-for-dto-domain-translation-layer-in-soa-wcf? – Lijo 2012-02-29 05:43:58

1

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

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

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

0

如果你想在客户端上暴露服务合同simpliest方式是这样的

[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.** 
}