Я пытаюсь написать код, чтобы помочь модульному тестированию служб WCF. Доступ к этим сервисам осуществляется через класс фасадов, который создает экземпляр прокси, затем вызывает метод прокси и возвращает результат; для каждого метода прокси. Я хотел бы иметь возможность заменить текущий код создания чем-то, что создает реальный сервис или фальшивый.
Я не мог заставить это работать. Я свел его к следующему:
using System.ServiceModel;
namespace ExpressionTrees
{
public interface IMyContract
{
void Method();
}
public class MyClient : ClientBase<IMyContract>, IMyContract
{
public MyClient()
{
}
public MyClient(string endpointConfigurationName)
: base(endpointConfigurationName)
{
}
public void Method()
{
Channel.Method();
}
}
public class Test
{
public TClient MakeClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract, new()
{
return new MyClient("config");
// Error:
// Cannot convert expression of type 'ExpressionTrees.ServiceClient' to return type 'TClient'
}
}
}
Почему, хотя класс MyClient
является производным от ClientBase<IMyContract>
и реализует IMyContract
, я не могу вернуть экземпляр MyClient
из метода, предназначенного для возврата TClient
? TClient
определяет ограничение типа, которое, как я думал, означало то же самое.
Моя цель состояла в том, чтобы назвать код следующим образом:
public void CallClient<TClient>()
where TClient : ClientBase<IMyContract>, IMyContract
{
TClient client = null;
bool success = false;
try
{
client = MakeClient<TClient>();
client.Method();
client.Close();
success = true;
}
finally
{
if (!success && client != null)
{
client.Abort();
}
}
}
Но вместо того, чтобы всегда вызывать MakeClient<TClient>
, я хотел, чтобы модульный тест мог вводить фиктивный объект. Поскольку приведенный выше код зависит от ClientBase<IMyContract>
, IMyContract
, похоже, я пытался «синтезировать» универсальный класс, который бы удовлетворял этому ограничению.
Оглядываясь назад, это не имело бы смысла. В качестве примера можно ожидать, что экземпляр ClientBase<IMyContract>
будет создан таким образом, что будет создан объект Channel
, который затем может делегировать метод Close
.
Я решил использовать один и тот же код для реальных и поддельных сервисов. Сейчас я ввожу IMyService
и вызываю IMyService.Method
или client.Method
в зависимости от того, является ли мое введенное свойство пустым.