异步不工作+回购的EF + UINT
我试图实现UOW &回购模式一起EF和ASP.net API项目。异步不工作+回购的EF + UINT
首先,我想指出,我知道DbContext & DbSets是UoW & Repo模式的实现,但我正在玩弄看看什么适合我的项目。
问题
我注意到,如果我叫异步回购方式从我的服务,什么也没有发生,方法被调用,但似乎等待永远不会触发。如果我同步调用方法,一切都很好。 (实施例方法的是计数/ CountAsync)。
什么是更奇怪(我想不通为什么),这同样的方法调用由于某种原因,在一个服务方法的工作,但不是在另一个。
我将在更多的细节后,我提出我的代码解释。
PROJCET STRUCTRUE
我的项目是这样构成:
- 我有API在服务层注入
- 在服务注入UOW &回购
- 在回购注射UOW
- 最后UOW调用数据库上下文工厂,其职责是CREA我DbContex
--- CODE ---
这里的德新实例是当前实现,当然代码的某些部分不再赘述。
数据库上下文工厂
/// <summary>
/// Interface for factory which is in charge of creating new DbContexts
/// </summary>
/// <autogeneratedoc />
public interface IDatabaseContextFactory
{
/// <summary>
/// Creates new Master Database Context.
/// </summary>
/// <returns>newly created MasterDatabaseContext</returns>
/// <autogeneratedoc />
DbContext MasterDbContext();
}
/// <inheritdoc />
/// <summary>
/// This is factory which is in charge of creating new DbContexts
/// It is implemented as Singleton as factory should be implemented (according to Gang of four)
/// </summary>
/// <seealso cref="T:Master.Domain.DataAccessLayer.IDatabaseContextFactory" />
/// <autogeneratedoc />
public class DatabaseContextFactory : IDatabaseContextFactory
{
/// <summary>
/// This is implementation of singleton
/// </summary>
/// <remarks>
/// To read more, visit: http://csharpindepth.com/Articles/General/Singleton.aspx (Jon skeet)
/// </remarks>
/// <autogeneratedoc />
private static readonly DatabaseContextFactory instance = new DatabaseContextFactory();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit (more about this: http://csharpindepth.com/Articles/General/Beforefieldinit.aspx)
static DatabaseContextFactory()
{
}
//so that class cannot be initiated
private DatabaseContextFactory()
{
}
/// <summary>
/// Instance of DatabaseContextFactory
/// </summary>
public static DatabaseContextFactory Instance => instance;
/// <inheritdoc />
/// <summary>
/// Creates new MasterDatabaseContext
/// </summary>
/// <returns></returns>
public DbContext MasterDbContext()
{
return new MasterDatabaseContext();
}
}
工作单位
通用库
/// <summary>
/// Generic repository pattern implementation
/// Repository Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
/// </summary>
/// <remarks>
/// More info: https://martinfowler.com/eaaCatalog/repository.html
/// </remarks>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <autogeneratedoc />
public interface IMasterRepository<TEntity, in TKey> where TEntity : class
{
/// <summary>
/// Gets entity (of type) from repository based on given ID
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Entity</returns>
/// <autogeneratedoc />
TEntity Get(TKey id);
/// <summary>
/// Asynchronously gets entity (of type) from repository based on given ID
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
/// <autogeneratedoc />
Task<TEntity> GetAsnyc(TKey id);
/// <summary>
/// Gets all entities of type from repository
/// </summary>
/// <returns></returns>
/// <autogeneratedoc />
IEnumerable<TEntity> GetAll();
/// <summary>
/// Asynchronously gets all entities of type from repository
/// </summary>
/// <returns></returns>
/// <autogeneratedoc />
Task<IEnumerable<TEntity>> GetAllAsync();
/// <summary>
/// Finds all entities of type which match given predicate
/// </summary>
/// <param name="predicate">The predicate.</param>
/// <returns>Entities which satisfy conditions</returns>
/// <autogeneratedoc />
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
}
//Note to self: according to P of EAA Repo plays nicely with QueryObject, Data mapper and Metadata mapper - Learn about those !!!
/// <summary>
/// Generic repository pattern implementation
/// Repository Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <seealso cref="Master.Domain.DataAccessLayer.Repository.Generic.IMasterRepository{TEntity, TKey}" />
/// <inheritdoc cref="IMasterRepository{TEntity,TKey}" />
public class MasterRepository<TEntity, TKey> : IMasterRepository<TEntity, TKey>
where TEntity : class
{
/// <summary>
/// DbSet is part of EF, it holds entities of the context in memory, per EF guidelines DbSet was used instead of IDbSet
/// </summary>
/// <remarks>
/// <para>
/// Even though we are not 100% happy about this,
/// We decided to go with this instead of (for example) IEnumerable so that we can use benefits of <see cref="DbSet"/>
/// Those benefits for example are Find and FindAsync methods which are much faster in fetching entities by their key than for example Single of First methods
/// </para>
/// </remarks>
/// <autogeneratedoc />
private readonly DbSet<TEntity> _dbSet;
/// <summary>
/// Initializes a new instance of the <see cref="MasterRepository{TEntity, TKey}"/> class.
/// </summary>
/// <param name="unitOfWork">The unit of work.</param>
/// <exception cref="ArgumentNullException">unitOfWork - Unit of work cannot be null</exception>
/// <autogeneratedoc />
public MasterRepository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null)
{
throw new ArgumentNullException(nameof(unitOfWork), @"Unit of work cannot be null");
}
_dbSet = unitOfWork.DatabaseContext.Set<TEntity>();
}
/// <inheritdoc />
/// <summary>
/// Gets entity with given key
/// </summary>
/// <param name="id">The key of the entity</param>
/// <returns>Entity with key id</returns>
public TEntity Get(TKey id)
{
return _dbSet.Find(id);
}
/// <inheritdoc />
/// <summary>
/// Asynchronously gets entity with given key
/// </summary>
/// <param name="id">The key of the entity</param>
/// <returns>Entity with key id</returns>
public async Task<TEntity> GetAsnyc(TKey id)
{
return await _dbSet.FindAsync(id);
}
/// <inheritdoc />
/// <summary>
/// Gets all entities
/// </summary>
/// <returns>List of entities of type TEntiy</returns>
public IEnumerable<TEntity> GetAll()
{
return _dbSet.ToList();
}
public async Task<IEnumerable<TEntity>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
return _dbSet.Where(predicate).ToList();
}
}
部保存的电影资源库
/// <inheritdoc />
/// <summary>
/// Repository for dealing with <see cref="T:Master.Domain.Model.MovieAggregate.SavedMovie" /> entity
/// </summary>
/// <seealso cref="!:Master.Domain.DataAccessLayer.Repository.Generic.IMasterRepository{Master.Domain.Model.MovieAggregate.SavedMovie,System.Guid}" />
/// <autogeneratedoc />
public interface ISavedMoviesRepository : IMasterRepository<SavedMovie, Guid>
{
/// <summary>
/// Asynchronously Gets number of saved Movies for the user
/// </summary>
/// <param name="user">The user.</param>
/// <returns>Number of saved Movies</returns>
/// <autogeneratedoc />
Task<int> CountForUser(Model.UserAggregate.User user);
}
/// <inheritdoc cref="ISavedMoviesRepository" />
/// />
/// <summary>
/// Repository for dealing with <see cref="T:Master.Domain.Model.MovieAggregate.SavedMovie" /> entity
/// </summary>
/// <seealso cref="!:Master.Domain.DataAccessLayer.Repository.Generic.MasterRepository{Master.Domain.Model.MovieAggregate.SavedMovie, System.Guid}" />
/// <seealso cref="T:Master.Domain.DataAccessLayer.Repository.SavedMovies.ISavedMoviesRepository" />
/// <autogeneratedoc />
public class SavedMovieRepository : MasterRepository<SavedMovie, Guid>, ISavedMoviesRepository
{
/// <summary>
/// Ef's DbSet - in-memory collection for dealing with entities
/// </summary>
/// <autogeneratedoc />
private readonly DbSet<SavedMovie> _dbSet;
private readonly IUnitOfWork _unitOfWork;
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Master.Domain.DataAccessLayer.Repository.SavedMovies.SavedMovieRepository" /> class.
/// </summary>
/// <param name="unitOfWork">The unit of work.</param>
/// <exception cref="T:System.ArgumentNullException"></exception>
/// <autogeneratedoc />
public SavedMovieRepository(UnitOfWork.UnitOfWork unitOfWork) : base(unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException();
_dbSet = unitOfWork.DatabaseContext.Set<SavedMovie>();
_unitOfWork = unitOfWork;
}
/// <inheritdoc />
/// <summary>
/// Asynchronously Gets number of saved Movies for the user
/// </summary>
/// <param name="user">The user.</param>
/// <returns>
/// Number of saved Movies
/// </returns>
/// <exception cref="T:System.ArgumentNullException">user - User cannot be null</exception>
/// <autogeneratedoc />
public async Task<int> CountForUser(Model.UserAggregate.User user)
{
if (user == null)
throw new ArgumentNullException(nameof(user), @"User cannot be null");
return await _dbSet.CountAsync(r => r.UserWhoSavedId == user.Id);
}
}
保存的电影服务
/// <inheritdoc />
/// <summary>
/// This is service for handling saved Movies!
/// </summary>
/// <seealso cref="T:Master.Infrastructure.Services.SavedMovieService.Interfaces.ISavedMovieService" />
/// <autogeneratedoc />
public class SavedMovieService : ISavedMovieService
{
/// <summary>
/// The saved Movies repository <see cref="ISavedMoviesRepository"/>
/// </summary>
/// <autogeneratedoc />
private readonly ISavedMoviesRepository _savedMoviesRepository;
/// <summary>
/// The unit of work <see cref="IUnitOfWork"/>
/// </summary>
/// <autogeneratedoc />
private readonly IUnitOfWork _unitOfWork;
/// <summary>
/// The user repository <see cref="IUserRepository"/>
/// </summary>
/// <autogeneratedoc />
private readonly IUserRepository _userRepository;
/// <summary>
/// Initializes a new instance of the <see cref="SavedMovieService"/> class.
/// </summary>
/// <param name="savedMoviesRepository">The saved Movies repository.</param>
/// <param name="userRepository">The user repository.</param>
/// <param name="unitOfWork">The unit of work.</param>
/// <autogeneratedoc />
public SavedMovieService(ISavedMoviesRepository savedMoviesRepository, IUserRepository userRepository,
IUnitOfWork unitOfWork)
{
_savedMoviesRepository = savedMoviesRepository;
_userRepository = userRepository;
_unitOfWork = unitOfWork;
}
public Task<int> CountNumberOfSavedMoviesForUser(string userId)
{
if (string.IsNullOrEmpty(userId))
throw new ArgumentNullException(nameof(userId), @"User id must not be empty");
var user = _userRepository.Get(userId);
return _savedMoviesRepository.CountForUser(user);
}
public async Task<Guid> SaveWorkoutFromLibraryAsync(string userWhoIsSavingId, Guid galleryId,
bool isUserPro)
{
if (string.IsNullOrEmpty(userWhoIsSavingId))
throw new ArgumentNullException(nameof(userWhoIsSavingId), @"User id cannot be empty");
if (galleryId == Guid.Empty)
throw new ArgumentException(@"Id of gallery cannot be empty", nameof(galleryId));
//get user who is saving from DB
var userWhoIsSaving = _userRepository.Get(userWhoIsSavingId);
if (userWhoIsSaving == null)
throw new ObjectNotFoundException($"User with provided id not found - id: {userWhoIsSavingId}");
//how many movies has this user saved so far
var numberOfAlreadySavedMoviesForUser = await _savedWorkoutsRepository.CountForUserAsync(userWhoIsSaving);
// more code here
}
}
网络API控制器
[Authorize]
[RoutePrefix("api/Saved")]
[ApiVersion("2.0")]
public class SavedController : ApiController
{
private readonly ISavedMovieService _savedMovieService;
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:Master.Infrastructure.Api.V2.Controllers.SavedController" /> class.
/// </summary>
/// <param name="savedMovieService">The saved Movie service.</param>
/// <autogeneratedoc />
public SavedController(ISavedMovieService savedMovieService)
{
_savedMovieService = savedMovieService;
}
public async Task<IHttpActionResult> GetNumberOfSavedForUser()
{
var cnt = await _savedMovieService.CountNumberOfSavedMoviesForUser(User.Identity.GetUserId());
return Ok(cnt);
}
public async Task<IHttpActionResult> SaveFromGalery(SaveModel model)
{
await _savedMovieService.SaveWorkoutFromGalleryAsync(User.Identity.GetUserId(), model.Id, model.IsPro);
return Ok();
}
}
Ninject配置
(仅重要部分)
kernel.Bind<MasterDatabaseContext>().ToSelf().InRequestScope();
kernel.Bind<IDatabaseContextFactory>().ToMethod(c => DatabaseContextFactory.Instance).InSingletonScope();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
kernel.Bind(typeof(IMasterRepository<,>)).To(typeof(MasterRepository<,>));
kernel.Bind<ISavedMoviesRepository>().To<SavedMovieRepository>();
kernel.Bind<IUserRepository>().To<UserRepository>();
kernel.Bind<ISavedMovieService>().To<SavedMovieService>();
我想指出的是,我有对夫妇更回购(4总共包括保存和用户)在SavedService注入,但我不相信他们是相关因为它们与SavedRepo几乎相同,但如果需要,我也可以添加它们。这也只是目前实现这种模式和方法的服务。
因此,这里所发生的事情,当我打电话SaveFromGalery
:
- UOW构造函数被调用
- DatabaseContextFactory MasterDbContext()被调用
- MasterRepository构造函数被调用
- SavedMoviesRepository构造函数被调用
- UoW构造函数被调用(再次第二次)
- DatabaseContextFactory MasterDbContext()被调用(第二次)
- MasterRepository调用构造函数(又第二次)
- UserRepository叫
- MasterRepository构造函数被调用
- MasterRepository调用构造函数(再次第四届(又第3次)时间)
- SavedService构造函数被调用
- HTTP GET SaveFromGalery叫
- 用户是成功 用户获取回购
- _savedWorkoutsRepository。CountForUserAsync叫
- 程序进入方法打的await但从来没有返回结果
就当GetNumberOfSavedForUser
被称为另一方面,会出现以下情况:
- 1 - 11步骤是相同的
- HTTP GET GetNumberOfSavedForUser称为
- 用户是从用户回购成功
- _savedWorkou取出tsRepository.CountForUserAsync被称为SUCCESSFULLY
- UOW调用Dispose
- UOW调用Dispose
而且如前所述,如果,_savedWorkoutsRepository.CountForUserAsync由同步一切进行罚款。
有人可以请我帮忙看看究竟发生了什么?
您可能正在使用Wait
或Result
在您的真实世界的代码(而不是这里发布的代码,这是不完整的)。这将在ASP.NET经典的cause a deadlock。
具体来说,当你将任务传递给await
时,默认情况下它将捕获“当前上下文”并使用它在该任务完成时恢复异步方法。然后,任务上的代码块(即,Wait
或Result
)。问题在于,ASP.NET经典的上下文一次只允许在一个线程中。所以只要该线程在任务中被阻塞,它就“接近”上下文,这实际上阻止了任务的完成。因此,僵局。
请注意,ConfigureAwait(false)
是不是一个修复;它至多是一种解决方法。正确的解决办法是用await
替换Wait
/Result
。
也许可能会有一些死锁。尝试使用“.ConfigureAwait(false)”和个人,我会重构方法,如'异步任务 SomeMethod(){返回等待someService.MethodAsync();}'任务 SomeMethod(){return someService.MethodAsync );}' –
Fildor
@Fildor感谢您的回答和重构提示。不幸的是,'.ConfigureAwait(false)'没有帮助:( – hyperN
@Fildor我的错误!它实际上**确实有帮助**。请接受我的道歉,我快速回答,我把'.ConfigureAwait(false)'都回购和服务,这有助于你知道也许有什么可以使用这种僵局和'.ConfigureAwait(false)'如何帮助?谢谢! – hyperN