我想在我的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; }
}
}
有什么建议吗?
答案 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是非常好的方法,在某些情况下它们是绝对必要的。其中一些情况是:
另一方面,您的架构应该由您的要求以及应用程序的预期复杂性和大小驱动。 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.**
}