async/await in Breeze Controller - asynchronous

Am I correct in assuming that a BreezeController does not support the async/await feature in .Net 4.5?
Take for example:
public class BreezeController {
[HttpGet]
public IQueryable<AssetType> AssetTypes()
{
return this.contextProvider.Context.AssetTypes;
}
It makes no sense to change this to:
[HttpGet]
public async Task<List<AssetType>> AssetTypes()
{
return await this.contextProvider.Context.AssetTypes.ToListAsync();
}
Doing so would now result in a Select * FROM AssetTypes when the client side breeze query may only be fetching a filtered list of AssetTypes
It seems that for breeze to support async on the server, BreezeQueryableAttribute and / or QueryHelper would need to perform the async/await.
Assuming I'm not off track, are there any plans in supporting async in a BreezeController?
Thanks
Christian

The AsyncSave branch of this implements an asynchronous save for the EF context. I am just looking at updating it to support latest version of breeze https://github.com/gilesbradshaw/breeze.server.net/tree/AsyncSave

Related

OData doesn't return context and count after upgrading to .NET 6

I used .NET 3.1 until I decided to upgrade my app to .NET 6. I did it successfully but some of my modules broke, one of them is OData. I used OData and I got #odata.context, #odata.count and value returned back in 3.1. Now in .NET 6, I get only the value which means that #odata.context and #odata.count aren't present in the return object. In order to make it run, I added this line in my code
services.AddControllers().AddOData(options => options.AddRouteComponents("v1", GetEdmModel()).Select().Filter().OrderBy()); in my Startup.cs where
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Project>("Project");
return builder.GetEdmModel();
}
My endpoint is as
[HttpGet]
[EnableQuery(PageSize = 20)]
public async Task<ActionResult> GetAsync()
{
var projects = await _projectRepository.GetAllAsync();
return Ok(projects);
}
Do you know what can I change and how should I change it in order to get the context and count from OData in .NET 6? I use the usual OData library with 8.0.0 version
EDIT to add more info about #Tiny Wang's answer:
This seems to be working and thank you very much for it! However, I stumbled upon another problem. I tried your example and the working version was on https://localhost:44327/odata/project$count=true&$skip=0&$orderby=CreateDate%20desc. My api prefix is api/[ControllerName] and I changed
options => options.EnableQueryFeatures().AddRouteComponents("odata", GetEdmModel()).Select().Filter().OrderBy()
to
options => options.EnableQueryFeatures().AddRouteComponents("api", GetEdmModel()).Select().Filter().OrderBy()
but when I access the endpoint, I get the following error:
"The request matched multiple endpoints. Matches: MyApp.Api.Controllers.ProjectController.GetAsync (MyApp.Api) MyApp.Api.Controllers.ProjectController.GetAsync (MyApp.Api)"
even tho GetAsync() is defined only once. Do you know how can I fix this and what causes it?
The change was in the Microsoft.AspNetCore.OData package, not .NET 6. You can use that package in .NET 5 if you want.
First of all, you always had to specify $count=true. That's an expensive operation. In a paging query it meant you had to execute two queries, one to receive a single page of data and another to count all possible results.
To do that you have to enable counting, just like any other OData operation, in the AddOData() call that adds the OData middleware.
Finally, for this to have any real effect, the controller action should return an IQueryable that will be used to construct the final LINQ and by extension SQL query. Otherwise your code will load everything in memory. You'd load a 100K row table in memory only to return 10 rows
OData failed to gain traction because it allowed clients to execute inefficient and unoptimized queries. Service developers had no idea what to optimize because client developers were free to execute anything. In later versions of the protocol, all query capabilites are off by default and have to be explicitly enabled. Server developers now can restrict the maximum size, whether expensive sort operations are allowed etc. They can prevent client code from filtering or sorting by unindexed fields for example.
In my own application I add the OData middleware with :
var edmModel=ODataModels.GetEdmModel();
services.AddControllersWithViews()
.AddOData(opt => opt.Select()
.OrderBy()
.Filter()
.Count()
.Expand()
.SetMaxTop(250)
.AddRouteComponents("odata", edmModel)
);
This enables Count and sets the maximum result size to a fairly large 250 - I have some grids users tend to scroll through.
To use the OData endpoints, a Controller that inherits from ODataController is needed. Query methods should return an IQueryable. If that IQueryable comes from a DbContet, the OData query will be used to construct a LINQ query and by extension the final SQL query. This will ensure that only the necessary data will be loaded.
[EnableQuery]
public IQueryable<Customers> Get()
{
return _db.Customers.AsNoTracking();
}
An OData controller that works on top of EF Core could look like this :
public class CustomersController:ODataController
{
private readonly ILogger<CustomersController> _logger;
private readonly SalesContext _db;
public CustomersController(SalesContext db, ILogger<CustomersController> logger)
{
_logger = logger;
_db = db;
}
[EnableQuery]
public IQueryable<Customers> Get()
{
return _db.Customers.AsNoTracking();
}
[EnableQuery]
[HttpGet]
public IActionResult Get(long key)
{
var cust = _db.Customers
.AsNoTracking()
.FirstOrDefault(t => t.ID == key);
if (cust == null)
{
return NotFound($"Not found: Customer ID = {key}");
}
return Ok(cust);
}
...
The Get(key) action is necessary to allow retrieving items by ID in OData eg using customers(123).
==================================
Per my test(created a .net 6 web api project and install Microsoft.AspNetCore.OData 8.0.10), I need to add ?$count=true behind my url then it can appear in my response.
My program.cs
builder.Services.AddControllers().AddOData(options => options.EnableQueryFeatures().AddRouteComponents("odata", GetEdmModel()).Select().Filter().OrderBy());
IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<WeatherForecast>("Hello");
return builder.GetEdmModel();
}
My test controller:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
namespace WebApi.Controllers
{
[Route("odata/[Controller]")]
public class HelloController : ODataController
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[EnableQuery]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Id = index,
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}

What are the recomendation for developing .net core 2.2 web api for following bulleted points? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I am developing a new WebApi using .NetCore2.2, Autofac4, Dapper. There are few very basic questions because this is my first WebApi project. As part of this project I have to write both unit-test and integration-test.
My questions are as follows (Sample Code is give below):
What is recommended return type between "Task< IActionResult >" and "Task< IEnumerable >"?
Recommended object Scope of the dependencies in startup class for my project?
Do I really need UnitOfWork for this given project structure?
What are the flaws if I follow this design?
Is there any better way to design this API?
As TDD do I need write test cases for API layer(Controller) and Infrastructure layer only or Doman Layer (it doesn't have any logic) as well?
What are the scenario I must include in my controller unit test?
Domain Layer:
[Table("Movie")]
public class Movie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID {get;set;}
public string Title {get;set;}
}
public interface ICommandRepository<T> where T : class
{
Task CreateAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
public interface IQueryRepository<T> where T : class
{
Task<IEnumerable<T>> GetAllMoviesAsync();
Task<IEnumerable<T>> GetMoviesByTitleAsync(string title);
Task<T> GetMovieByIDAsync(int id);
}
Infrastructure Layer:
public class MovieCommandContext : DbContext
{
public MovieCommandContext(DbContextOptions<MovieCommandContext> options)
: base(options)
{}
public DbSet<Movie> Movies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class MovieQueryContext : IDisposable
{
private readonly IDbConnection connection;
public MovieQueryContext(string connectionString)
{
connection = new SqlConnection(connectionString);
}
public async Task<IEnumerable<Movie>> GetAllMovies()
{
// Use Dapper->QueryAsync
throw new NotImplementedException();
}
...
public void Dispose()
{
if (connection?.State == ConnectionState.Open)
connection.Close();
}
}
public class MovieCommandRepository : ICommandRepository<Movie>
{
private readonly MovieCommandContext context;
public MovieCommandRepository(MovieCommandContext dbContext)
{
context = dbContext;
}
public async Task CreateAsync(Movie movie)
{
await context.AddAsync<Movie>(movie);
await context.SaveChangesAsync();
}
public async Task UpdateAsync(Movie movie)
{
var entity = context.Attach<Movie>(movie);
context.Entry<Movie>(movie).State = EntityState.Modified;
await context.SaveChangesAsync();
}
public async Task DeleteAsync(Movie movie)
{
context.Remove<Movie>(movie);
await context.SaveChangesAsync();
}
}
public class MovieQueryRepository : IQueryRepository<Movie>
{
private readonly MovieQueryContext context;
public MovieQueryRepository(MovieQueryContext dbContext)
{
context = dbContext;
}
public async Task<IEnumerable<Movie>> GetAllMoviesAsync()
{
return await context.GetAllMovies();
}
public async Task<IEnumerable<Movie>> GetMoviesByTitleAsync(string title)
{
return await context.GetMovieByName(title);
}
public async Task<Movie> GetMovieByIDAsync(int id)
{
return await context.GetMovieByID(id);
}
}
API Layer:
[Route("api/sample")]
[ApiController]
public class SampleController : ControllerBase
{
private readonly ICommandRepository<Movie> movieCommand;
private readonly IQueryRepository<Movie> movieQuery;
public SampleController(ICommandRepository<Movie> command, IQueryRepository<Movie> query)
{
movieCommand = command;
movieQuery = query;
}
[HttpGet]
public async Task<IActionResult> GetMoviesAsync()
{
try
{
var movies = await movieQuery.GetAllMoviesAsync();
return Ok(movies);
}
catch
{
// TODO: Logging
return BadRequest();
}
}
[Route("{name:alpha}")]
[HttpGet]
public async Task<IActionResult> GetMoviesByTitle(string movieTitle)
{
try
{
var movies = await movieQuery.GetMoviesByTitleAsync(movieTitle);
return Ok(movies);
}
catch
{
// TODO: Logging
return BadRequest();
}
}
[Route("{movieID:int:min(1)}")]
[HttpGet]
public async Task<IActionResult> GetMovieByID(int movieID)
{
try
{
var movie = await movieQuery.GetMovieByIDAsync(movieID);
return Ok(movie);
}
catch
{
// TODO: Logging
return BadRequest();
}
}
[Route("")]
[HttpDelete("{id:int:min(1)}")]
public async Task<IActionResult> Delete(int id)
{
try
{
var movie = await movieQuery.GetMovieByIDAsync(id);
if (movie == null)
return BadRequest();
await movieCommand.DeleteAsync(movie);
return Ok();
}
catch
{
// TODO: Logging
return BadRequest();
}
}
}
Startup.cs:
private void ConfigureContainer(ContainerBuilder builder)
{
var contextOptions = new DbContextOptionsBuilder<MovieCommandContext>()
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
.Options;
builder.RegisterType<MovieCommandContext>()
.WithParameter("options", contextOptions);
builder.RegisterType<MovieQueryContext>()
.AsSelf()
.WithParameter("connectionString",Configuration.GetConnectionString("DefaultConnection"));
builder.RegisterType<MovieCommandRepository>().As<ICommandRepository<Movie>>();
builder.RegisterType<MovieQueryRepository>().As<IQueryRepository<Movie>>();
}
Point 1:
You should return an IActionResult to return a propper Http response, instead of returning the Task<IEnumerable<Movie>>. That way you guarantee the S and the I of SOLID principles
Point 2 & 3:
see here: Entity Framework Core service default lifetime
Point 4:
IQueryRepository as some bad methods names. The names are tight coupled with domain concepts and they should not.
You are failing the Separation of concerns ( the S of SOLID).
ICommandRepository as an Add method that is being expose to some controller and not being used ( same as Update) here you are failing on the Interface segregation.
MovieQueryContext does not implement IDisposable pattern correctly please see here!
MovieQueryContext is different from MovieCommandContext on the way it initializes. Why? You should try to be coherent the way you design you types because it will give you reusability and apply the DRY principle.
Consider the effort you will need to to if the access to the database change to mongodb. Or if the access to the database changes to a remote service How many changes, and where do you do does changes to support that?
If Movie is a Domain Type it should not have attributes to any specific database access. keep it POCO as possible.
Point 5:
To design your API consider this post. The way you inject your dependencies should consider the lifetime of those objects. Keep in mind that in aspnet.core ApiControllers lifetime is per request. The way you manage your resources to access database should take that into consideration.
If you are considering CQRS, the controllers should be diferent. Keeping in mind the Separation of concerns regarding those responsabilities. One controller would have the responsability to expose some query API, and the other to process commands. There are good frameworks to support CQRS see this scott hanselman post.
Constraints exists on Route attribute not on Verbs.
Logging and Exception handling should be done on an ActionAttribute or on some Especific Middleware, because they are considered to be cross cutting concerns.
Delete Action does not comply to the Http protocol. please consider http rfc:
GetMoviesByTitle Action does not have the name parameter.
Point 6:
Unit tests should test business logic, mocking all the external dependencies with values relevant to the test in place. TDD methodology considers 3 main steps ( here for more details):
the first step consists on implementing the unit tests so it fails
Iterate on implementation of the method being test until it passes with success
Improve the implementation of the method being test
If you want to test your ApiController as being used with all the middleware integrated you need to have that environment put in place without using an actual server that open ports. To do that please consider the usage of TestServer ( see here and here )
1. What is recommended return type between "Task< IActionResult >" and "Task< IEnumerable < Movie > >"?
Even though the API allows you yo use the interface IActionResult, I wouldn't use it at all. Why? Semantics, the only way to know what the true return is, is to see the implementation. It's clearer if the returns is Task< IEnumerable< Movie>>.
If you need to throw a BadRequest or other http code, use the asp.net pipeline to handle this for you. See Notes below.
When using whatever tool to generate some sort of documentation of this API it won't help hiding the real result.
2. object Scope of the dependencies in startup class for my project?
Avoid sharing state between calls, to avoid future issues with synchronization just stick to scope dependencies per request. This may be a performance issue if you have a lot of requests, you can always change this later on. If it's an issue at all.
3. I really need UnitOfWork for this given project structure?
4. What are the flaws if I follow this design?
5. Is there any better way to design this API?
In hope of answering the above 3 questions. The problem I see is extending the functionality around Movie model. e.g. add a fourth action on ICommandRepository.
It seams it will grow vertically. It will only be a problem if multiple classes implement this interface, because they will all need to change. (Interface Segregation Principle)
A way to solve this is to use the Mediator pattern. Your controller will receive the mediator and the mediator will deliver the message to whoever handles it. With this type of solution you could have a class per operation and therefore your system can grow horizontally as new classes are added to the system. (Open Close Principle)
In time, you'll see that a lot of functionality can be reused and adding features is just a matter of configuration.
6. As TDD do I need write test cases for API layer(Controller) and Infrastructure layer only or Domain Layer (it doesn't have any logic) as well?
The idea of Testing in general is to test behavior, when TDDing that should be your mindset. In my experience I found that testing the whole behavior is better than multiple parts of the same behavior.
In this case, the API Layer is part of the infrastructure as is the persistence layer. They should have their own tests, the business rules (Application layer) should have their own tests. The application layer is what you want to last forever. The Api will change as technologies appear (windows forms, web forms, web apis, etc.) Regarding databases as well, you don't know if you want to stick with EF forever.
If the domain layer doesn't provide any behavior then there is nothing to test.
7. What are the scenario I must include in my controller unit test?
I would test using asp.net TestHost:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
Test the if routing is correct, test failing scenarios and successful scenarios.
Some notes:
An exception in the Controller does not mean a BadRequest.
Logging is a cross cutting concern, don't just do it everywhere.
Either use the asp.net pipeline or just move this concern to
application layer.
It appears that MovieQueryRepository does nothing, so you don't need it.
This is just some remarks about your questions, there is much more to it. Just remember to keep things simple and organized.
Hope it helped, let me know!

Not able to redirect to action when using TempData in Asp.Net Core

I am trying to achieve a simple thing in Asp.Net Core. This would have been no big deal in Asp.Net Mvc. I have an action method like this
public async Task<IActionResult> Create([Bind("Id,FirstName,LastName,Email,PhoneNo")] Customer customer)
{
if (ModelState.IsValid)
{
_context.Add(customer);
await _context.SaveChangesAsync();
TempData["CustomerDetails"] = customer;
return RedirectToAction("Registered");
}
return View(customer);
}
public IActionResult Registered()
{
Customer customer = (Customer)TempData["CustomerDetails"];
return View(customer);
}
At first I assumed TempData works by default but later realized that it has to be added and configured. I added ITempDataProvider in startup. The official document seems to describe that this should be enough. It didn't work. Then I also configured it to use Session
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddSession(
options => options.IdleTimeout= TimeSpan.FromMinutes(30)
);
services.AddMvc();
services.AddSingleton<ITempDataProvider,CookieTempDataProvider>();
}
I the following line related to Session in Configure method of Startup before writing app.UseMvc.
app.UseSession();
This still is not working. What is happening is I am not getting any exception because of use of TempData anymore which I got before when I missed some of the configuration but now the create action method is not able to redirect to Registered Method. Create method does all the work but RedirectToAction has no effect. If I remove the line that is assigning Customer details to TempData, the RedirectToAction successfully redirects to that action method. However in this case Registered action method don't have access to CustomerDetails obviously. What am I missing?
#Win. You were right. I realized the serialization, deserialization is required whenever you want to use TempData in Asp.net Core after reading the disclaimer in this article.
https://andrewlock.net/post-redirect-get-using-tempdata-in-asp-net-core/
I first tried to use BinaryFormatter but discovered that it has also been removed from .NET Core. Then I used NewtonSoft.Json to serialize and deserialize.
TempData["CustomerDetails"] = JsonConvert.SerializeObject(customer);
public IActionResult Registered()
{
Customer customer = JsonConvert.DeserializeObject<Customer>(TempData["CustomerDetails"].ToString());
return View(customer);
}
That's the extra work we have to do now but looks like that's how it is now.

Data caching per request in Owin application

In traditional ASP.NET applications (that use System.Web), I'm able to cache data in
HttpContext.Current.Items
Now in Owin the HttpContext is not available anymore. Is there a way to do the similar thing in Owin - a static method/property through which I can set/get per request data?
This question gave some hints but not exact a solution in my case.
Finally I found OwinRequestScopeContext. Very simple to use.
In the Startup class:
app.UseRequestScopeContext();
Then I can add per request cache like this:
OwinRequestScopeContext.Current.Items["myclient"] = new Client();
Then anywhere in my code I can do (just like HttpContext.Current):
var currentClient = OwinRequestScopeContext.Current.Items["myclient"] as Client;
Here is the source code if you're curious. It uses CallContext.LogicalGetData and LogicalSetData. Does any one see any problem with this approach of caching request data?
You just need to use OwinContext for this:
From your middleware:
public class HelloWorldMiddleware : OwinMiddleware
{
public HelloWorldMiddleware (OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
context.Set("Hello", "World");
await Next.Invoke(context);
}
}
From MVC or WebApi:
Request.GetOwinContext().Get<string>("Hello");

Preventing a deadlock when calling an async method without using await

I need to call a method returning a Task from within
public override void OnActionExecuting(ActionExecutingContext filterContext)
It wont let me make this method async it throws the following
An asynchronous module or handler completed while an asynchronous
operation was still pending.
and when calling
entityStorage.GetCurrentUser().Result
I get a deadlock. How can I avoid this?
I have been playing around with it coming up with stuff like
entityStorage.GetCurrentUser().Result.ConfigureAwait(false).GetAwaiter().GetResult();
But this isn't working. How can I do it? My solution will need to work with ASP.NET 4 and the Async Targetting Pack, I can't use ASP.NET 4.5 as am deploying to Azure.
The cause of the deadlock is explained here. In short, don't block on async code. You should use ConfigureAwait(false) in your library async code and await the results (not use Result or Wait).
Update: Please vote here for the MVC team to add support for async action filters.
Since await is just syntax sugar for the compiler rewriting a continuation for you, the most 'direct' path would be to take whatever code was going to follow your await and make it a ContinueWith call.
So, something like:
entityStorage.GetCurrentUser().ContinueWith(t =>
{
// do your other stuff here
});
If you MUST convert asynch to synch.
public User GetCurrentUserSynch()
{
return Task.Run(() =>
{
var asyncResult = entityStorage.GetCurrentUser();
while (!asyncResult.IsCompleted)
{
Application.Current.TryFindResource(new object()); // This is for WPF but you can do some other nonsense action of your choosing
}
return asyncResult.Result;
}).Result;
}
Otherwise use #Stephen's answer.

Resources