Xamarin PCL Refit 3.0.1, не похож на интерфейс Refit
Недавно я начал работать над проектом Xamarin для Android/iOS с классом PCL, в который я хочу поместить всю логику. Как и мои интерфейсы Refit, ReactiveUI ViewModels и т. Д., Но каждый раз, когда я пытаюсь выполнить свой код, я получаю сообщение о том, что Мой интерфейс не является интерфейсом Refit. В настоящее время мой интерфейс выглядит следующим образом.
public interface IMyApi
{
[Post("/authenticate")]
IObservable<Models.ApiResponses.AuthenticationResponse> SigninRaw([Body] JObject credentials);
[Get("/service")]
IObservable<Models.ApiResponses.MyListResponse> GetServiceListRaw();
[Get("/service/{id}/idstatus")]
IObservable<Models.ApiResponses.IdResponse> GetIdStatusRaw(string Id);
}
Насколько я знаю, это выглядит хорошо, и это также работает, когда я пытаюсь загрузить это с определенной платформы, такой как проект iOS. Но при попытке сделать это из PCL, если не удается! Я установил пакет Refit в оба моих проекта для платформы Android и iOS, и я ссылался на dll в PCL, что я пропустил?
Если вам нужна дополнительная информация или у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь спрашивать. Что ж, без дальнейших церемоний, спасибо за чтение, и, надеюсь, кто-то может помочь мне с этим, потому что я начал сходить с ума в последние пару дней.
Редактировать: добавлен метод вызова. Здесь я звоню из ViewModel
var client = new HttpClient(NetCache.UserInitiated)
{
BaseAddress = new Uri("https://api.address.com")
};
var api = RestService.For<IMyApi>(client); <= here it crashes
var response = api.SigninRaw(token);
1 ответ
Мне удалось отследить это, на самом деле есть несколько проблем. К счастью, есть способы обойти их.
Первая проблема заключается в том, что интерфейсы PCL не обнаруживаются в первую очередь. Refit запускает утилиту во время компиляции (InterfaceStubGenerator), которая сканирует подпапки для классов интерфейса и генерирует код реализации для каждого из них. Все они упакованы в промежуточный файл с именем RestStubs.g.cs, который включается в сборку. Эта утилита, однако, запускается только в тех проектах, к которым Refit был добавлен через nuget, и, поскольку она не включает проекты PCL, интерфейсы в этих проектах никогда не обрабатываются. Решение состоит в том, чтобы вызвать эту утилиту вручную на этапе предварительной сборки и включить созданный файл в каждый из проектов платформы. Перейдите к настройкам свойств вашего проекта PCL и добавьте следующее к этапам предварительной сборки:
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.Droid\RefitStubs.cs ..\..\..\ProjName
..\..\..\..\packages\refit.3.0.1\tools\InterfaceStubGenerator.exe ..\..\..\ProjName.iOS\RefitStubs.cs ..\..\..\ProjName
Это сгенерирует RefitStubs.cs для ваших проектов платформы, поэтому добавьте каждый файл в соответствующий проект.
Обычно это был бы конец, если бы не другая проблема. Обобщенные образцы RestService.For<>, которые вы вызываете для получения реализаций, предполагают, что классы реализации находятся в той же сборке, что и их соответствующие интерфейсы. Очевидно, что это не относится к проектам PCL. Чтобы обойти это, вам нужно реализовать собственную версию класса RestService, которая, вероятно, будет отвечать большинству ваших потребностей:
public static class PclRestService
{
public static T For<T>(string hostUrl)
{
var className = "AutoGenerated" + typeof(T).Name;
var typeName = typeof(T).Namespace + "." + typeof(T).Name.Replace(typeof(T).Name, className);
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var generatedType = assembly.GetType(typeName);
var requestBuilder = RequestBuilder.ForType<T>();
var client = new HttpClient(new HttpClientHandler()) { BaseAddress = new Uri(hostUrl) };
return (T)Activator.CreateInstance(generatedType, client, requestBuilder);
}
}
Вы тогда называете это так:
var netService = PclRestService.For<IMyApi>("http://jsonplaceholder.typicode.com");
var result = await netService.GetDataOrSomething();
Последняя проблема, с которой вы можете столкнуться - это множественные объявления класса PreserveAttribute, который объявлен в верхней части заглушек. Пока вы не добавляете интерфейсы Refit к проектам вашей платформы, этого не должно произойти, но если это произойдет, у вас есть 3 варианта:
- измените утилиту InterfaceStubGenerator, чтобы не создавать этот код
- написать препроцессор, чтобы вырезать этот код после его генерации
- перейдите в файл Refit "refit.targets" и закомментируйте строку
"<Exec Command="$(RefitExecCmd)" />
Папка "Инструменты для восстановления" содержит файл шаблона, используемый для создания стаб-файлов, но по какой-то странной причине он полностью игнорируется и статически связан с приложением, поэтому редактирование его в папке инструментов ничего не делает.