Похоже, вы пытаетесь использовать 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 вокруг конкретных операций было, безусловно, наиболее успешным как с точки зрения сервисов, так и с точки зрения потребительских реализаций. Он позволяет операциям развиваться независимо и очень четко определяет, что ожидается от службы и что будет возвращено клиенту.
person
jageall
schedule
28.02.2012