FormatException in custom IUserPasswordStore<Client> implementation - c#

I'm writing custom implementation of IUserPasswordStore<Client> but I got FormatException during login process.
It seems like method GetPasswordHashAsync or someone who call this method calls Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password) which cause FormatException with message
Invalid length for a Base-64 char array or string.
Here is my implementation of IUserPasswordStore<Client>
public partial class ClientRepository : IUserStore<Client>, IUserPasswordStore<Client>, IUserLockoutStore<Client, string>
{
public Task CreateAsync(Client user)
{
return Task.Factory.StartNew(() => Create(user));
}
public Task UpdateAsync(Client user)
{
return Task.Factory.StartNew(() => Update(user));
}
public Task DeleteAsync(Client user)
{
return Task.Factory.StartNew(() => Delete(user));
}
public Task<Client> FindByIdAsync(string userId)
{
return Task.FromResult(Find(userId));
}
public Task<Client> FindByNameAsync(string userName)
{
return Task.FromResult(FetchOne(new ClientByUsername(userName)));
}
public Task SetPasswordHashAsync(Client user, string passwordHash)
{
return Task.Factory.StartNew(() => user.Password = passwordHash);
}
public Task<string> GetPasswordHashAsync(Client user)
{
return Task.FromResult(user.Password);
}
public Task<bool> HasPasswordAsync(Client user)
{
return Task.Factory.StartNew(() => !string.IsNullOrEmpty(user.Password));
}
public Task<DateTimeOffset> GetLockoutEndDateAsync(Client user)
{
return Task.FromResult<DateTimeOffset>(user.LockoutTo ?? DateTime.Now);
}
public Task SetLockoutEndDateAsync(Client user, DateTimeOffset lockoutEnd)
{
return Task.Factory.StartNew(() => user.LockoutTo = lockoutEnd.DateTime);
}
public Task<int> IncrementAccessFailedCountAsync(Client user)
{
return Task.FromResult(++user.LoginAttempts);
}
public Task ResetAccessFailedCountAsync(Client user)
{
return Task.Factory.StartNew(() => user.LoginAttempts = 0);
}
public Task<int> GetAccessFailedCountAsync(Client user)
{
return Task.Factory.StartNew(() => user.LoginAttempts);
}
public Task<bool> GetLockoutEnabledAsync(Client user)
{
return Task.FromResult(false);
}
public Task SetLockoutEnabledAsync(Client user, bool enabled)
{
return Task.FromResult(0);
}
public void Dispose()
{
database = null;
Manager = null;
context = null;
}
}
And my question: Where is called Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password) and what should I return in GetPasswordHashAsync?
For start I'm using password in plain text...
Edit: I thought, maybe I should provide some kind of hash service, so I add dummy implementation of IPasswordHasher for my purpose to use plain text
public class CustomPasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
return password; //return password as is
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (hashedPassword.Equals(providedPassword))
{
return PasswordVerificationResult.Success;
}
return PasswordVerificationResult.Failed;
}
}
// and use it
public class ApplicationUserManager : UserManager<Client>
{
public ApplicationUserManager(IUserStore<Client> store, IPasswordHasher hasher)
: base(store)
{
// ...
PasswordHasher = hasher;
}
}
but breakpoints inside CustomPasswordHasher weren't hit, so propably I'm missing something...

Fixed. I was right with to use CustomPasswordHasher, but I had bug in Unity composition root. lol

Related

Selecting Injected Instance with Conditional

I have several classes that have inherited from one interface. I want the desired service to be loaded and used in the controller depending on the conditions.
Controller
public class GatewayController
{
private readonly IAction action;
public GatewayController(IAction action)
{
this.action = action;
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
try
{
var comId = gatewayRequest.CommandId;
switch (comId)
{
case (int) GuaranteeItemStatus.End:
return await action.Perform(gatewayRequest, cancellationToken); //must be use EndActionService
case (int) GuaranteeItemStatus.SendProduct:
return await action.Perform(gatewayRequest, cancellationToken); //must be use SendProductActionService
default:
return null;
}
}
catch (Exception ex)
{
return ApiResult.ToErrorModel("error");
}
}
}
Parent interface:
public interface IAction
{
Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken);
}
Services:
1-SendProductActionService:
public class SendProductActionService:IAction
{
public async Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
return ApiResult.ToSuccessModel("SendProduct");
}
}
2-EndActionService:
public class EndActionService:IAction
{
public async Task<ApiResult> Perform(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
return ApiResult.ToSuccessModel("EndAction");
}
}
It's possible to register, and inject, an IEnumerable that contains all your IActions.
First, to identify which IAction reacts to which command, you can add a CommandId property:
public interface IAction
{
int CommandId { get; }
}
public class SendProductActionService : IAction
{
public int CommandId => (int)GuaranteeItemStatus.SendProduct;
}
public class EndActionService : IAction
{
public int CommandId => (int)GuaranteeItemStatus.End;
}
In your Startup.cs, you register all your actions:
services.AddScoped<IAction, SendProductActionService>();
services.AddScoped<IAction, EndActionService>();
Then in your controller, you inject all the IAction, and select the appropriate one when needed:
public class GatewayController
{
// map the command ID to the proper IAction
private readonly Dictionary<int, IAction> actions;
// inject all the services registered that implement IAction
public GatewayController(IEnumerable<IAction> actions)
{
this.actions = actions.ToDictionary(_ => _.CommandId);
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
// find the appropriate IAction
if (!actions.TryGetValue((int)gatewayRequest.CommandId, out var action)
return BadRequest();
return await action.Perform(gatewayRequest, cancellationToken);
}
}
In your startup.cs:
services.AddScoped<SendProductActionService>();
services.AddScoped<EndActionService>();
services.AddScoped<Func<GuaranteeItemStatus, IAction>>(serviceProvider => status =>
{
switch (status)
{
case GuaranteeItemStatus.SendProduct:
return serviceProvider.GetService<SendProductActionService>();
case GuaranteeItemStatus.End:
return serviceProvider.GetService<EndActionService>();
default:
throw new InvalidOperationException();
}
});
Your controller should be similar to this:
public class GatewayController
{
private readonly Func<GuaranteeItemStatus, IAction> actionProvider;
public GatewayController(Func<GuaranteeItemStatus, IAction> actionProvider)
{
this.actionProvider = actionProvider;
}
[HttpPost("gateway")]
public async Task<ApiResult> Gateway(GatewayRequest gatewayRequest, CancellationToken cancellationToken)
{
try
{
return await actionProvider((GuaranteeItemStatus)gatewayRequest.CommandId)
.Perform(gatewayRequest, cancellationToken);
}
catch (Exception ex)
{
return ApiResult.ToErrorModel("error");
}
}
}

Issues with DbContext getting disposed after multiple calls to service

I am working on an API and am having problems with making multiple calls to a service and it's different methods, I have each method creating and using new DBContext (or at least that's the intention), but after the first service call the others complain that the DBContext has been disposed, I was hoping you could point me in the right direction, because as far as I can see I am creating a new context for each of these calls - obviously I am doing something wrong here, any help would be much appreciated.
The actual error I am getting is "Cannot access a disposed object."
I know I can maybe pull the db interaction and context creation code out of the service and into the controller method here (it's a simplified example), but will need to use more services in other parts of the application and have encountered the problem there also, so would like to try and identify what is causing my problem in this example so that I can apply the fix elsewhere.
Here are the simplified classes involved.
public class UserController : Controller
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost]
[ActionName("PostUserDetails")]
public async Task<IActionResult> PostUserDetails([FromBody]UserDetailsContract userDetailsContract)
{
// this call is fine
var user = await userService.GetUserByCode(userDetailsContract.Code);
if (user == null)
{
return BadRequest("User not found");
}
// this call fails with the object disposed error
var userDetails = await userService.GetUserDetailsByCode(userDetailsContract.Code);
if (userDetails != null)
{
return BadRequest("UserDetails already exists");
}
// .. go on to save new entity
return Ok();
}
}
public class UserService : IUserService
{
private readonly IDatabaseFactory databaseFactory;
public UserService(IDatabaseFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
public async Task<User> GetUserByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.Users.GetByCode(code);
}
}
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
}
public class ApiDbContext : DbContext, IApiDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserDetail> UserDetails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=192.168.1.1;Database=dbname;User Id=user; Password=pwd; MultipleActiveResultSets=True;");
}
}
public class DatabaseFactory : IDatabaseFactory
{
public IApiDatabase Create()
{
return new ApiDatabase(new ApiDbContext());
}
}
public class ApiDatabase : RepositoriesBase, IApiDatabase
{
private IUserRepository userRepository;
private IUserDetailsRepository userDetailsRepository;
public ApiDatabase(ApiDbContext context) : base(context)
{
}
public IUserRepository Users => userRepository ?? (userRepository = new UserRepository(context));
public IUserDetailsRepository UserExchange => userDetailsRepository ?? (userDetailsRepository = new UserDetailsRepository(context));
}
public abstract class RepositoriesBase : IRepositories
{
internal readonly ApiDbContext context;
private bool isDisposing;
protected RepositoriesBase(ApiDbContext context)
{
}
public void Dispose()
{
if (!isDisposing)
{
isDisposing = true;
context?.Dispose();
}
}
public Task SaveChanges() => context.SaveChangesAsync();
}
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(ApiDbContext context) : base(context)
{
}
public async Task<User> GetByCode(string code)
{
return Filter(x => x.code == code).Result.FirstOrDefault();
}
}
public class UserDetailsRepository : Repository<UserDetail>, IUserDetailRepository
{
public UserExchangeRepository(ApiDbContext context) : base(context)
{
}
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly ApiDbContext context;
public Repository(ApiDbContext context) => this.context = context;
public async Task Add(T entity)
{
context.Set<T>().Add(entity);
}
public async Task Add(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Add(entity);
}
}
public async Task Delete(T entity)
{
context.Set<T>().Remove(entity);
}
public async Task Delete(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Remove(entity);
}
}
public async Task Delete(int id)
{
var entityToDelete = context.Set<T>().FirstOrDefault(e => e.Id == id);
if (entityToDelete != null)
{
context.Set<T>().Remove(entityToDelete);
}
}
public async Task Update(T entity)
{
context.Set<T>().Update(entity);
}
public async Task Edit(T entity)
{
var editedEntity = context.Set<T>().FirstOrDefault(e => e.Id == entity.Id);
editedEntity = entity;
}
public async Task<IEnumerable<T>> GetAll(Expression<Func<T, bool>> predicate = null)
{
var query = context.Set<T>().Include(context.GetIncludePaths(typeof(T)));
if (predicate != null)
{
query = query.Where(predicate);
}
return await query.ToListAsync();
}
public async Task<T> GetById(int id)
{
return context.Set<T>().FirstOrDefault(e => e.Id == id);
}
public async Task<IEnumerable<T>> Filter()
{
return context.Set<T>();
}
public virtual async Task<IEnumerable<T>> Filter(Func<T, bool> predicate)
{
return context.Set<T>().Where(predicate);
}
public async Task SaveChanges() => context.SaveChanges();
}
In my DI config I have DatabaseFactory and UserService defined as singletons.
Error: "Cannot access a disposed object."
More error details: " at
Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at
Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_Model() at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
at System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() at
System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2
predicate) at
App.Api.Controllers.UserController.PostUserDetail(UserDetailContract
userDetailContract) in
D:\Repositories\application\src\App\Api\Controllers\UserController.cs:line
89"
Thank you
I think you may be a victim of delayed execution. The following piece of code creates an instance of of ApiDatabase which in turn creates a new ApiDbContext:
public IApiDatabase Create() //in DatabaseFactory
{
return new ApiDatabase(new ApiDbContext());
}
I detect a code smell here, by the way, as ApiDbContext is disposable so you should be tracking this reference and disposing of it properly.
Anyways, ApiDatabase is disposable since it's wrapped in a using statement, so I think the the context is being disposed after the call to GetByUserId:
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
Notice you are returning an enumeration. I think it may not be materialized by the time you use it, hence the error. Add a cast to an array to force materialization:
return await Filter(x => x.UserId == userId).ToArray();
Your problem is the signature of this method:
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
IEnumerable<T> is an enumerable, which are generally lazy-evaluated. In the meantime, the Task<T> is considered complete once the enumerable is defined (not when it is completed). And the context is disposed once that enumerable is defined. You would have the same problem if the code was synchronous.
The fix is to "reify" (evaluate) the enumerable before the context is disposed:
public async Task<IReadOnlyCollection<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code).ToList();
}
}

Different API functionality for different roles

I have API with asp.net core 2.1. Claims-based authentication. Is it possible to combine these two api function in one?
[Authorize(Roles = "Admin")]
[HttpPost("delete")]
public IActionResult Delete([FromBody]Item item)
{
_itemService.Delete(item.Id);
return Ok();
}
[Authorize]
[HttpPost("delete")]
public IActionResult Delete([FromBody]Item item)
{
var id = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
if (_itemService.IsAuthor(id))
{
_itemService.Delete(item.Id);
return Ok();
}
return Forbid();
}
Or should I just check the role inside method?
For checking the permission with whether the user is Admin or Author, you could implement multiple requirements as the doc from #user2884707bond.
For using the multiple requrements for your scenario.
You could follow steps below:
PermissionHandler.cs
public class PermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList();
foreach (var requirement in pendingRequirements)
{
if (requirement is ReadPermission)
{
if (IsOwner(context.User, context.Resource) ||
IsAdmin(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
else if (requirement is EditPermission ||
requirement is DeletePermission)
{
if (IsOwner(context.User, context.Resource))
{
context.Succeed(requirement);
}
}
}
return Task.CompletedTask;
}
private bool IsAdmin(ClaimsPrincipal user, object resource)
{
if (user.IsInRole("Admin"))
{
return true;
}
return false;
}
private bool IsOwner(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
private bool IsSponsor(ClaimsPrincipal user, object resource)
{
// Code omitted for brevity
return true;
}
}
Requirements
public class ReadPermission : IAuthorizationRequirement
{
// Code omitted for brevity
}
public class EditPermission : IAuthorizationRequirement
{
// Code omitted for brevity
}
public class DeletePermission : IAuthorizationRequirement
{
// Code omitted for brevity
}
Register Requirement in Startup.cs
services.AddAuthorization(options =>
{
options.AddPolicy("Read", policy => policy.AddRequirements(new ReadPermission()));
});
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
Use
[Authorize(Policy = "Read")]
[HttpPost("delete")]
public IActionResult Delete([FromBody]Item item)
{
_itemService.Delete(item.Id);
return Ok();
}

Passing generic functions with paramaters

I think I might be retarded or I'm asking too much out of C# but I can't get this to work.
What essentially I'm trying to do is to wrap an API-client with some logging functions and a method to request a new token from the API-server.
controller:
public class TestController : ApiController
{
[HttpGet]
[Route("api/model/get/{id}"]
public IHttpActionResult GetModel<Model>(int id)
{
var result = Service.DoHttp<Model>(ServiceClass.GetModel, id);
}
}
service:
public static class ServiceClass
{
private static readonly HttpClient client = new HttpClient() { BaseAddress = new Uri(Globals.ExternalApiPath) };
private static string TokenHeader = "";
public async static Task<HttpResponseMessage> GetModel(int id)
{
var response = client.GetAsync($"/api/get/{id}");
return await response;
}
public static T DoHttp<T>(Func<int, HttpResponseMessage> funk, int id)
{
try
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", TokenHeader);
var result = funk(id);
if (result.IsSuccessStatusCode)
{
return result.Content.ReadAsAsync<T>().Result;
}
else
{
throw new Exception(String.Format($"Unknown error! Unable to contact remote API! AccessToken: {TokenHeader} Status code: {result.StatusCode}"));
}
}
catch (Exception e)
{
// log ex
throw e;
}
}
}
But my Service.DoHttp(Service.GetModel, id); complains about it being the wrong return type.
What am I doing wrong or have I misunderstood the whole concept?
EDIT: Compiler complains about 'Task ServiceClass.GetModel(int)' has the wrong return type
Change the DoHttp method to the following.
public static T DoHttp<T>(Func<int, Task<HttpResponseMessage>> funk, int id)
As the GetModel method returns a Task you need to use a task as the return type of the Func too.

UserManager.CreateAsync(user, password) stuck in infinite loop

I'm trying to make a very simple implementation of an IUserStore that would essentially:
use NHibernate
save users in a single table (no claims/logins)
store role names in the same table, in an nvarchar column (pipe ('|')
separated if multiple items).
When I run the following method:
[Fact]
public void Create_A_User()
{
// _session is a valid NHibernate ISession object
using (var userStore = new SimpleUserStore<SimpleIdentityUser>(_session))
using (var userManager = new UserManager<SimpleIdentityUser>(userStore))
{
var user = new SimpleIdentityUser
{
UserName = "kenny_mccormick",
RolesStr = "admin",
};
var createTask = userManager.CreateAsync(user, "the_password");
var result = createTask.Result; // this never finishes...
}
}
the last line would never finish executing.
The weird thing is that the UserManager never calls any of the functions in my SimpleUserStore; it gets stuck before that.
Here are the components I defined:
The user class:
public class SimpleIdentityUser : IUser
{
public virtual Guid UserId { get; set; }
public virtual string PasswordHash { get; set; }
public virtual string SecurityStamp { get; set; }
public virtual string RolesStr { get; set; }
public virtual string UserName { get; set; }
public virtual string Id
{
get { return UserId.ToString(); }
}
}
The User Store:
public class SimpleUserStore<TUser> :
IUserPasswordStore<TUser>,
IUserRoleStore<TUser>,
IUserSecurityStampStore<TUser>
where TUser : SimpleIdentityUser
{
// ReSharper disable once StaticFieldInGenericType
private static readonly Task EmptyTask = new Task(() => { });
private readonly ISession _session;
public SimpleUserStore(ISession session)
{
_session = session;
}
public Task<TUser> FindAsync(UserLoginInfo login)
{
return Task.FromResult((TUser) null);
}
public Task CreateAsync(TUser user)
{
_session.Save(user);
return EmptyTask;
}
public Task UpdateAsync(TUser user)
{
// updates will (hopefully) be saved automatically when the current session is committed
return EmptyTask;
}
public Task DeleteAsync(TUser user)
{
_session.Delete(user);
return EmptyTask;
}
public Task<TUser> FindByIdAsync(string userId)
{
TUser user = null;
Guid guidId;
if (Guid.TryParse(userId, out guidId))
user = _session.Get<TUser>(guidId);
return Task.FromResult(user);
}
public Task<TUser> FindByNameAsync(string userName)
{
TUser user = _session.Query<TUser>().SingleOrDefault(u => u.UserName == userName);
return Task.FromResult(user);
}
public Task SetPasswordHashAsync(TUser user, string passwordHash)
{
user.PasswordHash = passwordHash;
return EmptyTask;
}
public Task<string> GetPasswordHashAsync(TUser user)
{
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(TUser user)
{
return Task.FromResult(user.PasswordHash != null);
}
public void Dispose()
{
}
public Task AddToRoleAsync(TUser user, string role)
{
new SimpleRoleManager<TUser>(user).AddRole(role);
return EmptyTask;
}
public Task RemoveFromRoleAsync(TUser user, string role)
{
new SimpleRoleManager<TUser>(user).DeleteRole(role);
return EmptyTask;
}
public Task<IList<string>> GetRolesAsync(TUser user)
{
List<string> roles = new SimpleRoleManager<TUser>(user).GetRoles().ToList();
return Task.FromResult((IList<string>) roles);
}
public Task<bool> IsInRoleAsync(TUser user, string role)
{
return Task.FromResult(new SimpleRoleManager<TUser>(user).IsInRole(role));
}
public Task SetSecurityStampAsync(TUser user, string stamp)
{
user.SecurityStamp = stamp;
return EmptyTask;
}
public Task<string> GetSecurityStampAsync(TUser user)
{
return Task.FromResult(user.SecurityStamp);
}
}
How I manage roles
Probably not so important, but here it is anyway:
public class SimpleRoleManager<TUser> where TUser : SimpleIdentityUser
{
private const string Separator = "|";
private readonly TUser _user;
public SimpleRoleManager(TUser user)
{
_user = user;
}
public string[] GetRoles()
{
return (_user.RolesStr ?? String.Empty)
.Split(Separator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
public bool IsInRole(string roleName)
{
return GetRoles().Contains(roleName);
}
public bool AddRole(string roleName)
{
var roles = GetRoles().ToList();
if (roles.Contains(roleName))
return false;
roles.Add(roleName);
SetRoles(roles);
return true;
}
public bool DeleteRole(string roleName)
{
List<string> roles = GetRoles().ToList();
if (!roles.Contains(roleName))
return false;
roles.Remove(roleName);
SetRoles(roles);
return true;
}
private void SetRoles(IEnumerable<string> roles)
{
_user.RolesStr = String.Join(Separator, roles);
}
}
I have been inspecting the UserManager<TUser> class with DotPeek, but found no obvious causes for this weird behavior.
What could be causing this?
Your approach to async is fundamentally broken at the moment, because you're returning the same task for all operations... and never starting it. I don't see any "infinite loop" here - I just see you blocking on a task which can never complete.
It's not clear what you hope to accomplish with your EmptyTask task, but it's definitely not helping you at the moment.
Furthermore, it's not clear that your code is really asynchronous in any aspect, unless _session.Save (etc) are really asynchronous.
You could improve things somewhat by just running extra tasks, e.g.
public Task CreateAsync(TUser user)
{
Action action = () => _session.Save(user);
return Task.Run(action);
}
... although the fact that you're then immediately blocking on the task in the calling code makes it pointless, too. (It's not even clear how this compiles at the moment, as Task doesn't have a Result property... only Task<T> does.)
As noted in comments, this will create a new task which will effectively run synchronously in a new thread - using more threads than you'd otherwise need, with the potential benefit of parallelism. It doesn't achieve the same goals as a properly asynchronous database call (where there wouldn't be any threads tied up for the save call - just a task which would complete when the relevant network response was returned).
If you don't really care about it being asynchronous, you can use:
public Task CreateAsync(TUser user)
{
_session.Save(user);
return Task.FromResult<object>(null);
}
This will synchronously save (just like your current code does) but then return a completed task (rather than the task which will never complete, as per your current code).

Categories

Resources