This is my first Asynchronous EF attempt :)
I am having a generic entity framework repository and I want to make it async.
The original code is:
public virtual IEnumerable<TEntity> Get()
{
IQueryable<TEntity> query = dbSet;
return query.ToList();
}
I trnsfor it as:
public class GenericRepository<TEntity> where TEntity : class
{
internal Context context;
internal DbSet<TEntity> dbSet;
public GenericRepository(Context context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual Task<IEnumerable<TEntity>> Get()
{
IQueryable<TEntity> query = dbSet;
return query.ToListAsync();
}
}
However, I have compile error, cannot convert type System.threading.Tasks.Task> to return type Tasks.Task>
The ToListAsync() method retrieve a list and I am expecting an Task>, my oringinal IEnumerable results is using elsewhere to populate selectbox
_xxx.Get().Select(x => new SelectListItem
{
Value = x.ps_id.ToString(),
Text = x.ps_label
}).OrderBy(x => x.Text);
Can you point me out what need to be done to do it correctly
Thanks
Task<T> is invariant because it's a class. It makes more sense to return a list since you're going through the work of making it that way anyway.
public virtual Task<List<TEntity>> Get()
{
IQueryable<TEntity> query = dbSet;
return query.ToListAsync();
}
Related
I have a series of class libraries that are used in asp.net-core middleware, and in an IHostedService.
To fetch the user context, I can inject IHttpContextAccessor to grab the HttpContext user:
public class MyLibrary
{
public MyLibrary(IHttpContextAccessor accessor)
{
// set the accessor - no problem
}
public async Task DoWorkAsync(SomeObject payload)
{
// get the user from the accessor
// do some work
}
}
To be a little more abstract, I have an IUserAccessor with an HttpUserAccessor implementation:
public class HttpUserAccessor: IUserAccessor
{
IHttpContextAccessor _httpaccessor;
public HttpUserAccessor(IHttpContextAccessor accessor)
{
_httpaccessor = accessor;
}
public string GetUser()
{
// return user from _httpaccessor
}
}
and then MyLibrary does not need an IHttpContextAccessor dependency:
public class MyLibrary
{
public MyLibrary(IUserAccessor accessor)
{
// set the accessor - no problem
}
public async Task DoWorkAsync(SomeObject payload)
{
// get the user from the accessor
// do some work
}
}
My IHostedService is popping message from a queue, where the message includes:
a user context, and
a serialized SomeObject to pass to MyLibrary.DoWorkAsync
So, something like:
public class MyHostedService : IHostedService
{
IServiceScopeProvider _serviceScopeFactory;
public MyHostedService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = servicesScopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{ ... }
public Task StopAsync(CancellationToken cancellationToken)
{ ... }
public async Task ExecuteAsync(CancellationToken stoppingToken)
{
foreach (var message in queue)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
// todo: tell IUserAccessor what message.User is!
var payload = // create a SomeObject from the queue message
var mylibrary = _services.GetRequiredService<MyLibrary>();
await myLibrary.DoWorkAsync(payload);
}
}
}
}
So, my question is, how does MyHostedService store message.User in such a way that a custom IUserAccessor can access it in a thread-safe manner via DI?
how does MyHostedService store message.User in such a way that a custom IUserAccessor can access it in a thread-safe manner via DI?
The thing you're looking for is AsyncLocal<T> - it's like a thread-local variable but scoped to a (possibly asynchronous) code block instead of a thread.
I tend to prefer a "provider" + "accessor" pairing for this: one type that provides the value, and a separate type that reads the value. This is logically the same thing as a React Context in the JS world, though the implementation is quite different.
One tricky thing about AsyncLocal<T> is that you need to overwrite its value on any change. In this case, that's not really a problem (no message processing will want to update the "user"), but in the general case it's important to keep in mind. I prefer storing immutable types in the AsyncLocal<T> to ensure they aren't mutated directly instead of overwriting the value. In this case, your "user" is a string, which is already immutable, so that's perfect.
First, you'll need to define the actual AsyncLocal<T> to hold the user value and define some low-level accessors. I strongly recommend using IDisposable to ensure the AsyncLocal<T> value is unset properly at the end of the scope:
public static class AsyncLocalUser
{
private static AsyncLocal<string> _local = new AsyncLocal<string>();
private static IDisposable Set(string newValue)
{
var oldValue = _local.Value;
_local.Value = newValue;
// I use Nito.Disposables; feel free to replace with another IDisposable implementation.
return Disposable.Create(() => _local.Value = oldValue);
}
private static string Get() => _local.Value;
}
Then you can define a provider:
public static class AsyncLocalUser
{
... // see above
public sealed class Provider
{
public IDisposable SetUser(string value) => Set(value);
}
}
and the accessor is similarly simple:
public static class AsyncLocalUser
{
... // see above
public sealed class Accessor : IUserAccessor
{
public string GetUser() => Get();
}
}
You'll want to set up your DI to point IUserAccessor to AsyncLocalUser.Accessor. You can also optionally add AsyncLocalUser.Provider to your DI, or you can just create it directly.
Usage would go something like this:
foreach (var message in queue)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var userProvider = new AsyncLocalUser.Provider(); // (or get it from DI)
using (userProvider.SetUser(message.User))
{
var payload = // create a SomeObject from the queue message
var mylibrary = _services.GetRequiredService<MyLibrary>();
await myLibrary.DoWorkAsync(payload);
}
}
}
I am getting the error as my title. I have only learnt ASP.NET MVC for a month or so, I'm getting confused with the errors, for those who knows how to fix this error and having the time to explain what's the causes, huge thanks to you, sir!
The code i have for my ViewModel is
public class RescuerViewModel
{
public Rescuer Rescuer { get; set; }
}
For my controller that is having the error is
public ActionResult New()
{
var rescuers = _context.Rescuers.ToList();
var viewModel = new RescuerViewModel
{
Rescuer = rescuers //error here
};
return View("RescuerForm", viewModel);
}
In the same controller which is using the same viewmodel but without having the error is
public ActionResult Edit(int id)
{
var rescuer = _context.Rescuers.SingleOrDefault(c => c.Id == id);
if (rescuer == null)
return HttpNotFound();
var viewModel = new RescuerViewModel
{
Rescuer = rescuer //this works fine
};
return View("RescuerForm", viewModel);
}
I try IEnumerable<> but it just cause a lot more errors.
The variable rescuers stores a collection of Rescuer objects. It's type is a collection type (of Rescuer objects). Your view model Rescuer property is of type Rescuer, which can store a single object. In your current code, you are trying to store a collection where it is supposed store a single item, hence you are getting this error.
The solution is to store a single object instead of a list in that property (or null since it is nullable reference type). But this is your New action method. So it does not make sense to assign a Rescuer object read from database here. If you are running into null reference because you have Rescuer property in your view model which you are using in your view with helper methods(ex: TextBoxFor) or tag helpers, you may consider initializing it to an empty object.
var viewModel = new RescuerViewModel();
viewModel.Rescuer = new Rescuer();
Another option is to do this initialization int he view model constructor.
public class RescuerViewModel
{
public Rescuer Rescuer { get; set; }
public RescuerViewModel()
{
Rescuer = new Rescuer();
}
}
Now you can simply create an object of RescuerViewModel and pass that to your view.
public ActionResult New()
{
var viewModel = new RescuerViewModel();
return View("RescuerForm", viewModel);
}
I have a situation in which I would like to do custom parameter binding for an api controller in ASP.NET core.In WebAPI 2.0 it was possible to perform custom binding to primitive types by implementing various interfaces such as IValueProvider and providing a ValueProviderFactory. This does not seem the case with ASP.NET core in as far as what I understand from the documentation I found here.
I did notice this SO post which lead me to this article which overrides the behavior for the MutableObjectModelBinder. It would appear I could do something along those lines such as:
[HttpGet]
[Route("api/{domain}/[controller]")]
public IEnumerable<string> Get([ModelBinder(BinderType = typeof(MyCustomBinder))]string orderby)
{
//Do stuff here
}
This doesn't necessarily seem right to me since I am just dealing with a primitive type however I cannot seem to find any documentation for another way of doing this.
Create a binder provider class for your custom type
public class MyCustomBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.ModelType == typeof(MyCustom))
{
return new BinderTypeModelBinder(typeof(MyCustomBinder));
}
return null;
}
}
and register it in the services
services.AddMvc(c =>
{
c.ModelBinderProviders.Insert(0, new MyCustomBinderProvider());
});
And the custom binder can go like
public class MyCustomBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(MyCustom))
{
return TaskCache.CompletedTask;
}
var parameters = new Dictionary<string, string>();
foreach (var parameter in bindingContext.ModelType.GetProperties())
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(parameter.Name);
if (valueProviderResult.FirstValue != null)
{
parameters.Add(parameter.Name, valueProviderResult.FirstValue);
}
}
var result = Activator.CreateInstance(bindingContext.ModelType);
//write your custom code to map the result with the parameters
bindingContext.Result = ModelBindingResult.Success(result);
return TaskCache.CompletedTask;
}
}
Your custom type class
[ModelBinder(BinderType = typeof(MyCustomBinder))]
public class MyCustom
{
public int Page { get; set; }
public int Rows { get; set; }
}
and your controller can take the custom class as query string parameter
[HttpGet("data")]
public DataTransferObject GetData(MyCustom query)
{
}
Migrating OP's solution from the question to an answer, with meta commentary trimmed:
I just decided to go with a helper class to parse the parameter due to having to meet deadlines.
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);
}
}
I have an interface named IAuthorizationRepository with the following interface:
public interface IAuthorizationRepository
{
IQueryable<User> Users { get; }
Int32 SaveChanges();
void Detach(Object entity);
void Attach(IEntityWithKey entity);
void DeleteObject(Object entity);
void AddObject(String entitySetName, Object entity);
}
Where User is defined as follows:
public class User{
string Username { get; set; }}
And I have a TestInitialize method like this:
[TestInitialize]
public void Init()
{
_repository = new Mock<IAuthorizationRepository>();
List<User> users = new List<User>();
User user = new User();
user.Username = "test_osness";
_repository.ExpectGet(r => r.Users).Returns(users.AsQueryable());
_repository.Expect(r => r.AddObject("Users", It.IsAny<Object>()))
.Callback<User>(u => users.Add(u));
_repository.Object.AddObject("Users", user);
Console.WriteLine("Users: {0}", _repository.Object.Users.Count());
}
But when I run the test I am getting a System.Reflection.TargetParameterCountException on the line which calls _repository.Object.AddObject("Users", user). I'm new to Moq, but from what I can tell this should work. What am I doing wrong. I want to add a user object to my List when AddObject("Users" , Object) is called. So _repository.Object.Users.Count() should reflect that the correct number of users.
I'm gonna overwrite my previous answer.
This line:
_repository.Expect(r => r.AddObject("Users", It.IsAny<Object>()))
.Callback<User>(u => users.Add(u));
Is looking to call a version of .Callback() that takes a single parameter. However, your "AddObject" method that you're expecting takes two parameters. I think you need something more like this:
_repository.Expect(r => r.AddObject("Users", It.IsAny<Object>()))
.Callback((s, o) => users.Add(o as User));
Now the Callback lambda takes the same number of parameters as your expectation, so it should work.