Commit 0308c030 authored by Savorboard's avatar Savorboard

Refactor mongodb module for new transaction mode

parent 53c5c74c
...@@ -24,9 +24,12 @@ namespace DotNetCore.CAP ...@@ -24,9 +24,12 @@ namespace DotNetCore.CAP
services.AddSingleton<CapDatabaseStorageMarkerService>(); services.AddSingleton<CapDatabaseStorageMarkerService>();
services.AddSingleton<IStorage, SqlServerStorage>(); services.AddSingleton<IStorage, SqlServerStorage>();
services.AddSingleton<IStorageConnection, SqlServerStorageConnection>(); services.AddSingleton<IStorageConnection, SqlServerStorageConnection>();
services.AddScoped<ICapPublisher, CapPublisher>();
services.AddScoped<ICallbackPublisher, CapPublisher>(); services.AddScoped<ICapPublisher, SqlServerPublisher>();
services.AddScoped<ICallbackPublisher, SqlServerPublisher>();
services.AddTransient<ICollectProcessor, SqlServerCollectProcessor>(); services.AddTransient<ICollectProcessor, SqlServerCollectProcessor>();
services.AddTransient<CapTransactionBase, SqlServerCapTransaction>();
AddSqlServerOptions(services); AddSqlServerOptions(services);
} }
......
...@@ -4,26 +4,26 @@ ...@@ -4,26 +4,26 @@
using System; using System;
using System.Data; using System.Data;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapper; using Dapper;
using DotNetCore.CAP.Abstractions; using DotNetCore.CAP.Abstractions;
using DotNetCore.CAP.Models; using DotNetCore.CAP.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
namespace DotNetCore.CAP.SqlServer namespace DotNetCore.CAP.SqlServer
{ {
public class CapPublisher : CapPublisherBase, ICallbackPublisher public class SqlServerPublisher : CapPublisherBase, ICallbackPublisher
{ {
private readonly DbContext _dbContext; private readonly DbContext _dbContext;
private readonly SqlServerOptions _options; private readonly SqlServerOptions _options;
private readonly bool _isUsingEF;
public CapPublisher(ILogger<CapPublisher> logger, IDispatcher dispatcher, private SqlConnection _connection;
IServiceProvider provider, SqlServerOptions options)
: base(logger, dispatcher) public SqlServerPublisher(IServiceProvider provider, SqlServerOptions options) : base(provider)
{ {
ServiceProvider = provider;
_options = options; _options = options;
if (_options.DbContextType == null) if (_options.DbContextType == null)
...@@ -31,57 +31,63 @@ namespace DotNetCore.CAP.SqlServer ...@@ -31,57 +31,63 @@ namespace DotNetCore.CAP.SqlServer
return; return;
} }
IsUsingEF = true; _isUsingEF = true;
_dbContext = (DbContext) ServiceProvider.GetService(_options.DbContextType); _dbContext = (DbContext)ServiceProvider.GetService(_options.DbContextType);
} }
public async Task PublishCallbackAsync(CapPublishedMessage message) public async Task PublishCallbackAsync(CapPublishedMessage message)
{ {
using (var conn = new SqlConnection(_options.ConnectionString)) await PublishAsyncInternal(message);
}
protected override Task ExecuteAsync(CapPublishedMessage message, ICapTransaction transaction,
CancellationToken cancel = default(CancellationToken))
{
var dbTrans = transaction.DbTransaction as IDbTransaction;
if (dbTrans == null && transaction.DbTransaction is IDbContextTransaction dbContextTrans)
{ {
var id = await conn.ExecuteScalarAsync<int>(PrepareSql(), message); dbTrans = dbContextTrans.GetDbTransaction();
message.Id = id;
Enqueue(message);
} }
var conn = dbTrans?.Connection;
return conn.ExecuteAsync(PrepareSql(), message, dbTrans);
} }
protected override void PrepareConnectionForEF() protected override object GetDbTransaction()
{ {
DbConnection = _dbContext.Database.GetDbConnection(); if (_isUsingEF)
var dbContextTransaction = _dbContext.Database.CurrentTransaction;
var dbTrans = dbContextTransaction?.GetDbTransaction();
//DbTransaction is dispose in original
if (dbTrans?.Connection == null)
{ {
IsCapOpenedTrans = true; var dbContextTransaction = _dbContext.Database.CurrentTransaction;
dbContextTransaction?.Dispose(); if (dbContextTransaction == null)
dbContextTransaction = _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted); {
dbTrans = dbContextTransaction.GetDbTransaction(); return InitDbConnection();
}
return dbContextTransaction;
} }
DbTransaction = dbTrans; return InitDbConnection();
} }
protected override int Execute(IDbConnection dbConnection, IDbTransaction dbTransaction, #region private methods
CapPublishedMessage message) private string PrepareSql()
{ {
return dbConnection.ExecuteScalar<int>(PrepareSql(), message, dbTransaction); return
$"INSERT INTO {_options.Schema}.[Published] ([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
} }
protected override Task<int> ExecuteAsync(IDbConnection dbConnection, IDbTransaction dbTransaction, private IDbTransaction InitDbConnection()
CapPublishedMessage message)
{ {
return dbConnection.ExecuteScalarAsync<int>(PrepareSql(), message, dbTransaction); _connection = new SqlConnection(_options.ConnectionString);
_connection.Open();
return _connection.BeginTransaction(IsolationLevel.ReadCommitted);
} }
#region private methods #endregion private methods
private string PrepareSql() public void Dispose()
{ {
return _dbContext?.Dispose();
$"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; _connection?.Dispose();
} }
#endregion private methods
} }
} }
\ No newline at end of file
using System.Data;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage;
// ReSharper disable once CheckNamespace
namespace DotNetCore.CAP
{
public class SqlServerCapTransaction : CapTransactionBase
{
public SqlServerCapTransaction(IDispatcher dispatcher) : base(dispatcher) { }
public override void Commit()
{
Debug.Assert(DbTransaction != null);
switch (DbTransaction)
{
case IDbTransaction dbTransaction:
dbTransaction.Commit();
break;
case IDbContextTransaction dbContextTransaction:
dbContextTransaction.Commit();
break;
}
Flush();
}
public override void Rollback()
{
Debug.Assert(DbTransaction != null);
switch (DbTransaction)
{
case IDbTransaction dbTransaction:
dbTransaction.Rollback();
break;
case IDbContextTransaction dbContextTransaction:
dbContextTransaction.Rollback();
break;
}
}
public override void Dispose()
{
(DbTransaction as IDbTransaction)?.Dispose();
}
}
public static class CapTransactionExtensions
{
public static ICapTransaction Begin(this ICapTransaction transaction,
IDbTransaction dbTransaction, bool autoCommit = false)
{
transaction.DbTransaction = dbTransaction;
transaction.AutoCommit = autoCommit;
return transaction;
}
public static ICapTransaction Begin(this ICapTransaction transaction,
IDbContextTransaction dbTransaction, bool autoCommit = false)
{
transaction.DbTransaction = dbTransaction;
transaction.AutoCommit = autoCommit;
return transaction;
}
}
}
...@@ -38,7 +38,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -38,7 +38,7 @@ namespace DotNetCore.CAP.SqlServer
return new SqlServerMonitoringApi(this, _options); return new SqlServerMonitoringApi(this, _options);
} }
public async Task InitializeAsync(CancellationToken cancellationToken) public async Task InitializeAsync(CancellationToken cancellationToken = default(CancellationToken))
{ {
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
{ {
...@@ -64,15 +64,10 @@ BEGIN ...@@ -64,15 +64,10 @@ BEGIN
EXEC('CREATE SCHEMA [{schema}]') EXEC('CREATE SCHEMA [{schema}]')
END; END;
IF OBJECT_ID(N'[{schema}].[Queue]',N'U') IS NOT NULL
BEGIN
DROP TABLE [{schema}].[Queue];
END;
IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL IF OBJECT_ID(N'[{schema}].[Received]',N'U') IS NULL
BEGIN BEGIN
CREATE TABLE [{schema}].[Received]( CREATE TABLE [{schema}].[Received](
[Id] [int] IDENTITY(1,1) NOT NULL, [Id] [bigint] NOT NULL,
[Name] [nvarchar](200) NOT NULL, [Name] [nvarchar](200) NOT NULL,
[Group] [nvarchar](200) NULL, [Group] [nvarchar](200) NULL,
[Content] [nvarchar](max) NULL, [Content] [nvarchar](max) NULL,
...@@ -90,7 +85,7 @@ END; ...@@ -90,7 +85,7 @@ END;
IF OBJECT_ID(N'[{schema}].[Published]',N'U') IS NULL IF OBJECT_ID(N'[{schema}].[Published]',N'U') IS NULL
BEGIN BEGIN
CREATE TABLE [{schema}].[Published]( CREATE TABLE [{schema}].[Published](
[Id] [int] IDENTITY(1,1) NOT NULL, [Id] [bigint] NOT NULL,
[Name] [nvarchar](200) NOT NULL, [Name] [nvarchar](200) NOT NULL,
[Content] [nvarchar](max) NULL, [Content] [nvarchar](max) NULL,
[Retries] [int] NOT NULL, [Retries] [int] NOT NULL,
......
...@@ -28,7 +28,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -28,7 +28,7 @@ namespace DotNetCore.CAP.SqlServer
return new SqlServerStorageTransaction(this); return new SqlServerStorageTransaction(this);
} }
public async Task<CapPublishedMessage> GetPublishedMessageAsync(int id) public async Task<CapPublishedMessage> GetPublishedMessageAsync(long id)
{ {
var sql = $@"SELECT * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Id={id}"; var sql = $@"SELECT * FROM [{Options.Schema}].[Published] WITH (readpast) WHERE Id={id}";
...@@ -50,7 +50,7 @@ namespace DotNetCore.CAP.SqlServer ...@@ -50,7 +50,7 @@ namespace DotNetCore.CAP.SqlServer
} }
} }
public async Task<int> StoreReceivedMessageAsync(CapReceivedMessage message) public void StoreReceivedMessage(CapReceivedMessage message)
{ {
if (message == null) if (message == null)
{ {
...@@ -58,16 +58,16 @@ namespace DotNetCore.CAP.SqlServer ...@@ -58,16 +58,16 @@ namespace DotNetCore.CAP.SqlServer
} }
var sql = $@" var sql = $@"
INSERT INTO [{Options.Schema}].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) INSERT INTO [{Options.Schema}].[Received]([Id],[Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName])
VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOPE_IDENTITY();"; VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
using (var connection = new SqlConnection(Options.ConnectionString)) using (var connection = new SqlConnection(Options.ConnectionString))
{ {
return await connection.ExecuteScalarAsync<int>(sql, message); connection.Execute(sql, message);
} }
} }
public async Task<CapReceivedMessage> GetReceivedMessageAsync(int id) public async Task<CapReceivedMessage> GetReceivedMessageAsync(long id)
{ {
var sql = $@"SELECT * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Id={id}"; var sql = $@"SELECT * FROM [{Options.Schema}].[Received] WITH (readpast) WHERE Id={id}";
using (var connection = new SqlConnection(Options.ConnectionString)) using (var connection = new SqlConnection(Options.ConnectionString))
...@@ -87,7 +87,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP ...@@ -87,7 +87,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP
} }
} }
public bool ChangePublishedState(int messageId, string state) public bool ChangePublishedState(long messageId, string state)
{ {
var sql = var sql =
$"UPDATE [{Options.Schema}].[Published] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}"; $"UPDATE [{Options.Schema}].[Published] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}";
...@@ -98,7 +98,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP ...@@ -98,7 +98,7 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP
} }
} }
public bool ChangeReceivedState(int messageId, string state) public bool ChangeReceivedState(long messageId, string state)
{ {
var sql = var sql =
$"UPDATE [{Options.Schema}].[Received] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}"; $"UPDATE [{Options.Schema}].[Received] SET Retries=Retries+1,ExpiresAt=NULL,StatusName = '{state}' WHERE Id={messageId}";
...@@ -108,9 +108,5 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP ...@@ -108,9 +108,5 @@ VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);SELECT SCOP
return connection.Execute(sql) > 0; return connection.Execute(sql) > 0;
} }
} }
public void Dispose()
{
}
} }
} }
\ No newline at end of file
...@@ -62,29 +62,5 @@ namespace DotNetCore.CAP.SqlServer ...@@ -62,29 +62,5 @@ namespace DotNetCore.CAP.SqlServer
_dbTransaction.Dispose(); _dbTransaction.Dispose();
_dbConnection.Dispose(); _dbConnection.Dispose();
} }
public void EnqueueMessage(CapPublishedMessage message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Publish},
_dbTransaction);
}
public void EnqueueMessage(CapReceivedMessage message)
{
if (message == null)
{
throw new ArgumentNullException(nameof(message));
}
var sql = $"INSERT INTO [{_schema}].[Queue] values(@MessageId,@MessageType);";
_dbConnection.Execute(sql, new CapQueue {MessageId = message.Id, MessageType = MessageType.Subscribe},
_dbTransaction);
}
} }
} }
\ No newline at end of file
using System;
using System.Data; using System.Data;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.Threading;
using Dapper; using Dapper;
using Microsoft.Extensions.Logging;
using Moq;
namespace DotNetCore.CAP.SqlServer.Test namespace DotNetCore.CAP.SqlServer.Test
{ {
public abstract class DatabaseTestHost : TestHost public abstract class DatabaseTestHost:IDisposable
{ {
private static bool _sqlObjectInstalled; protected ILogger<SqlServerStorage> Logger;
public static object _lock = new object(); protected CapOptions CapOptions;
protected SqlServerOptions SqlSeverOptions;
protected override void PostBuildServices() public bool SqlObjectInstalled;
protected DatabaseTestHost()
{ {
base.PostBuildServices(); Logger = new Mock<ILogger<SqlServerStorage>>().Object;
lock (_lock) CapOptions = new Mock<CapOptions>().Object;
{ SqlSeverOptions = new Mock<SqlServerOptions>()
if (!_sqlObjectInstalled) .SetupProperty(x => x.ConnectionString, ConnectionUtil.GetConnectionString())
{ .Object;
InitializeDatabase();
} InitializeDatabase();
}
} }
public override void Dispose() public void Dispose()
{ {
DeleteAllData(); DeleteAllData();
base.Dispose();
} }
private void InitializeDatabase() private void InitializeDatabase()
{
using (CreateScope())
{
var storage = GetService<SqlServerStorage>();
var token = new CancellationTokenSource().Token;
CreateDatabase();
storage.InitializeAsync(token).GetAwaiter().GetResult();
_sqlObjectInstalled = true;
}
}
private void CreateDatabase()
{ {
var masterConn = ConnectionUtil.GetMasterConnectionString(); var masterConn = ConnectionUtil.GetMasterConnectionString();
var databaseName = ConnectionUtil.GetDatabaseName(); var databaseName = ConnectionUtil.GetDatabaseName();
...@@ -50,8 +41,12 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -50,8 +41,12 @@ namespace DotNetCore.CAP.SqlServer.Test
IF NOT EXISTS (SELECT * FROM sysdatabases WHERE name = N'{databaseName}') IF NOT EXISTS (SELECT * FROM sysdatabases WHERE name = N'{databaseName}')
CREATE DATABASE [{databaseName}];"); CREATE DATABASE [{databaseName}];");
} }
new SqlServerStorage(Logger, CapOptions, SqlSeverOptions).InitializeAsync().GetAwaiter().GetResult();
SqlObjectInstalled = true;
} }
private void DeleteAllData() private void DeleteAllData()
{ {
var conn = ConnectionUtil.GetConnectionString(); var conn = ConnectionUtil.GetConnectionString();
......
...@@ -10,30 +10,31 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -10,30 +10,31 @@ namespace DotNetCore.CAP.SqlServer.Test
[Collection("sqlserver")] [Collection("sqlserver")]
public class SqlServerStorageConnectionTest : DatabaseTestHost public class SqlServerStorageConnectionTest : DatabaseTestHost
{ {
private SqlServerStorageConnection _storage; private readonly SqlServerStorageConnection _storage;
public SqlServerStorageConnectionTest() public SqlServerStorageConnectionTest()
{ {
var options = GetService<SqlServerOptions>(); _storage = new SqlServerStorageConnection(SqlSeverOptions, CapOptions);
var capOptions = GetService<CapOptions>();
_storage = new SqlServerStorageConnection(options, capOptions);
} }
[Fact] [Fact]
public async Task GetPublishedMessageAsync_Test() public async Task GetPublishedMessageAsync_Test()
{ {
var sql = "INSERT INTO [Cap].[Published]([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) OUTPUT INSERTED.Id VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);"; var sql = "INSERT INTO [Cap].[Published]([Id],[Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) VALUES(@Id,@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
var insertedId = SnowflakeId.Default().NextId();
var publishMessage = new CapPublishedMessage var publishMessage = new CapPublishedMessage
{ {
Id= insertedId,
Name = "SqlServerStorageConnectionTest", Name = "SqlServerStorageConnectionTest",
Content = "", Content = "",
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
var insertedId = default(int);
using (var connection = ConnectionUtil.CreateConnection()) using (var connection = ConnectionUtil.CreateConnection())
{ {
insertedId = connection.QueryFirst<int>(sql, publishMessage); await connection.ExecuteAsync(sql, publishMessage);
} }
var message = await _storage.GetPublishedMessageAsync(insertedId); var message = await _storage.GetPublishedMessageAsync(insertedId);
Assert.NotNull(message); Assert.NotNull(message);
Assert.Equal("SqlServerStorageConnectionTest", message.Name); Assert.Equal("SqlServerStorageConnectionTest", message.Name);
...@@ -41,7 +42,7 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -41,7 +42,7 @@ namespace DotNetCore.CAP.SqlServer.Test
} }
[Fact] [Fact]
public async Task StoreReceivedMessageAsync_Test() public void StoreReceivedMessageAsync_Test()
{ {
var receivedMessage = new CapReceivedMessage var receivedMessage = new CapReceivedMessage
{ {
...@@ -54,7 +55,7 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -54,7 +55,7 @@ namespace DotNetCore.CAP.SqlServer.Test
Exception exception = null; Exception exception = null;
try try
{ {
await _storage.StoreReceivedMessageAsync(receivedMessage); _storage.StoreReceivedMessage(receivedMessage);
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -66,20 +67,20 @@ namespace DotNetCore.CAP.SqlServer.Test ...@@ -66,20 +67,20 @@ namespace DotNetCore.CAP.SqlServer.Test
[Fact] [Fact]
public async Task GetReceivedMessageAsync_Test() public async Task GetReceivedMessageAsync_Test()
{ {
var sql = $@" var sql = @"INSERT INTO [Cap].[Received]([Id],[Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) VALUES(@Id,@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
INSERT INTO [Cap].[Received]([Name],[Group],[Content],[Retries],[Added],[ExpiresAt],[StatusName]) OUTPUT INSERTED.Id var insertedId = SnowflakeId.Default().NextId();
VALUES(@Name,@Group,@Content,@Retries,@Added,@ExpiresAt,@StatusName);";
var receivedMessage = new CapReceivedMessage var receivedMessage = new CapReceivedMessage
{ {
Id= insertedId,
Name = "SqlServerStorageConnectionTest", Name = "SqlServerStorageConnectionTest",
Content = "", Content = "",
Group = "mygroup", Group = "mygroup",
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
var insertedId = default(int);
using (var connection = ConnectionUtil.CreateConnection()) using (var connection = ConnectionUtil.CreateConnection())
{ {
insertedId = connection.QueryFirst<int>(sql, receivedMessage); await connection.ExecuteAsync(sql, receivedMessage);
} }
var message = await _storage.GetReceivedMessageAsync(insertedId); var message = await _storage.GetReceivedMessageAsync(insertedId);
......
using System;
using Microsoft.Extensions.DependencyInjection;
namespace DotNetCore.CAP.SqlServer.Test
{
public abstract class TestHost : IDisposable
{
protected IServiceCollection _services;
protected string _connectionString;
private IServiceProvider _provider;
private IServiceProvider _scopedProvider;
public TestHost()
{
CreateServiceCollection();
PreBuildServices();
BuildServices();
PostBuildServices();
}
protected IServiceProvider Provider => _scopedProvider ?? _provider;
private void CreateServiceCollection()
{
var services = new ServiceCollection();
services.AddOptions();
services.AddLogging();
_connectionString = ConnectionUtil.GetConnectionString();
services.AddSingleton(new SqlServerOptions { ConnectionString = _connectionString });
services.AddSingleton(new CapOptions());
services.AddSingleton<SqlServerStorage>();
_services = services;
}
protected virtual void PreBuildServices()
{
}
private void BuildServices()
{
_provider = _services.BuildServiceProvider();
}
protected virtual void PostBuildServices()
{
}
public IDisposable CreateScope()
{
var scope = CreateScope(_provider);
var loc = scope.ServiceProvider;
_scopedProvider = loc;
return new DelegateDisposable(() =>
{
if (_scopedProvider == loc)
{
_scopedProvider = null;
}
scope.Dispose();
});
}
public IServiceScope CreateScope(IServiceProvider provider)
{
var scope = provider.GetService<IServiceScopeFactory>().CreateScope();
return scope;
}
public T GetService<T>() => Provider.GetService<T>();
public T Ensure<T>(ref T service)
where T : class
=> service ?? (service = GetService<T>());
public virtual void Dispose()
{
(_provider as IDisposable)?.Dispose();
}
private class DelegateDisposable : IDisposable
{
private Action _dispose;
public DelegateDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
_dispose();
}
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment