We use Refit for our API declarations and want to use Interface inheritance.
A sample is shown here: https://github.com/reactiveui/refit#interface-inheritance
public interface IPlatformRestClient
{
HttpClient Client { get; }
[Get("/version")]
Task<string> GetVersion();
}
public interface ITestRestClient : IPlatformRestClient
{
[Get("/test")]
Task<string> Test();
}
I may be blind and do something wrong, but the construct results in the following error message
'AutoGeneratedITestRestClient' does not implement interface member 'IPlatformRestClient.GetVersion()' Platform.RestClient.UnitTests
And if I open the stubs:
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::System.Diagnostics.DebuggerNonUserCode]
[Preserve]
[global::System.Reflection.Obfuscation(Exclude=true)]
partial class AutoGeneratedITestRestClient : ITestRestClient
{
/// <inheritdoc />
public HttpClient Client { get; protected set; }
readonly IRequestBuilder requestBuilder;
/// <inheritdoc />
public AutoGeneratedITestRestClient(HttpClient client, IRequestBuilder requestBuilder)
{
Client = client;
this.requestBuilder = requestBuilder;
}
/// <inheritdoc />
Task<string> ITestRestClient.Test()
{
var arguments = new object[] { };
var func = requestBuilder.BuildRestResultFuncForMethod("Test", new Type[] { });
return (Task<string>)func(Client, arguments);
}
}
So is this feature broken or do I have any error?
Related
I'm developing a Xamarin APP and I want to load a Picker with data from a Web API that has Server Database. I tried to Google this but most of the articles don't show the content of source "Services class" that use Get async Method, Model class and ViewModel. I would be very grateful if someone could help me with an example.
This is my Controller in ASP.NET Web API
// GET: api/TipoUsers
public IQueryable<TipoUser> GetTipoUsers()
{
return db.TipoUsers;
}
Model class
public class TipoUsuario
{
public int IdTipoUsuario { get; set; }
public string Nome { get; set; }
}
ViewModel class
public class UsuarioViewModel
{
public ObservableCollection<TipoUsuario> tipos { get; set; }
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
tipos = new ObservableCollection<TipoUsuario>(task.Result);
}
}
Xaml Page
<Picker Title="Selecione o Tipo de Usuario"
ItemsSource="{Binding tipos}"
ItemDisplayBinding="{Binding Nome}"/>
Xaml.cs
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class UsuarioPage : ContentPage
{
public UsuarioPage()
{
InitializeComponent();
BindingContext = new UsuarioViewModel();
}
}
}
Service class
public class ApiService
{
public const string Url = "http://thisismysite:44342/";
public static async Task<List<TipoUsuario>> GetTipoUsers()
{
try
{
HttpClient client = new HttpClient();
string url = Url + "/api/TipoUsers";
string response = await client.GetStringAsync(url);
List<TipoUsuario> tipos = JsonConvert.DeserializeObject<List<TipoUsuario>>(response);
return tipos;
}
catch (Exception)
{
throw;
}
}
}
when I debug the app it just doesn't load the screen.
This can happen for a few reasons, I would check your async method isn’t throwing an exception that you aren’t able to see. Async methods return a Task object and if an exception is thrown inside it will be visible in the returned object in Task.Exception.
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
Also property changed events aren’t called when you set an ObserableCollection to a new instance, you want to add and remove from the collection.
You want to change:
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
tipos = new ObservableCollection<TipoUsuario>(task.Result);
}
to something like:
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
var temptipos = task.Result;
foreach(var tipo in temptipos)
{
tipos.Add(tipo);
}
}
I had originally set up DI with ninject for an asp.net web api 2 service with a single controller and everything was working correctly. Upon adding a second controller, ninject does not work for the new one. I'm getting the following error:
"An error occurred when trying to create a controller of type 'VstsController'. Make sure that the controller has a parameterless public constructor."
First controller (for which ninject works):
public class RepositoryController : ApiController
{
private GitHubClient _client;
public RepositoryController(IGitHubClientAuthenticated gitHubClientAuthenticated)
{
_client = gitHubClientAuthenticated.Client;
_client.Credentials = gitHubClientAuthenticated.Credentials;
}
Second controller:
public class VstsController : ApiController
{
private VssConnection _connection;
public VstsController(IVssConnectionAuthenticated vssConnectionAuthenticated)
{
_connection = vssConnectionAuthenticated.VssConnection;
}
Ninject config file:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IVssConnectionAuthenticated>().To<VssConnectionAuthenticated>();
kernel.Bind<IGitHubClientAuthenticated>().To<GitHubClientAuthenticated>();
kernel.Bind<IAuthenticationHelper>().To<AuthenticationHelper>();
}
Do I need to tweak anything if I want to keep adding controllers? Couldn't find any documentation on this. Thanks in advance
EDIT: Including ninject set up code as well as VssAuthenticated + IvssAuthenticated:
namespace Dashboard.WebAPI.Models
{
public interface IVssConnectionAuthenticated
{
VssConnection VssConnection { get; }
Uri Uri { get; }
}
}
namespace Dashboard.WebAPI.Models
{
public class VssConnectionAuthenticated: IVssConnectionAuthenticated
{
public VssConnection VssConnection { get; private set; }
public Uri Uri { get; private set; }
VssConnectionAuthenticated()
{
Uri = new Uri("uri");
string vstsSecretUri = "vstssecreturi";
GetKeyVaultSecret keyVaultSecretGetter = new GetKeyVaultSecret(new AuthenticationHelper(), vstsSecretUri);
string keyVaultSecret = keyVaultSecretGetter.KeyVaultSecret;
VssBasicCredential vssBasicCredential = new VssBasicCredential(string.Empty, keyVaultSecret);
VssConnection = new VssConnection(Uri, vssBasicCredential);
}
Full Ninject Config File:
namespace Dashboard.WebAPI.App_Start
{
using System;
using System.Web;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Common;
using System.Web.Http;
using Dashboard.WebAPI.Models;
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage the application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load modules and register services
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IVssConnectionAuthenticated>().To<VssConnectionAuthenticated>();
kernel.Bind<IGitHubClientAuthenticated>().To<GitHubClientAuthenticated>();
kernel.Bind<IAuthenticationHelper>().To<AuthenticationHelper>();
}
}
}
Registering Ninject as Dependency Resolver:
namespace Dashboard.WebAPI.App_Start
{
public class NinjectDependencyScope : IDependencyScope
{
IResolutionRoot resolver;
public NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has been disposed");
return resolver.GetAll(serviceType);
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
}
public class NinjectDependencyResolver: NinjectDependencyScope, IDependencyResolver
{
IKernel kernel;
public NinjectDependencyResolver(IKernel kernel) : base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
}
In case anyone else runs into this problem- The problem was in VssConnectionAuthenticated: The constructor needs to be public.
TLDR: In Asp Mvc 6 how do I perform model validation with a service using data annotations? What are the alternatives?
I have a very simple model
public class MyModel
{
[Required]
public string Name { get; set; }
}
I also have a service that exposes some simple validation methods
public interface IMyService
{
string[] ReservedWords { get; }
bool IsValidName(string name);
// Internally calls IsValidName and throws an Exception if the name is invalid
void Save(MyModel myModel);
// ... snip
}
And I have wired up my controller like so
public class MyController : Controller
{
private readonly IMyService _service;
public MyController(IMyService service)
{
_service = service;
}
// ... snip
public IActionResult Post(MyModel myModel)
{
if (!_service.IsValidName(input?.Name))
{
ModelState.AddModelError(nameof(MyModel.Name), "Invalid Name");
}
if (!ModelState.IsValid)
{
return View(myModel);
}
_service.Save(myModel);
return RedirectToAction(nameof(Index));
}
}
It feels a bit clucky to have 2 stages of validation - automatic model validation then manually performing service validation. I was hoping that something simialr to this would work
public class MyModel
{
[ServiceValidation(nameof(IMyService), nameof(IMyService.IsValidName)]
[Required]
public string Name { get; set; }
}
public ServiceValidationAttribute : ValidationAttribute
{
private readonly Type _interfaceOrClass;
private readonly string _methodOrProperty;
public ServiceValidationAttribute(Type interfaceOrClass, string methodOrProperty)
{
_interfaceOrClass = interfaceOrClass;
_methodOrProperty = methodOrProperty;
}
public override bool RequiresValidationContext => true;
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = validationContext.GetService(_interfaceOrClass);
// Extension method in shared library to assist with reflection
bool isValid = _interfaceOrClass.ValueForMethodOrPropertyNamed<bool>(service, _methodOrProperty, value);
return isValid
? ValidationResult.Success
: new ValidationResult(ErrorMessage);
}
}
However var serivce is always null, is there any way around this? I have wired up the IMyService to an implementation in the Startup.cs as it is available in the Controller.
Alternatively is there a better way of adding to the ModelState with a service?
<ItemsControl DockPanel.Dock="Right" x:Name="Actions">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="Action"
HorizontalAlignment="Right"
Content="{Binding Label}"
Margin="3" Width="30"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The above view binds with this viewmodel
public class DeploymentInputViewModel<T> : PropertyChangedBase
{
public BindableCollection<InputActionViewModel> Actions {get;set;}
}
I see my buttons. But when clicking it nothing happen.
The viewModels for InputActionViewModel:
public abstract class InputActionViewModel{
public InputActionViewModel()
{
}
public virtual Task Action()
{
return Task.FromResult<object>(null);
}
public string ActionToolTip { get; set; }
public string Label { get; set; }
public object Value { get; set; }
}
and also
public class InputCertificateActionViewModel : InputActionViewModel
{
[Import]
private IShell _shell;
[Import]
private IWindowsDialogs _dialogs;
private readonly IDeploymentSettingInputViewModel vm;
public InputCertificateActionViewModel(IDeploymentSettingInputViewModel vm)
{
this.vm = vm;
Label = "...";
ActionToolTip = "Pick a Certificate";
}
public bool IsManagementCertificate {get;set;}
public bool IsDeploymentCertificate { get; set; }
public async override Task Action()
{
if(IsManagementCertificate)
{
var subs = await _shell.IdentityModel.GetEnabledSubscriptionsAsync();
foreach(var sub in subs)
{
using (ManagementClient client = CloudContext.Clients.CreateManagementClient(sub.GetCredentials()))
{
var cert = _dialogs.SelectItemDialog("Select a certificate", "Pick one", true,
(await client.ManagementCertificates.ListAsync()).Select(c =>
new SelectItem(c.Thumbprint, Encoding.Default.GetString(c.PublicKey), c, (s) => c.Thumbprint.Contains(s))).ToArray())
.Tag as ManagementCertificateListResponse.SubscriptionCertificate;
this.vm.Value = cert.Thumbprint;
}
}
}else if(IsDeploymentCertificate)
{
}
}
}
I am adding actionViewModels by inserting directly into the observable code at startup.
haveActions.Actions.Add(DI.BuildUp(new InputCertificateActionViewModel(vm)
{
IsDeploymentCertificate = certAttribute.IsDeploymentCertificate,
IsManagementCertificate = certAttribute.IsManagementCertificate,
}));
haveActions is an instance of InputCertificateActionViewModel
Couldn't fit this all in a comment:
I can't have a peek at the Caliburn.Micro at the moment, but it might be something related to calling your method Action.
At a guess though, I'd say that by convention Caliburn.Micro expects to find a method that matches the Action<T> delegate to use for it's Actions, so your public virtual Task Action() won't be located and bound.
Have a quick check by defining a new method with a compatible signature, e.g public void MyMethod() and checking to see if it's located correctly and will function.
If that is the problem, you'll probably want to have a look at the IResult and Coroutines part of the Caliburn.Micro documentation, which looks like it will help you implement your desired behaviour.
When I use SetupAllProperties on a Mock, it works as expected:
/// <summary>
/// demos SetupAllProprties on an interface. This seems to work fine.
/// </summary>
[Test]
public void Demo_SetupAllProperties_forAnInterface()
{
var mock = new Mock<IAddress>();
mock.SetupAllProperties();
var stub = mock.Object;
stub.City = "blahsville";
var retrievedCity = stub.City;
Assert.AreEqual("blahsville", retrievedCity);
}
However, when I try it on a class, it fails:
/// <summary>
/// demos SetupAllProprties on a class. This seems to work fine for mocking interfaces, but not classes. :( The Get accessor returns null even after setting a property.
/// </summary>
[Test]
public void Demo_SetupAllProperties_forAClass()
{
var mock = new Mock<Address>();
mock.SetupAllProperties();
var stub = mock.Object;
stub.City = "blahsville";
var retrievedCity = stub.City;
Assert.AreEqual("blahsville", retrievedCity);
}
Did I do something wrong? Am I trying to do something unsupported by moq?
For good measure, here are the IAddress interface and the Address class:
public interface IAddress
{
string City { get; set; }
string State { get; set; }
void SomeMethod(string arg1, string arg2);
string GetFormattedAddress();
}
public class Address : IAddress
{
#region IAddress Members
public virtual string City { get; set; }
public virtual string State { get; set; }
public virtual string GetFormattedAddress()
{
return City + ", " + State;
}
public virtual void SomeMethod(string arg1, string arg2)
{
// blah!
}
#endregion
}
I copied your code into a new project could not reproduce your problem. I set a breakpoint in Demo_SetupAllProperties_forAClass() at the Assert.AreEqual line and retrievedCity did have the value "blahsville".
I am using xUnit, but I don't think that would make a difference. What version of Moq are you using? I am using 4.0.10510.6.