SOA问题:公开实体

时间:2010-10-07 15:18:37

标签: c# wcf soa

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

  

用户界面<> WCF<> BLL<> DAL

   <---[Entities] --->

问题是,我将我的实体放在单独的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; }
       }
    }

有什么建议吗?

5 个答案:

答案 0 :(得分:3)

这是适用于我们的系统:

您通常应该使用数据传输对象来反映您在客户端需要的数据。业务层应该定义这些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);
}

答案 1 :(得分:1)

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

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

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

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

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

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

答案 2 :(得分:1)

我认为我使用了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<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);
 }

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

感谢Dr Stiffling和Goblin

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:0)

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

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