As described in the headline I want to build a Xamarin Forms app which is communicating with a web server. On the client side I am using the MMVM pattern of course.
Here my question: what about the communication with the server? From my point of view the server should nothing know about the concrete client technology (INotifyPropertyChanged e.g), so in this layer I still have to apply the MVC pattern for communication, right?
What costs me sleep on this approach is that I need to convert the objects 2 times in every direction:
Entity <--> MVC-Model (Dto) <--> MVVM-ViewModel
Am I right with my assumption or completely off the track?
One way to achieve this is to create a WebAPI which you then call from your view model in your shared Xamarin Forms project.
public class CommunicationService<T> where T : class
{
public CommunicationService ()
{
}
public async Task<T> GetRequestAsync (string url)
{
var client = new System.Net.Http.HttpClient ();
var response = await client.GetAsync (url);
var responseResult = response.Content.ReadAsStringAsync ().Result;
var result = JsonConvert.DeserializeObject<T> (responseResult);
return result;
}
}
From my view model I then call my function using the code below:
var communicationService = new CommunicationService<List<MyDtoModel>> ();
var items = await communicationService.GetRequestAsync (#"http://myurl.com/api/controller/action");
Then you can use AutoMapper to map the result to your view model, or of course, use the DTO directly, even if that not is best practice for the MVVM pattern.
Related
I am very new with orleans and trying to grasp everything with grains and so forth.
What i got is that in my startup.cs file i add the SignalR like this
public IServiceProvider ConfigureServices(IServiceCollection services)
{
Program.WriteConsole("Adding singletons");
services
.AddSingleton(achievementManager)
.AddMvc();
services.AddSingleton(SignalRClient);
return services.BuildServiceProvider();
}
So far everything is fine i can start my host/application and it connects to SignalR as it should. But what i cant wrap my head around is how do i get this down to my grain? if i had a controller i would simply send it down in the constructor on startup but how do i do this with a grain? Or can i even do it like this. Any guidance is appreciated.
In the grain then i want to do something like this
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private HubConnection SignalRClient { get; }
public NotifierGrain(HubConnection signalRClient)
{
SignalRClient = signalRClient;
SignalRClient.SendAsync(Methods.RegisterService, Constants.ServiceName);
}
public Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
SignalRClient.SendAsync(Methods.RegisterUserToMultipleGroups, registerUserNotification.UserId, registerUserNotification.InfoIds);
}
return Task.CompletedTask;
}
Then i try to call the Notify method from another grain like this
var notifier = GrainFactory.GetGrain<INotifierGrain>(Constants.NotifierGrain);
await notifier.NotifyClients(notification, MessageType.RegisterUser);
But trying to do this ends up with an error like this
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.Client.HubConnection' while attempting to activate 'User.Implementation.Grains.NotifierGrain'.
Orleans supports constructor injection, so you can inject the SignalRClient into your grain constructor. In your code you are already correctly registering the client using services.AddSingleton(SignalRClient), so I will focus on how to inject the type into your grain.
I do not know what the type the SignalR client object is, but in this example I assume that the type is "SignalRClient":
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private readonly SignalRClient signalRClient;
public NotifierGrain(SignalRClient signalRClient)
{
this.signalRClient = signalRClient;
}
public async Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
await this.signalRClient.SendAsync(
MessageMethods.RegisterUserToMultipleGroups,
registerUserNotification.UserId,
registerUserNotification.infoIds);
}
}
Depends how you're thinking to use SignalR Server, if you're going to host your SignalR server with Microsoft Orleans for sure you need to have backplane to handle the Orleans cluster communications.
You can use SignalR Orleans which has everything done out of the box for you :)
Also if you need a reactive SignalR library for the frontend, you can use Sketch7 SignalR Client
PS I m one of the authors of both libraries.
We have an MVC5 SPA application where one of our Web API controllers needs to process the import of a file. This process is made once per month, and can take up to 5 hours (our client wants this import to be made via Web API).
Our problem is that the connection is lost (ERR_CONNECTION_RESET) after one hour, and we need to keep it opened during the entire process.
We think one option is to send partial content from the server, to let the browser know the connection is still active and processing. In MVC Controller, something like this:
public class PartialActionResult : ActionResult
{
private Counter _counter;
private Action<Counter> _counterIncrease;
public PartialActionResult(Action<Counter> counterIncrease)
{
_counter = new Counter();
_counterIncrease = counterIncrease;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "text/html";
context.HttpContext.Response.BufferOutput = true;
if (context.HttpContext.Response.IsClientConnected)
{
while (_counter.Value < 100)
{
_counterIncrease(_counter);
context.HttpContext.Response.Write("processing...");
if (_counter.Value == 100)
context.HttpContext.Response.Write("OK");
context.HttpContext.Response.Flush();
}
}
}
}
With this, we can pass a delegate as reference, and from there we can update the information of the import status.
Is there any option for Web API, as this ActionResult only works for MVC Controllers? Other than that, any option to maintain the connection opened? BTW we cannot use signalR, just in case that might work.
Thanks in advance
I've set up a Web API project using Ninject, and I've used the fix detailed here for getting it to work with the latest version of the Web API. Everything is working fine, but I'm now trying to write some tests.
I'm using in-memory hosting to run the project for the tests, as detailed here, as I have a DelegatingHandler that performs authentication and then sets a property on the request message that is used by all the Api Controllers.
So, I've got a base class for my tests, and have a SetUp method where I set up the HttpServer and configuration, which I've pretty much taken from my working Ninject code:
[SetUp]
public void Setup()
{
bootstrapper = new Bootstrapper();
DynamicModuleUtility.RegisterModule(
typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(
typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("Login",
"api/auth/token",
new { controller = "Users", action = "Login" });
config.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
config.DependencyResolver =
new NinjectResolver(CreateKernel());
config.MessageHandlers.Add(
new AuthenticationHandler(CreateUserManager()));
Server = new HttpServer(config);
}
This is how I create the MoqMockingKernel:
private static IKernel CreateKernel()
{
var kernel = new MoqMockingKernel();
kernel.Bind<Func<IKernel>>()
.ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>()
.To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver =
new NinjectResolver(kernel);
return kernel;
}
And this is how I register the objects to use:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserManager>().ToMock();
kernel.Bind<UsersController>().ToSelf();
}
While I'm not testing the Controller per se, I do want a proper instance of it to be called, which is why I'm binding it ToSelf. I must admit that I am assuming that this is correct. This is an example of a test:
public void UserCannotLogin()
{
System.Net.Http.HttpClient client =
new System.Net.Http.HttpClient(Server);
string json = string.Format(
"{{ \"Username\": \"{0}\", \"Password\": \"{1}\" }}",
"wrong", "wrong");
HttpRequestMessage request =
CreateRequest(#"api/auth/token", json, HttpMethod.Get);
Action action = () => client.SendAsync(request);
using (var response = client.SendAsync(request).Result)
{
response.StatusCode.Should()
.Be(HttpStatusCode.Unauthorized);
}
}
I'm basically getting a 404 error. When I debug it, it does go to my DelegatingHandler, but it doesn't go to my controller.
I get the feeling that I'm fundamentally missing a point here, and it may not even be possible to do what I'm trying to do, but if anyone has any suggestions for either how to do this, or a different way to achieve the same thing, I'm all ears.
Update I think that it's because the default behaviour of the MockingKernel is to provide a Mock unless told otherwise, so it is returning a Mock of IHttpControllerSelector. I've set up a couple of default ones now:
kernel.Bind<IHttpControllerSelector>()
.To<DefaultHttpControllerSelector>();
kernel.Bind<IContentNegotiator>()
.To<DefaultContentNegotiator>();
It's still not working, I think because there are no formatters specified. I'll try that tomorrow and see if that gets me there.
Ok, I think that I was correct when I said that I was fundamentally missing a point here, but I'll answer this in case it helps someone else avoid the same mistake!
The Ninject MockingKernel is, I think, primarily about auto-mocking, so where you have a lot of interfaces you don't care about how they are set up in your test, you can ignore them in your tests and they will be automatically created for you.
In the case of the Web API, this is most definitely not the case, as you don't want the controller selector class to be auto mocked, otherwise you won't end up calling your controllers.
So, the solution I've come up with is to stick with using a standard Ninject Kernel, and then bind your interface to a constant Mock object:
kernel.Bind<IUserManager>().ToConstant(CreateUserManager());
private IUserManager CreateUserManager()
{
Mock<IUserManager> userManager = new Mock<IUserManager>();
// Set up the methods you want mocked
return userManager.Object;
}
Doing this, I've been able to successfully write tests that use an HttpClient to call an in-memory HttpServer that successfully call my DelegatingHandler and then end up at my controllers.
I am creating a REST API in ASP.NET MVC. I want the format of the request and response to be JSON or XML, however I also want to make it easy to add another data format and easy to create just XML first and add JSON later.
Basically I want to specify all of the inner workings of my API GET/POST/PUT/DELETE requests without having to think about what format the data came in as or what it will leave as and I could easily specify the format later or change it per client. So one guy could use JSON, one guy could use XML, one guy could use XHTML. Then later I could add another format too without having to rewrite a ton of code.
I do NOT want to have to add a bunch of if/then statements to the end of all my Actions and have that determine the data format, I'm guessing there is some way I can do this using interfaces or inheritance or the like, just not sure the best approach.
Serialization
The ASP.NET pipeline is designed for this. Your controller actions don't return the result to the client, but rather a result object (ActionResult) which is then processed in further steps in the ASP.NET pipeline. You can override the ActionResult class. Note that FileResult, JsonResult, ContentResult and FileContentResult are built-in as of MVC3.
In your case, it's probably best to return something like a RestResult object. That object is now responsible to format the data according to the user request (or whatever additional rules you may have):
public class RestResult<T> : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
string resultString = string.Empty;
string resultContentType = string.Empty;
var acceptTypes = context.RequestContext.HttpContext.Request.AcceptTypes;
if (acceptTypes == null)
{
resultString = SerializeToJsonFormatted();
resultContentType = "application/json";
}
else if (acceptTypes.Contains("application/xml") || acceptTypes.Contains("text/xml"))
{
resultString = SerializeToXml();
resultContentType = "text/xml";
}
context.RequestContext.HttpContext.Response.Write(resultString);
context.RequestContext.HttpContext.Response.ContentType = resultContentType;
}
}
Deserialization
This is a bit more tricky. We're using a Deserialize<T> method on the base controller class. Please note that this code is not production ready, because reading the entire response can overflow your server:
protected T Deserialize<T>()
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(Request.InputStream);
var rawData = sr.ReadToEnd(); // DON'T DO THIS IN PROD!
string contentType = Request.ContentType;
// Content-Type can have the format: application/json; charset=utf-8
// Hence, we need to do some substringing:
int index = contentType.IndexOf(';');
if(index > 0)
contentType = contentType.Substring(0, index);
contentType = contentType.Trim();
// Now you can call your custom deserializers.
if (contentType == "application/json")
{
T result = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(rawData);
return result;
}
else if (contentType == "text/xml" || contentType == "application/xml")
{
throw new HttpException(501, "XML is not yet implemented!");
}
}
Just wanted to put this on here for the sake of reference, but I have discovered that using ASP.NET MVC may not be the best way to do this:
Windows Communication Foundation (WCF)
provides a unified programming model
for rapidly building service-oriented
applications that communicate across
the web and the enterprise
Web application developers today are
facing new challenges around how to
expose data and services. The cloud,
move to devices, and shift toward
browser-based frameworks such as
jQuery are all placing increasing
demands on surfacing such
functionality in a web-friendly way.
WCF's Web API offering is focused on
providing developers the tools to
compose simple yet powerful
applications that play in this new
world. For developers that want to go
further than just exposing over HTTP,
our API will allow you to access all
the richness of HTTP and to apply
RESTful constraints in your
application development. This work is
an evolution of the HTTP/ASP.NET AJAX
features already shipped in .Net 4.0.
http://wcf.codeplex.com/
However I will not select this as the answer because it doesn't actually answer the question despite the fact that this is the route I am going to take. I just wanted to put it here to be helpful for future researchers.
What's the easiest way to clone current request's HttpContext instance?
I'm developing an app in Asp.net MVC v1. I upgraded the regular PartialView capabilities to actually have sub-controllers that act very similar, but have their own context. When you use PartialViews you have to fill view data for the partial view in your main view's controller action. I created my own functionality that makes it possible to call controller actions from within a view. This way I get:
I don't have to provide sub-view's data in my main view's controller action
sub controller methods can manipulate data more encapsulated without any relation to other views/controllers
The problem is that each sub-controller request uses HttpContext. So when I set some HttpContext.Item in a sub-controller it actually populates HttpContext of the actual request.
That's why I want to clone HttpContext. I'm already using:
HttpContext subContext = new HttpContext(request, response);
// what happened to Session, User, Items etc. properties?
but this doesn't set anything else than request and response. But I would probably also need other properties and collections... Like Session, Items, User... etc.
While the "Not Possible" answer is correct, there is an alternative that is much cleaner than writing values into the current context and then rewriting back to its original state. The solution is to make a new HttpContext object entirely that is based on the URL of your choosing.
// A new request/response is constructed to using a new URL.
// The new response is using a StreamWriter with null stream as a backing stream
// which doesn't consume resources
using (var nullWriter = new StreamWriter(Stream.Null))
{
var newRequestUri = new Uri("http://www.somewhere.com/some-resource/");
var newRequest = new HttpRequest("", newRequestUri.ToString(), newRequestUri.Query);
var newResponse = new HttpResponse(nullWriter);
var newContext = new HttpContextWrapper(new HttpContext(newRequest, newResponse));
// Work with the new context here before it is disposed...
}
Reference: https://github.com/maartenba/MvcSiteMapProvider/issues/278#issuecomment-34905271
Not possible
I guess an actual deep cloning is not possible because of server session state. Cloning would also have to clone this value, which is web server specific internal resource that is intrinsically static and can not be cloned. In this case a web server would have multiple Session objects for instance.
Workaround
Anyway. The workaround was to set additional context values before instantiating sub-controller processing. After processing is finished I reverted values back to original. So I actually had context as it was before.
For ASP.Net Core/.Net 5 the following will work (based on the ASP.Net Core source code for SignalR, if you need more features just add them).
public static HttpContext Clone(this HttpContext httpContext, bool copyBody)
{
var existingRequestFeature = httpContext.Features.Get<IHttpRequestFeature>();
var requestHeaders = new Dictionary<string, StringValues>(existingRequestFeature.Headers.Count, StringComparer.OrdinalIgnoreCase);
foreach (var header in existingRequestFeature.Headers)
{
requestHeaders[header.Key] = header.Value;
}
var requestFeature = new HttpRequestFeature
{
Protocol = existingRequestFeature.Protocol,
Method = existingRequestFeature.Method,
Scheme = existingRequestFeature.Scheme,
Path = existingRequestFeature.Path,
PathBase = existingRequestFeature.PathBase,
QueryString = existingRequestFeature.QueryString,
RawTarget = existingRequestFeature.RawTarget,
Headers = new HeaderDictionary(requestHeaders),
};
if(copyBody)
{
// We need to buffer first, otherwise the body won't be copied
// Won't work if the body stream was accessed already without calling EnableBuffering() first or without leaveOpen
httpContext.Request.EnableBuffering();
httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
requestFeature.Body = existingRequestFeature.Body;
}
var features = new FeatureCollection();
features.Set<IHttpRequestFeature>(requestFeature);
// Unless we need the response we can ignore it...
features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
var newContext = new DefaultHttpContext(features);
if (copyBody)
{
// Rewind for any future use...
httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
}
// Can happen if the body was not copied
if(httpContext.Request.HasFormContentType && httpContext.Request.Form.Count != newContext.Request.Form.Count)
{
newContext.Request.Form = new Microsoft.AspNetCore.Http.FormCollection(httpContext.Request.Form.ToDictionary(f => f.Key, f => f.Value));
}
return newContext;
}
The ASP.NET MVC framework intentionally makes dependencies to abstract classes with all members virtual. That simply says - extensibility.
Controllers depend on HttpContextBase, not HttpContext. Perhaps you can make your sub-controllers depend on HttpContextBase too so you can wrap it.
Just my 2 cents.
I've used
<% Html.RenderAction("Action", "Controller"); %>
to great effect, allowing me to create completely isolated/escapsulated actions without resorting to complex code. This would seem to offer the same functionality without the same complexity.
The rendered views are standard partial views and the controller actions just like any other.