Browse Source

ICapPublisher add synchronous publish methods.

master
Savorboard 7 years ago
parent
commit
5842dfd58b
2 changed files with 185 additions and 34 deletions
  1. +134
    -25
      src/DotNetCore.CAP.SqlServer/CapPublisher.cs
  2. +51
    -9
      src/DotNetCore.CAP/ICapPublisher.cs

+ 134
- 25
src/DotNetCore.CAP.SqlServer/CapPublisher.cs View File

@@ -7,11 +7,13 @@ using DotNetCore.CAP.Models;
using DotNetCore.CAP.Processor; using DotNetCore.CAP.Processor;
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 : ICapPublisher public class CapPublisher : ICapPublisher
{ {
private readonly ILogger _logger;
private readonly SqlServerOptions _options; private readonly SqlServerOptions _options;
private readonly DbContext _dbContext; private readonly DbContext _dbContext;


@@ -21,9 +23,12 @@ namespace DotNetCore.CAP.SqlServer


protected IServiceProvider ServiceProvider { get; } protected IServiceProvider ServiceProvider { get; }


public CapPublisher(IServiceProvider provider, SqlServerOptions options)
public CapPublisher(IServiceProvider provider,
ILogger<CapPublisher> logger,
SqlServerOptions options)
{ {
ServiceProvider = provider; ServiceProvider = provider;
_logger = logger;
_options = options; _options = options;


if (_options.DbContextType != null) if (_options.DbContextType != null)
@@ -33,57 +38,152 @@ namespace DotNetCore.CAP.SqlServer
} }
} }


public void Publish(string name, string content)
{
CheckIsUsingEF(name);

PublishCore(name, content);
}

public Task PublishAsync(string name, string content) public Task PublishAsync(string name, string content)
{ {
if (name == null) throw new ArgumentNullException(nameof(name));
if (!IsUsingEF) throw new InvalidOperationException("If you are using the EntityFramework, you need to configure the DbContextType first." +
" otherwise you need to use overloaded method with IDbConnection and IDbTransaction.");
CheckIsUsingEF(name);


return Publish(name, content);
return PublishCoreAsync(name, content);
}

public void Publish<T>(string name, T contentObj)
{
CheckIsUsingEF(name);

var content = Helper.ToJson(contentObj);

PublishCore(name, content);
} }


public Task PublishAsync<T>(string name, T contentObj) public Task PublishAsync<T>(string name, T contentObj)
{ {
if (name == null) throw new ArgumentNullException(nameof(name));
if (!IsUsingEF) throw new InvalidOperationException("If you are using the EntityFramework, you need to configure the DbContextType first." +
" otherwise you need to use overloaded method with IDbConnection and IDbTransaction.");
CheckIsUsingEF(name);


var content = Helper.ToJson(contentObj); var content = Helper.ToJson(contentObj);
return Publish(name, content);

return PublishCoreAsync(name, content);
} }


public Task PublishAsync(string name, string content, IDbConnection dbConnection)
public void Publish(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction = null)
{ {
if (IsUsingEF) throw new InvalidOperationException("If you are using the EntityFramework, you do not need to use this overloaded.");
if (name == null) throw new ArgumentNullException(nameof(name));
if (dbConnection == null) throw new ArgumentNullException(nameof(dbConnection));
CheckIsAdoNet(name);

if (dbConnection == null)
throw new ArgumentNullException(nameof(dbConnection));


var dbTransaction = dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
dbTransaction = dbTransaction ?? dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
IsCapOpenedTrans = true; IsCapOpenedTrans = true;
return PublishWithTrans(name, content, dbConnection, dbTransaction);

PublishWithTrans(name, content, dbConnection, dbTransaction);
} }


public Task PublishAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction)
public Task PublishAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction = null)
{
CheckIsAdoNet(name);

if (dbConnection == null)
throw new ArgumentNullException(nameof(dbConnection));

dbTransaction = dbTransaction ?? dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);
IsCapOpenedTrans = true;

return PublishWithTransAsync(name, content, dbConnection, dbTransaction);
}

public void Publish<T>(string name, T contentObj, IDbConnection dbConnection, IDbTransaction dbTransaction = null)
{
CheckIsAdoNet(name);

if (dbConnection == null)
throw new ArgumentNullException(nameof(dbConnection));

var content = Helper.ToJson(contentObj);

dbTransaction = dbTransaction ?? dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);

PublishWithTrans(name, content, dbConnection, dbTransaction);
}

public Task PublishAsync<T>(string name, T contentObj, IDbConnection dbConnection, IDbTransaction dbTransaction = null)
{
CheckIsAdoNet(name);

if (dbConnection == null)
throw new ArgumentNullException(nameof(dbConnection));

var content = Helper.ToJson(contentObj);

dbTransaction = dbTransaction ?? dbConnection.BeginTransaction(IsolationLevel.ReadCommitted);

return PublishWithTransAsync(name, content, dbConnection, dbTransaction);
}

#region private methods

private void CheckIsUsingEF(string name)
{ {
if (IsUsingEF) throw new InvalidOperationException("If you are using the EntityFramework, you do not need to use this overloaded.");
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));
if (dbConnection == null) throw new ArgumentNullException(nameof(dbConnection));
if (dbTransaction == null) throw new ArgumentNullException(nameof(dbTransaction));
if (!IsUsingEF)
throw new InvalidOperationException("If you are using the EntityFramework, you need to configure the DbContextType first." +
" otherwise you need to use overloaded method with IDbConnection and IDbTransaction.");
}


return PublishWithTrans(name, content, dbConnection, dbTransaction);
private void CheckIsAdoNet(string name)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (IsUsingEF)
throw new InvalidOperationException("If you are using the EntityFramework, you do not need to use this overloaded.");
} }


private async Task Publish(string name, string content)
private async Task PublishCoreAsync(string name, string content)
{ {
var connection = _dbContext.Database.GetDbConnection(); var connection = _dbContext.Database.GetDbConnection();
var transaction = _dbContext.Database.CurrentTransaction; var transaction = _dbContext.Database.CurrentTransaction;
IsCapOpenedTrans = transaction == null; IsCapOpenedTrans = transaction == null;
transaction = transaction ?? await _dbContext.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted); transaction = transaction ?? await _dbContext.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted);
var dbTransaction = transaction.GetDbTransaction(); var dbTransaction = transaction.GetDbTransaction();
await PublishWithTrans(name, content, connection, dbTransaction);
await PublishWithTransAsync(name, content, connection, dbTransaction);
}

private void PublishCore(string name, string content)
{
var connection = _dbContext.Database.GetDbConnection();
var transaction = _dbContext.Database.CurrentTransaction;
IsCapOpenedTrans = transaction == null;
transaction = transaction ?? _dbContext.Database.BeginTransaction(IsolationLevel.ReadCommitted);
var dbTransaction = transaction.GetDbTransaction();
PublishWithTrans(name, content, connection, dbTransaction);
}

private async Task PublishWithTransAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction)
{
var message = new CapPublishedMessage
{
Name = name,
Content = content,
StatusName = StatusName.Scheduled
};
await dbConnection.ExecuteAsync(PrepareSql(), message, transaction: dbTransaction);

_logger.LogInformation("Message has been persisted in the database. name:" + name);

if (IsCapOpenedTrans)
{
dbTransaction.Commit();
dbTransaction.Dispose();
dbConnection.Dispose();
}

PublishQueuer.PulseEvent.Set();
} }


protected virtual async Task PublishWithTrans(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction)
private void PublishWithTrans(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction)
{ {
var message = new CapPublishedMessage var message = new CapPublishedMessage
{ {
@@ -91,15 +191,24 @@ namespace DotNetCore.CAP.SqlServer
Content = content, Content = content,
StatusName = StatusName.Scheduled StatusName = StatusName.Scheduled
}; };
var count = dbConnection.Execute(PrepareSql(), message, transaction: dbTransaction);

_logger.LogInformation("Message has been persisted in the database. name:" + name);


var sql = $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)";
await dbConnection.ExecuteAsync(sql, message, transaction: dbTransaction);
if (IsCapOpenedTrans) if (IsCapOpenedTrans)
{ {
dbTransaction.Commit(); dbTransaction.Commit();
dbTransaction.Dispose();
dbConnection.Dispose(); dbConnection.Dispose();
} }
PublishQueuer.PulseEvent.Set(); PublishQueuer.PulseEvent.Set();
} }

private string PrepareSql()
{
return $"INSERT INTO {_options.Schema}.[Published] ([Name],[Content],[Retries],[Added],[ExpiresAt],[StatusName])VALUES(@Name,@Content,@Retries,@Added,@ExpiresAt,@StatusName)";
}

#endregion private methods
} }
} }

+ 51
- 9
src/DotNetCore.CAP/ICapPublisher.cs View File

@@ -9,7 +9,7 @@ namespace DotNetCore.CAP
public interface ICapPublisher public interface ICapPublisher
{ {
/// <summary> /// <summary>
/// Publish a string message to specified topic.
/// (EntityFramework) Asynchronous publish a message.
/// <para> /// <para>
/// If you are using the EntityFramework, you need to configure the DbContextType first. /// If you are using the EntityFramework, you need to configure the DbContextType first.
/// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction.
@@ -20,7 +20,18 @@ namespace DotNetCore.CAP
Task PublishAsync(string name, string content); Task PublishAsync(string name, string content);


/// <summary> /// <summary>
/// Publis a object message to specified topic.
/// (EntityFramework) Publish a message.
/// <para>
/// If you are using the EntityFramework, you need to configure the DbContextType first.
/// otherwise you need to use overloaded method with IDbConnection and IDbTransaction.
/// </para>
/// </summary>
/// <param name="name">the topic name or exchange router key.</param>
/// <param name="content">message body content.</param>
void Publish(string name, string content);

/// <summary>
/// (EntityFramework) Asynchronous publish a object message.
/// <para> /// <para>
/// If you are using the EntityFramework, you need to configure the DbContextType first. /// If you are using the EntityFramework, you need to configure the DbContextType first.
/// otherwise you need to use overloaded method with IDbConnection and IDbTransaction. /// otherwise you need to use overloaded method with IDbConnection and IDbTransaction.
@@ -28,24 +39,55 @@ namespace DotNetCore.CAP
/// </summary> /// </summary>
/// <typeparam name="T">The type of conetent object.</typeparam> /// <typeparam name="T">The type of conetent object.</typeparam>
/// <param name="name">the topic name or exchange router key.</param> /// <param name="name">the topic name or exchange router key.</param>
/// <param name="contentObj">object instance that will be serialized of json.</param>
/// <param name="contentObj">message body content, that will be serialized of json.</param>
Task PublishAsync<T>(string name, T contentObj); Task PublishAsync<T>(string name, T contentObj);


/// <summary> /// <summary>
/// Publish a string message to specified topic with transacton.
/// (EntityFramework) Publish a object message.
/// <para>
/// If you are using the EntityFramework, you need to configure the DbContextType first.
/// otherwise you need to use overloaded method with IDbConnection and IDbTransaction.
/// </para>
/// </summary> /// </summary>
/// <typeparam name="T">The type of conetent object.</typeparam>
/// <param name="name">the topic name or exchange router key.</param> /// <param name="name">the topic name or exchange router key.</param>
/// <param name="content">message body content.</param>
/// <param name="dbConnection">the dbConnection of <see cref="IDbConnection"/></param>
Task PublishAsync(string name, string content, IDbConnection dbConnection);
/// <param name="contentObj">message body content, that will be serialized of json.</param>
void Publish<T>(string name, T contentObj);


/// <summary> /// <summary>
/// Publish a string message to specified topic with transacton.
/// (ado.net) Asynchronous publish a message.
/// </summary>
/// <param name="name">the topic name or exchange router key.</param>
/// <param name="content">message body content</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param>
Task PublishAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction = null);

/// <summary>
/// (ado.net) Publish a message.
/// </summary> /// </summary>
/// <param name="name">the topic name or exchange router key.</param> /// <param name="name">the topic name or exchange router key.</param>
/// <param name="content">message body content.</param> /// <param name="content">message body content.</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param> /// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param> /// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param>
Task PublishAsync(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction);
void Publish(string name, string content, IDbConnection dbConnection, IDbTransaction dbTransaction = null);

/// <summary>
/// (ado.net) Asynchronous publish a object message.
/// </summary>
/// <param name="name">the topic name or exchange router key.</param>
/// <param name="contentObj">message body content, that will be serialized of json.</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param>
Task PublishAsync<T>(string name, T contentObj, IDbConnection dbConnection, IDbTransaction dbTransaction = null);

/// <summary>
/// (ado.net) Publish a object message.
/// </summary>
/// <param name="name">the topic name or exchange router key.</param>
/// <param name="contentObj">message body content, that will be serialized of json.</param>
/// <param name="dbConnection">the connection of <see cref="IDbConnection"/></param>
/// <param name="dbTransaction">the transaction of <see cref="IDbTransaction"/></param>
void Publish<T>(string name, T contentObj, IDbConnection dbConnection, IDbTransaction dbTransaction = null);
} }
} }

Loading…
Cancel
Save