I would like to incorporate SOA pattern in my 3 tier structure. I created a Service layer (WCF Host) between the BLL and the UI. My structure setup is now looks like this
UI <> WCF <> BLL <> DAL
<---[Entities] --->
The problem is, I have my entities in separate DLL (ANd it was visible in ALL Layers except on the UI) Now, I need to expose it so that the consumer of my service can use it.In this case, the UI. How can I possibly do that?
Entities.DLL
namespace Entities
{
public class Account
{
public string AcctID { get; set; }
public string AcctName { get; set; }
}
}
now, Im planning to use it in WCF
Service Interface Layer
public class AccountService : IAccountService
{
public Account GetAccount(string AcctID)
{
//fetch from DAL through BLL
}
}
Is it ok to just Attribute my Entities? (Note, I'm also using the entities in DAL and BLL)
using System.Runtime.Serialization;
namespace Entities
{
[DataContract]
public class Account
{
[DataMember]
public string AcctID { get; set; }
[DataMember]
public string AcctName { get; set; }
}
}
Any suggestion guys?
Here's the system that works for us:
You should typically use a Data Transfer Object that reflects the data you're expecting to need on the client side. The Business Layer should define these DTOs, along with their repository interfaces. The Data Layer should implement the repository interfaces, converting your data-layer entities into the DTOs. The WCF layer should simply be an outward-facing wrapper for your various repository interface methods.
This way, it looks more like this:
UI ---\ | BLL -- DAL WCF---/ [ DTO ] [Repositories] [Entities]
In my mind, I see the WCF layer as being a part of the UI layer, so I'd feel all right having them both be aware of objects defined in the business layer. However, you could take it one step further, and make the WCF layer be in charge of converting your business objects into DTOs:
UI -- WCF -- BLL -- DAL [ DTOs ] [ Repositories ] [ Business Objects ] [Entities]
This way, each layer is only aware of at most a single layer on each side of it. The DTOs can be annotated for serialization or whatever, because they really are only intended for that purpose. Only the Data-Access Layer is aware of your Data Entities.
In Response to your comment:
If your entities are defined in your data-access layer, then they really are not DTOs. They are modeling your data layer, which doesn't necessarily translate directly into the objects you need in the UI.
The reason I'm suggesting defining interfaces for your repositories is so that you can use dependency injection to loosen the coupling between your WCF layer and your business layer. This will also serve to make your WCF layer unit-testable, because you can create fake or mock repository implementations that simulate a particular situation.
In the first model I recommended, your WCF methods would look almost exactly like your repository methods, so a typical WCF would basically just "wrap" a repository method:
public IEnumerable<Task> GetActiveTasks() {
return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser);
}
I can tell you how I do it.
I have separate DTO's and Entities - and it is not always a 1:1 relationship. I really don't like to have all the attributes in my entities. (Also, it breaks encapsulation as all properties are suddenly read-write.
If you want easy conversion between the two - there are libraries to make it easy(ier) like AutoMapper.
If you use the entities as DTOs you will often send way too much data - e.g. an Order having an Account having multiple OpenOrders of type Order. Everytime you fetch one order, you will get all the open orders of the Account as well.
Secondly - I would use the same business-dll on the UI as I use in the service layer - so I can validate on the client-side before sending it to the server. This part is optional of course - you could also duplicate the logic (but I hate duplication as well :-)).
Hope this gets you a bit of direction.
I think I Got IT using the AutoMapper.
I Finally expose the Entity as a DTO via WCF ..the simple way..
the Entity
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 Consumer
protected void Page_Load(object sender, EventArgs e)
{
ServiceClient accountService= new ServiceClient();
AccountDTO account = accountService.GetAccount();
Response.Write(account.AccountName);
}
Please comment for any suggestions/corrections guys.. ^^
THANKS to Sir Stiffling and Goblin
DTOs are very good approach and in some scenarios they are absolutely necessary. Some of these scenarios are:
- Big projects - goes together with other scenarios. Separation of Concerns is important.
- Entities are domain objects = contains business logic
- Entities are somehow .NET specific and service has to be used from other platforms
- Service layer exposes specialized data types for each operation instead of CRUDy interfaces. For example operation for selecting can return object with data like creation date, last modification date, etc. But operation for updating does not need to transfer this data from client so it uses DTO whithout these fields. You usually go even further and you don't have pure selecting and updating but some real business functions.
On the other hand your architecture should be driven by your requirements and expected complexity and size of your application. DTO involve a lot of additional work = additional costs. For smaller simple project where your service will be consumed only by your UI client written in .NET there is nothing wrong in defining your entities in separate assembly, marking these entities with attributes for serializing and with data annotations (can be used for validation on both client and server side) or with other validation attributes (for example Validation application block from Enterprise library) and sharing this assembly among all application layers including UI client. Simplicity first.
If you want to expose service contract on the client the simpliest way is this
[Serializable]
public class Accountd
{
public string AccountNo { get; set; }
public string AccountName { get; set; }
}
and if you need more control on which member should cross the boundary then use this
[DataContract]
public class Account
{
[DataMember]
public string AccountNo { get; set; }
public string AccountName { get; set; } **// client won't see this data.**
}
来源:https://stackoverflow.com/questions/3883158/soa-question-exposing-entities