Похоже, вы пытаетесь использовать SOA для удаленного доступа к вашей объектной модели. Вам бы лучше было рассмотреть взаимодействия и возможности, которые вы хотите, чтобы ваш сервис представлял, и не раскрывать детали наследования реализации ваших сервисов.
Так что в этом случае, когда вам нужен список учетных записей, ваш интерфейс будет выглядеть примерно так:
[ServiceContract]
interface ISomeService
{
[OperationContract]
Collection<AccountSummary> ListAccountsForUser(
User user /*This information could be out of band in a claim*/);
}
[DataContract]
class AccountSummary
{
[DataMember]
public string AccountNumber {get;set;}
[DataMember]
public string AccountType {get;set;}
//Other account summary information
}
если вы решите пойти по маршруту наследования, вы можете использовать атрибут KnownType , но имейте в виду, что это добавит некоторую информацию о типе в сообщение, отправляемое по сети, что может ограничить вашу совместимость в некоторых случаях.
Обновление :
Я немного ограничился временем, когда отвечал, поэтому постараюсь уточнить, почему я предпочитаю этот стиль.
Я бы не советовал выставлять OOAD через DTO в отдельном слое, это обычно приводит к раздутому интерфейсу, где вы передаете много неиспользуемых данных и религиозно сопоставляете их с тем, что по сути является копией. Ваша модель домена со всей логикой удалена, а я просто не вижу значения. Я обычно проектирую свой уровень обслуживания вокруг операций, которые он предоставляет, и я использую DTO для определения взаимодействий служб.
Использование DTO, основанных на открытых операциях, а не на модели домена, помогает сохранить инкапсуляцию службы и уменьшает связь с моделью домена. Не раскрывая мою модель предметной области, мне не нужно идти на компромиссы в отношении видимости или наследования полей ради сериализации.
например, если бы я предоставлял метод Transfer из одной учетной записи в другую, интерфейс службы выглядел бы примерно так:
[ServiceContract]
interface ISomeService
{
[OperationContract]
TransferResult Transfer(TransferRequest request);
}
[DataContract]
class TransferRequest
{
[DataMember]
public string FromAccountNumber {get;set;}
[DataMember]
public string ToAccountNumber {get;set;}
[DataMember]
public Money Amount {get;set;}
}
class SomeService : ISomeService
{
TransferResult Transfer(TransferRequest request)
{
//Check parameters...omitted for clarity
var from = repository.Load<Account>(request.FromAccountNumber);
//Assert that the caller is authorised to request transfer on this account
var to = repository.Load<Account>(request.ToAccountNumber);
from.Transfer(to, request.Amount);
//Build an appropriate response (or fault)
}
}
теперь из этого интерфейса клиенту очень ясно, какие данные необходимы для вызова этой операции. Если бы я реализовал это как
[ServiceContract]
interface ISomeService
{
[OperationContract]
TransferResult Transfer(AccountDto from, AccountDto to, MoneyDto dto);
}
и AccountDto - это копия полей в учетной записи, как потребитель, какие поля мне следует заполнить? Все они? Если новое свойство добавлено для поддержки новой операции, все пользователи всех операций теперь могут видеть это свойство. WCF позволяет мне пометить это свойство как необязательное, чтобы не прерывать работу всех остальных моих клиентов, но если оно является обязательным для новой операции, клиент узнает об этом только при вызове операции.
Хуже того, как разработчик службы, что произойдет, если они предоставили мне текущий баланс? я должен верить этому?
Общее правило здесь - спрашивать, кому принадлежат данные, клиенту или услуге? Если клиент владеет им, он может передать его службе, и после выполнения некоторых базовых проверок служба может использовать его. Если сервис принадлежит ему, клиент должен только передать достаточно информации для сервиса, чтобы получить то, что ему нужно. Это позволяет службе поддерживать согласованность данных, которыми она владеет.
В этом примере службе принадлежит информация об учетной записи, а ключом для ее поиска является номер учетной записи. Хотя служба может проверять сумму (является положительной, поддерживаемой валютой и т. Д.), Она принадлежит клиенту, и поэтому мы ожидаем, что все поля в DTO будут заполнены.
Таким образом, я видел, что это было сделано всеми тремя способами, но проектирование DTO вокруг конкретных операций было безусловно самым успешным как с точки зрения реализации услуг, так и с точки зрения потребителей. Он позволяет операциям развиваться независимо и очень четко говорит о том, что ожидает служба и что будет возвращено клиенту.