0

DB でファイルの内容をフェッチするカスタム IfileProvider を実装しました。用途の 1 つは、css を取得することです。問題は、型エラーが頻繁に発生することです

InvalidOperationException: 前の操作が完了する前に、このコンテキストで 2 番目の操作が開始されました。これは通常、異なるスレッドが 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;
        }
    }

DatabaseChangeToken

    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