0

我实现了一个自定义 IfileProvider,它将获取数据库中文件的内容。用途之一是去获取 css。问题是我经常遇到类型错误

InvalidOperationException:在前一个操作完成之前在此上下文上启动了第二个操作。这通常是由使用相同 DbContext 实例的不同线程引起的。有关如何避免 DbContext 线程问题的更多信息

我认为起源是我在启动时使用依赖注入。我尝试了所有类型的配置,但没有结果。

我的文件 :

启动

public void ConfigureServices(IServiceCollection services)
{
    [...]
    services.AddTransient<IAssetDBRepository, AssetDBRepository>();
    services.AddTransient<IAssetDBService, AssetDBService>();
    services.AddSingleton<CacheMemoryHelper>();
    [...]   
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseNpgsql(
            applicationDBConnectionStringDecrypted,
                b =>
                {
                    b.MigrationsAssembly(migrationsAssembly);
                    if (!string.IsNullOrWhiteSpace(applicationDBVersion))
                        b.SetPostgresVersion(new Version(applicationDBVersion));
                }),
                ServiceLifetime.Transient,
                ServiceLifetime.Transient
        ) ;
    }
    [...]   

public void Configure(IApplicationBuilder app, CacheMemoryHelper memoryCache)
{
    [...]
    var assetDbService = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider.GetRequiredService<IAssetDBService>();
    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new DatabaseFileProvider(assetDbService, memoryCache),
        RequestPath = new PathString("/" + Constants.PATH_ASSETDB)
    });
    [...]
}

数据库文件提供者

public class DatabaseFileProvider : IFileProvider
{
    private  IAssetDBService _assetdbService;
    private readonly CacheMemoryHelper _cache;
    public DatabaseFileProvider(IAssetDBService assetService,CacheMemoryHelper memoryCache)
    {
        _assetdbService = assetService;
        _cache = memoryCache;
    }

    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        throw new NotImplementedException();
    }

    public IFileInfo GetFileInfo(string idAsset)
    {
        var result = new DatabaseFileInfo(_assetdbService, _cache,idAsset);
        return result.Exists ? result as IFileInfo : new NotFoundFileInfo(idAsset);
    }

    public IChangeToken Watch(string filter)
    {
        return new DatabaseChangeToken(_assetdbService, filter);
    }
}

数据库文件信息

    public class DatabaseFileInfo : IFileInfo
    {
        private readonly string _viewPath;

        private byte[] _assetContent;
        private DateTimeOffset _lastModified;
        private bool _exists;

        public DatabaseFileInfo(IAssetDBService assetService, CacheMemoryHelper memoryCache,string viewPath)
        {
            _viewPath = viewPath;
            
            //Async method
            //Task<bool> task = Task.Run<bool>(async () => await GetAsset(assetService, memoryCache,_viewPath));
            //var result = task.Result;
            
            GetAsset(assetService, memoryCache, _viewPath);
        }

        public bool Exists => _exists;

        public bool IsDirectory => false;

        public DateTimeOffset LastModified => _lastModified;

        public long Length
        {
            get
            {
                using (var stream = new MemoryStream(_assetContent))
                {
                    return stream.Length;
                }
            }
        }

        public string Name => Path.GetFileName(_viewPath);

        public string PhysicalPath => null;

        public Stream CreateReadStream()
        {
            return new MemoryStream(_assetContent);
        }

        private async Task<bool> GetAssetAsync(IAssetDBService assetService, CacheMemoryHelper memoryCache, string viewPath)
        {
            Asset asset = null;
            if (memoryCache == null)
            {
                asset = await assetService.GetAssetAsync(_viewPath);
            }

            else
            {
                asset = memoryCache.Get<Asset>(viewPath);
                if (asset == null)
                {
                    asset = await assetService.GetAssetAsync(_viewPath);
                    if (asset != null)
                        memoryCache.Set<Asset>(viewPath, asset);
                }
            }


            if (asset != null)
            {
                var result = await assetService.UpdateDateLastRequestedAssetAsync(asset.Id);

                _exists = true;
                _assetContent = asset.Content;
                _lastModified = asset.LastModified;
            }
            else
            {
                _exists = false;
            }

            return true;
        }

        private bool GetAsset(IAssetDBService assetService, CacheMemoryHelper memoryCache, string viewPath)
        {
            Asset asset = null;
            if (memoryCache == null)
            {
               asset =  assetService.GetAsset(_viewPath);
            }
                
            else
            {
                asset = memoryCache.Get<Asset>(viewPath);
                if (asset == null)
                {
                    asset = assetService.GetAsset(_viewPath);
                    if (asset != null)
                        memoryCache.Set<Asset>(viewPath, asset);
                }
            }
            

            if (asset != null)
            {
                var result = assetService.UpdateDateLastRequestedAsset(asset.Id);

                _exists = true;
                _assetContent = asset.Content;
                _lastModified = asset.LastModified;
            }
            else
            {
                _exists = false;
            }

            return true;
        }
    }

数据库变更令牌

    public class DatabaseChangeToken : IChangeToken
    {
        private readonly IAssetDBService _assetdbService;
        private string _viewPath;

        public DatabaseChangeToken(IAssetDBService assetService, string viewPath)
        {
            _assetdbService = assetService;
            _viewPath = viewPath;
        }

        public bool ActiveChangeCallbacks => false;

        public bool HasChanged
        {
            get
            {
                Asset asset = _assetdbService.GetAsset(_viewPath);
                if (asset == null)
                    return false;
                if (asset.LastModified == null || asset.LastRequested == null)
                    return false;
                var result = Convert.ToDateTime(asset.LastModified) > Convert.ToDateTime(asset.LastRequested);
                return result;
            }
        }

        public IDisposable RegisterChangeCallback(Action<object> callback, object state) => EmptyDisposable.Instance;
    }

    internal class EmptyDisposable : IDisposable
    {
        public static EmptyDisposable Instance { get; } = new EmptyDisposable();
        private EmptyDisposable() { }
        public void Dispose() { }
    }


我通过将 IappBuilder 直接传递给 DatabaseFileProvider 解决了这个问题

            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new DatabaseFileProvider(app, memoryCache),
                RequestPath = new PathString("/" + Constants.PATH_ASSETDB)
            });

但我认为这不是最好的方法。

你有想法吗?

谢谢你。

4

0 回答 0