Asynchronous call to webservice in MVC 4 web application - asp.net

I am building my first real MVC4 application and I have run into following issue.
I have a model for "User" class. Data for it are obtained through asynchronous call to webservice:
public sealed class AdminDMSEntities
{
public List<User> UserList { get; private set; }
public AdminDMSEntities()
{
this.UserList = new List<User>(0);
ServiceClient client = new ServiceClient();
client.GetUsersCompleted += (s, e) =>
{
if (e.Result == null)
throw new ArgumentNullException("No users were retrieved");
UserList = new List<User>(0);
e.Result.ForEach(w => this.UserList.Add(new User(w.Guid, w.TrusteeType, w.Username, w.Email, w.LastLogin, w.PasswordChanged, w.IsUsingTempPassword)));
};
client.GetUsersAsync();
}
}
I intend to use this class as I would use class derived from DbContext (if I could use Entity Framework which I cant). So far I have only users in the class.
I am using tis class in UsersController like this:
public class UsersController : Controller
{
private AdminDMSEntities adminEntities = new AdminDMSEntities();
//
// GET: /User/
public ActionResult Index()
{
return View(adminEntities.UserList);
}
}
The problem is that I will end up with InvalidOperationException, because controller is not waiting for async call completion and passes UserList to the view before it is properly filled with users.
I can have the call synchronous for the time being, but it is very likely I will be forced to use asynchronous calls later, so I would like to know how to ensure, that controller will wait for async call to be completed before UserList is passed to view...
Thanks in advance
EDIT: I have tried the approach with AsyncController as listed below, currently I have added this to AdminDMS entities class:
public static async Task<AdminDMSEntities> AdminDMSEntitiesAsync()
{
AdminDMSEntities result = null;
Task<AdminDMSEntities> getUsersAsyncTask = Task.Factory.StartNew(() => new AdminDMSEntities());
await getUsersAsyncTask;
return result;
}
and this is the change to the controller:
public class UsersController : AsyncController
{
private AdminDMSEntities adminEntities = null;
//
// GET: /User/
public async Task<ActionResult> Index()
{
if (adminEntities == null)
{
adminEntities = await AdminDMSEntities.AdminDMSEntitiesAsync();
}
return View(adminEntities.UserList);
}
}
The result is that adminEntities are containing an instance of the class, but there are no users in the list (there should be 11).
EDIT2: Since i was told that creating new task is not the right thing to do, I went with the first suggested approach removin AdminDMSEntities class from the code. My thanks to Darin for helping me out :)

You could use an asynchronous controller. The idea is to have your controller derive from the AsyncController class instead of the Controller class. This class provides methods that allow you to perform asynchronous operations.
For example:
public class MyController: AsyncController
{
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment();
var client = new SomeClient();
client.GetUsersCompleted += (s, e) =>
{
UserList = new List<User>();
AsyncManager.Parameters["users"] = e.Result.Select(
w => new User(
w.Guid,
w.TrusteeType,
w.Username,
w.Email,
w.LastLogin,
w.PasswordChanged,
w.IsUsingTempPassword
)
)
.ToList();
AsyncManager.OutstandingOperations.Decrement();
};
client.GetUsersAsync();
}
public ActionResult IndexCompleted(IEnumerable<User> users)
{
return View(users);
}
}
and if you are using .NET 4.5 you could even take advantage of the new async keyword simplifying the asynchronous code even further. This is possible if you refactor your data access layer to the new pattern (i.e. return Tasks):
public class MyController: AsyncController
{
public async Task<ActionResult> Index()
{
var client = new SomeClient();
var users = await client.GetUsersAsync().Select(
w => new User(
w.Guid,
w.TrusteeType,
w.Username,
w.Email,
w.LastLogin,
w.PasswordChanged,
w.IsUsingTempPassword
)
)
.ToList();
return View(users);
}
}

Related

Action filter : how to call service layer and async method

I have a controller with many action method. The requirement for me is to check a value of a field from database and if the field value is "true" all the action methods can execute otherwise these action methods should not execute.
The method is in service layer
public class CustomAttributeFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var myFlag = await _adminDB.GetFlagSettingsAsync();
// how do i call async method from OnActionExecuting filter
if (!myFlag)
{
//Create your result
filterContext.Result = new EmptyResult();
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
Interface implementaion
public interface IAdminDB
{
Task<MySettings> GetMySettingsAsync();
}
public class AdminDB : IAdminDB
{
public async Task<MySettings> GetMySettingsAsync()
{
var dbName = _appSettings.AdminDbName;
var blobName = _appSettings.AdminBlobName;
return await _dbStorage.GetBlobAsync<MySettings>(blobName, dbName);
}
}
public class MySettings
{
public bool MyFlag { get; set; }
}
I get an error message "no suitable method found to override". How do i clear this error and how to inject service properly . Above is what i have tried, the call to async getting failed here.
I don't see where the _adminDB dependency comes from in your code, but I'm guessing that is causing the problem.
If you want to use async filters you have to implement the IAsyncActionFilter interface.
You can retrieve services from the executing context's DI container and use async methods the following way:
public class CustomAttributeFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context, ActionExecutionDelegate next)
{
var adminDb = filterContext.HttpContext.RequestServices.GetService<AdminDb>();
var myFlag = await adminDb.GetFlagSettingsAsync();
//..
await next();
}
}
Depending on your your needs, you can place your custom logic after the next() call as well.
See the documentation for more information.

Registering Actions for ControllerModel in ASP.NET Core

I am trying to add a new ActionModel for a ControllerModel in an implementation of IControllerModelConvention, but I cannot find any documentation or examples of how this model system works or how to do this correctly. I am able to add a new ActionModel easily enough, but it is not routable once the application is running:
var action = new ActionModel(method, new object[] { new HttpGetAttribute("/test") })
{
Controller = controller,
ActionName = "test"
};
controller.Actions.Add(action);
It seems I need to add a selector to the action, perhaps other properties as well, but I haven't been able to find one that exposes this action. Also unsure if my attributes are correct/redundant. Ultimately I would like to add multiple actions that do not map 1:1 to the methods in the controller.
I've made it work similiar to your approach. Maybe this can help you:
Controller
public class TestController : Controller
{
public IActionResult Index()
{
return Ok(new string[] { "Hello", "World" });
}
}
Model Convention
public class TestControllerModelConvention : IControllerModelConvention
{
public void Apply(ControllerModel controller)
{
Type controllerType = typeof(TestController);
MethodInfo indexAction = controllerType.GetMethod("Index");
var testAction = new ActionModel(indexAction, new[] { new HttpGetAttribute("/test") })
{
ActionName = "Index",
Controller = controller
};
controller.Actions.Add(testAction);
}
}
Startup
public void ConfigureServices(IServiceCollection services)
{
// other initialitation stuff
services.AddMvc(options =>
{
options.Conventions.Add(new TestControllerModelConvention());
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Now when I start the application and browse "/test" it will hit the controller action.

IConfigureOptions<T> is not creating scoped options

Typically Options are singleton. However i am building options from the database, and one of the Options property is password which keep changing every month. So i wanted to create Scoped instance of Options. I am using IConfigureOptions<T> like below to build Options from the database
public class MyOptions
{
public string UserID {get;set;}
public string Password {get;set;
}
public class ConfigureMyOptions : IConfigureOptions<MyOptions>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConfigureMyOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(MyOptions options)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
using (var dbContext = provider.GetRequiredService<MyDBContext>())
{
options.Configuration = dbContext.MyOptions
.SingleOrDefault()
.Select(x => new MyOptions()
{
UserID = x.UserID,
Password = x.Password
});
}
}
}
}
Use it in controller
public class HomeController : BaseController
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> option)
{
_options = option.Value;
}
[HttpGet]
[Route("home/getvalue")]
public string GetValue()
{
// do something with _options here
return "Success";
}
}
I want to create an instance of MyOptions for every new request so register it as Scoped in startup.cs
services.AddScoped<IConfigureOptions<MyOptions>, ConfigureMyOptions>();
However, when i put debugger inside ConfigureMyOptions's Configure method it only gets hit once for the first request. For next request onward the container returns the same instance (like singleton).
How do i set the scope here so MyOptions will get created for each request?
Use IOptionsSnapshot instead of IOptions in your controller and it will recreate options per request.
Why doesn't work with IOptions:
.AddOptions extension method of Configuration API registers the OptionsManager instance as a singlethon for IOptions<>
services.TryAdd(ServiceDescriptor.Singleton(typeof(IOptions<>), typeof(OptionsManager<>)));
services.TryAdd(ServiceDescriptor.Scoped(typeof(IOptionsSnapshot<>), typeof(OptionsManager<>)));
and OptionsManager class uses caching internally:
public virtual TOptions Get(string name)
{
name = name ?? Options.DefaultName;
// Store the options in our instance cache
return _cache.GetOrAdd(name, () => _factory.Create(name));
}
The following issue on github helped to find above: OptionsSnapshot should always be recreated per request

Unity injection of an instance inside WebAPI action

I am stuck with this and I wonder what is the best way to approach this problem. I have a WebApi controller where I want to inject ICommand instance but I can know what is the instance I need once I inspect the Post request data. I'll give an example to be more clear but my question also applies to Winform events where you receive an event argument and depending of this event arg you want to have different implementation injected.
public class TestController : ApiController
{
public object Post(int id)
{
ICommand command = null;
if(id = 1)
{
command = new Id1Command();
}
else
{
command = new Id2Command();
}
return new object();
}
}
The only thing I can think of is creating a factory that accepts unity container as argument and inside that factory to call container.Resolve with named instance.
My problem with that is that I am taught that you shouldn't register or resolve outside your composition root and that is violation of the good practices (according Mark Seemann). I am looking for the best design for this problem in general.
I would use a CommandFactory and pass it to the TestController:
public class TestController : ApiController
{
private readonly ICommandFactory mCommandFactory;
public TestController(ICommandFactory CommandFactory)
{
mCommandFactory = CommandFactory;
}
public object Post(int id)
{
ICommand command = null;
if(id = 1)
{
command = CommandFactory.CreateId1Command();
}
else
{
command = CommandFactory.CreateId2Command();
}
return new object();
}
}
Now you have to make sure that Unity is creating the TestController. To do so, you have to implement, configure and set an IDependencyResolver. Check Dependency Injection in ASP.NET Web API 2.
Edit to your comment:
For this scenario you can use an autofactory using a functor that takes an int:
public class TestController : ApiController
{
private readonly Func<int, ICommand> mCommandFactory
public TestController(Func<int, ICommand> CommandFactory)
{
mCommandFactory = CommandFactory;
}
public object Post(int id)
{
var command mCommandFactory(id);
return new object();
}
}
The registration should look like this:
container.RegisterType<Func<int, ICommand>>(new InjectionFactory(
c => new Func<int, ICommand>(
id =>
{
if (id == 1)
{
return new Command();
}
else
{
return new Command2();
}
})));
Note: You still have to set the DependencyResolver!

Moq Throws an exception for verify

I'm new to Mocking, but this must be something really basic that I'm missing:
The test code below produces an exception:
Expected invocation on the mock at least once, but was never performed: x => x.DeleteProducts(._products)\r\n\r\nConfigured setups:\r\nx => x.DeleteProducts(._products), Times.Never\r\n\r\nPerformed invocations:\r\nIProductRepository.DeleteProducts(System.Collections.Generic.List`1[WebApiDemo.DataAccessLayer.Product])
I step through the controller method and it does seem to call the DeleteProducts method...
// Arrange
IEnumerable<Product> _products = Helpers.ProductHelpers.CreateProducts(_numberProducts);
Mock<IProductRepository> _productRepository = new Mock<IProductRepository>();
_productRepository.Setup(x => x.DeleteProducts(_products));
ProductsController controller = new ProductsController(_productRepository.Object);
// Act
controller.Destroy(_productViewModels); // Destroy calls DeleteProducts
// Assert
_productRepository.Verify(x => x.DeleteProducts(_products));
Does DeleteProducts(_products); return void? I assume it does, so you need to put the .Verifiable() at the end of the .Setup() for it.
With that in place, it should run through ok, although I'm not sure why you have the Times.Never() instead of Times.Once() ??
I would also advocate on the Setup call using It.IsAny<T> rather than a specific collection, such as:
MyMock.Setup(x => x.MyMethod(It.IsAny<IEnumerable<Widget>>)).Verifiable()
Unless you have the mock behaviour set to strict there is no need for the setup. You are not returning anything from the Delete. The call to Verify will suffice.
Some things are not totally obvious from the code.
The repository deletes products but the controller destroys productviewmodels.
In Moq 4 the test should work if
You have a product view model for every product in _products
The Controller.Destroy method gets products from the viewmodels in the same order as _products
I would check that _productViewModels is a 1:1 match to the _products and check how Destroy() extracts the products from the viewmodels before calling Delete()
I would not go with the IsAny>() because you want to check that these specific products were deleted not any other ones.
[TestClass]
public class Verifying {
public interface IProductRepository {
void Delete(IEnumerable<Product> products);
}
public class ProductController {
private IProductRepository _repository;
public ProductController(IProductRepository repository) {
_repository = repository;
}
public void Destroy(IEnumerable<Product> products) {
_repository.Delete(products);
}
public void Destroy(IEnumerable<ProductViewModel> productViewModels) {
_repository.Delete(productViewModels.Select(vm => vm.Product));
}
}
public class Product {
}
public class ProductViewModel {
public Product Product { get; set;}
}
static Verifying() {
sProducts = new List<Product> { new Product(), new Product(), new Product() };
sProductViewModels = new List<ProductViewModel>(sProducts.Select(p => new ProductViewModel { Product = p }));
}
private static List<Product> sProducts;
private static List<ProductViewModel> sProductViewModels;
private Mock<IProductRepository> _mockRepository;
private ProductController CreateController() {
_mockRepository = new Mock<IProductRepository>();
return new ProductController(_mockRepository.Object);
}
[TestMethod]
public void DestroyingProducts() {
var controller = CreateController();
controller.Destroy(sProducts);
_mockRepository.Verify(mk => mk.Delete(sProducts));
}
[TestMethod]
public void DestroyingProductViewModels() {
var controller = CreateController();
controller.Destroy(sProductViewModels);
_mockRepository.Verify(mk => mk.Delete(sProducts));
}
}

Resources