控制台应用程序-DbContext实例不能在OnConfiguring内部使用

时间:2018-10-02 06:52:12

标签: multithreading dependency-injection .net-core console-application ef-core-2.0

我正在使用Asp.Net Core控制台应用程序和Entiy Framework Core和工作单元存储库模式。使用多线程功能时,出现此错误:

  

DbContext实例不能在OnConfiguring内部使用,因为此时仍在配置它。如果在上一个操作完成之前在此上下文上启动了第二个操作,则会发生这种情况。不保证任何实例成员都是线程安全的。

UnitOfwork.cs

public interface IUnitOfWork : IDisposable
{
    void Commit();
    ApplicationDbContext GetContext();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _applicationDbContext;

    public UnitOfWork(ApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public void Commit()
    {
        try
        {
            _applicationDbContext.SaveChanges();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }

    public ApplicationDbContext GetContext()
    {
        return _applicationDbContext;
    }

    public void Dispose()
    {
        _applicationDbContext.Dispose();
    }
}

IRepository.cs

public interface IGenericRepository<T>
    where T : class, IEntity
{
    List<T> GetAll(Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
        string includeProperties = "");

    T FindSingle(int id);

    T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "");

    void Add(T toAdd);

    void Update(T toUpdate);

    void Delete(int id);

    void Delete(T entity);
}

Repository.cs

public class GenericRepository<T> : IGenericRepository<T>
    where T : class, IEntity
{
    private readonly IUnitOfWork _unitOfWork;

    public GenericRepository(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public virtual List<T> GetAll(Expression<Func<T, bool>> filter = null,
        Func<IQueryable<T>, IOrderedEnumerable<T>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<T> query = _unitOfWork.GetContext().Set<T>();

        if (filter != null)
        {
            query = query.Where(filter);
        }

        foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }

        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }

        return query.ToList();
    }

    public virtual T FindSingle(int id)
    {
        return _unitOfWork.GetContext().Set<T>().Find(id);
    }

    public virtual T FindBy(Expression<Func<T, bool>> predicate, string includeProperties = "")
    {
        IQueryable<T> query = _unitOfWork.GetContext().Set<T>();
        foreach (string includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }
        return query.Where(predicate).FirstOrDefault();
    }

    public virtual void Add(T toAdd)
    {
        _unitOfWork.GetContext().Set<T>().Add(toAdd);
    }

    public virtual void Update(T toUpdate)
    {
        _unitOfWork.GetContext().Entry(toUpdate).State = EntityState.Modified;
    }

    public virtual void Delete(int id)
    {
        T entity = FindSingle(id);
        _unitOfWork.GetContext().Set<T>().Remove(entity);
    }

    public virtual void Delete(T entity)
    {
        _unitOfWork.GetContext().Set<T>().Remove(entity);
    }
}

商业服务;

public interface IUserService
{
    void CreateUser(UserEntity userEntity, bool commit = false);
}
public class UserService : IUserService
{
    private readonly IGenericRepository<UserEntity> _userRepository;
    private readonly IUnitOfWork _unitOfWork;

    public UserService(IUnitOfWork unitOfWork, IGenericRepository<UserEntity> userRepository)
    {
        _unitOfWork = unitOfWork;
        _userRepository = userRepository;
    }

    public void CreateUser(UserEntity userEntity, bool commit = false)
    {
        try
        {
            _userRepository.Add(userEntity);

            if (commit)
                _unitOfWork.Commit();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

控制台Main.cs;

class Program
{
    public static ServiceProvider ServiceProvider;

    static void Main(string[] args)
    {
        InitializeIoc();

        Task.Run(() => { FuncA(); });

        Task.Run(() => { FuncB(); });

        Console.ReadLine();
    }

    private static void InitializeIoc()
    {
        ServiceProvider = new ServiceCollection()
            .AddDbContext<ApplicationDbContext>()
            .AddTransient<IUnitOfWork, UnitOfWork>()
            .AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>))
            .AddTransient<IUserService, UserService>()
            .BuildServiceProvider();
    }

    private static void FuncA()
    {
        var userService = ServiceProvider.GetService<IUserService>();
        for (int i = 0; i < 100; i++)
        {
            userService.CreateUser(new UserEntity { FirstName = "FuncA_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
        }
    }
    private static void FuncB()
    {
        var userService = ServiceProvider.GetService<IUserService>();
        for (int i = 0; i < 100; i++)
        {
            userService.CreateUser(new UserEntity { FirstName = "FuncB_" + Guid.NewGuid(), LastName = "Last", CreatedDate = DateTime.Now }, false);
        }
    }
}

我该如何解决这个问题?

谢谢您的帮助。

1 个答案:

答案 0 :(得分:2)

问题是使用的AddDbContextApplicationDbContext注册了ServiceLifetime.Scoped,但是您没有创建范围,因此它有效地作为单例工作,因此被多个线程同时共享和访问这会导致有问题的异常(可能还有许多其他异常,因为DbContext不是线程安全的。)

解决方案是使用范围,例如调用CreateScope并使用返回的对象ServiceProvider属性来解析服务:

private static void FuncA()
{
    using (var scope = ServicePropvider.CreateScope())
    {
        var userService = scope.ServiceProvider.GetService<IUserService>();
        // Do something ...
    }
}

private static void FuncB()
{
    using (var scope = ServicePropvider.CreateScope())
    {
        var userService = scope.ServiceProvider.GetService<IUserService>();
        // Do something ...
    }
}