Move using statements to globalusing file
This commit is contained in:
parent
5fd90e8cb6
commit
9d73669d86
@ -1,10 +1,10 @@
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||
|
||||
public enum EventStateEnum
|
||||
{
|
||||
public enum EventStateEnum
|
||||
{
|
||||
NotPublished = 0,
|
||||
InProgress = 1,
|
||||
Published = 2,
|
||||
PublishedFailed = 3
|
||||
}
|
||||
NotPublished = 0,
|
||||
InProgress = 1,
|
||||
Published = 2,
|
||||
PublishedFailed = 3
|
||||
}
|
||||
|
||||
|
@ -1,45 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
|
||||
public class IntegrationEventLogContext : DbContext
|
||||
{
|
||||
public class IntegrationEventLogContext : DbContext
|
||||
public IntegrationEventLogContext(DbContextOptions<IntegrationEventLogContext> options) : base(options)
|
||||
{
|
||||
public IntegrationEventLogContext(DbContextOptions<IntegrationEventLogContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public DbSet<IntegrationEventLogEntry> IntegrationEventLogs { get; set; }
|
||||
public DbSet<IntegrationEventLogEntry> IntegrationEventLogs { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<IntegrationEventLogEntry>(ConfigureIntegrationEventLogEntry);
|
||||
}
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<IntegrationEventLogEntry>(ConfigureIntegrationEventLogEntry);
|
||||
}
|
||||
|
||||
void ConfigureIntegrationEventLogEntry(EntityTypeBuilder<IntegrationEventLogEntry> builder)
|
||||
{
|
||||
builder.ToTable("IntegrationEventLog");
|
||||
void ConfigureIntegrationEventLogEntry(EntityTypeBuilder<IntegrationEventLogEntry> builder)
|
||||
{
|
||||
builder.ToTable("IntegrationEventLog");
|
||||
|
||||
builder.HasKey(e => e.EventId);
|
||||
builder.HasKey(e => e.EventId);
|
||||
|
||||
builder.Property(e => e.EventId)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.EventId)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.Content)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.Content)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.CreationTime)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.CreationTime)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.State)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.State)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.TimesSent)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.TimesSent)
|
||||
.IsRequired();
|
||||
|
||||
builder.Property(e => e.EventTypeName)
|
||||
.IsRequired();
|
||||
builder.Property(e => e.EventTypeName)
|
||||
.IsRequired();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,36 @@
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF
|
||||
public class IntegrationEventLogEntry
|
||||
{
|
||||
public class IntegrationEventLogEntry
|
||||
private IntegrationEventLogEntry() { }
|
||||
public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId)
|
||||
{
|
||||
private IntegrationEventLogEntry() { }
|
||||
public IntegrationEventLogEntry(IntegrationEvent @event, Guid transactionId)
|
||||
EventId = @event.Id;
|
||||
CreationTime = @event.CreationDate;
|
||||
EventTypeName = @event.GetType().FullName;
|
||||
Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions
|
||||
{
|
||||
EventId = @event.Id;
|
||||
CreationTime = @event.CreationDate;
|
||||
EventTypeName = @event.GetType().FullName;
|
||||
Content = JsonSerializer.Serialize(@event, @event.GetType(), new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
State = EventStateEnum.NotPublished;
|
||||
TimesSent = 0;
|
||||
TransactionId = transactionId.ToString();
|
||||
}
|
||||
public Guid EventId { get; private set; }
|
||||
public string EventTypeName { get; private set; }
|
||||
[NotMapped]
|
||||
public string EventTypeShortName => EventTypeName.Split('.')?.Last();
|
||||
[NotMapped]
|
||||
public IntegrationEvent IntegrationEvent { get; private set; }
|
||||
public EventStateEnum State { get; set; }
|
||||
public int TimesSent { get; set; }
|
||||
public DateTime CreationTime { get; private set; }
|
||||
public string Content { get; private set; }
|
||||
public string TransactionId { get; private set; }
|
||||
WriteIndented = true
|
||||
});
|
||||
State = EventStateEnum.NotPublished;
|
||||
TimesSent = 0;
|
||||
TransactionId = transactionId.ToString();
|
||||
}
|
||||
public Guid EventId { get; private set; }
|
||||
public string EventTypeName { get; private set; }
|
||||
[NotMapped]
|
||||
public string EventTypeShortName => EventTypeName.Split('.')?.Last();
|
||||
[NotMapped]
|
||||
public IntegrationEvent IntegrationEvent { get; private set; }
|
||||
public EventStateEnum State { get; set; }
|
||||
public int TimesSent { get; set; }
|
||||
public DateTime CreationTime { get; private set; }
|
||||
public string Content { get; private set; }
|
||||
public string TransactionId { get; private set; }
|
||||
|
||||
public IntegrationEventLogEntry DeserializeJsonContent(Type type)
|
||||
{
|
||||
IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent;
|
||||
return this;
|
||||
}
|
||||
public IntegrationEventLogEntry DeserializeJsonContent(Type type)
|
||||
{
|
||||
IntegrationEvent = JsonSerializer.Deserialize(Content, type, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }) as IntegrationEvent;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,10 @@
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services
|
||||
public interface IIntegrationEventLogService
|
||||
{
|
||||
public interface IIntegrationEventLogService
|
||||
{
|
||||
Task<IEnumerable<IntegrationEventLogEntry>> RetrieveEventLogsPendingToPublishAsync(Guid transactionId);
|
||||
Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction);
|
||||
Task MarkEventAsPublishedAsync(Guid eventId);
|
||||
Task MarkEventAsInProgressAsync(Guid eventId);
|
||||
Task MarkEventAsFailedAsync(Guid eventId);
|
||||
}
|
||||
Task<IEnumerable<IntegrationEventLogEntry>> RetrieveEventLogsPendingToPublishAsync(Guid transactionId);
|
||||
Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction);
|
||||
Task MarkEventAsPublishedAsync(Guid eventId);
|
||||
Task MarkEventAsInProgressAsync(Guid eventId);
|
||||
Task MarkEventAsFailedAsync(Guid eventId);
|
||||
}
|
||||
|
@ -1,110 +1,99 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using Microsoft.eShopOnContainers.BuildingBlocks.EventBus.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Services
|
||||
public class IntegrationEventLogService : IIntegrationEventLogService, IDisposable
|
||||
{
|
||||
public class IntegrationEventLogService : IIntegrationEventLogService, IDisposable
|
||||
private readonly IntegrationEventLogContext _integrationEventLogContext;
|
||||
private readonly DbConnection _dbConnection;
|
||||
private readonly List<Type> _eventTypes;
|
||||
private volatile bool disposedValue;
|
||||
|
||||
public IntegrationEventLogService(DbConnection dbConnection)
|
||||
{
|
||||
private readonly IntegrationEventLogContext _integrationEventLogContext;
|
||||
private readonly DbConnection _dbConnection;
|
||||
private readonly List<Type> _eventTypes;
|
||||
private volatile bool disposedValue;
|
||||
_dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection));
|
||||
_integrationEventLogContext = new IntegrationEventLogContext(
|
||||
new DbContextOptionsBuilder<IntegrationEventLogContext>()
|
||||
.UseSqlServer(_dbConnection)
|
||||
.Options);
|
||||
|
||||
public IntegrationEventLogService(DbConnection dbConnection)
|
||||
_eventTypes = Assembly.Load(Assembly.GetEntryAssembly().FullName)
|
||||
.GetTypes()
|
||||
.Where(t => t.Name.EndsWith(nameof(IntegrationEvent)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IntegrationEventLogEntry>> RetrieveEventLogsPendingToPublishAsync(Guid transactionId)
|
||||
{
|
||||
var tid = transactionId.ToString();
|
||||
|
||||
var result = await _integrationEventLogContext.IntegrationEventLogs
|
||||
.Where(e => e.TransactionId == tid && e.State == EventStateEnum.NotPublished).ToListAsync();
|
||||
|
||||
if (result != null && result.Any())
|
||||
{
|
||||
_dbConnection = dbConnection ?? throw new ArgumentNullException(nameof(dbConnection));
|
||||
_integrationEventLogContext = new IntegrationEventLogContext(
|
||||
new DbContextOptionsBuilder<IntegrationEventLogContext>()
|
||||
.UseSqlServer(_dbConnection)
|
||||
.Options);
|
||||
|
||||
_eventTypes = Assembly.Load(Assembly.GetEntryAssembly().FullName)
|
||||
.GetTypes()
|
||||
.Where(t => t.Name.EndsWith(nameof(IntegrationEvent)))
|
||||
.ToList();
|
||||
return result.OrderBy(o => o.CreationTime)
|
||||
.Select(e => e.DeserializeJsonContent(_eventTypes.Find(t => t.Name == e.EventTypeShortName)));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IntegrationEventLogEntry>> RetrieveEventLogsPendingToPublishAsync(Guid transactionId)
|
||||
return new List<IntegrationEventLogEntry>();
|
||||
}
|
||||
|
||||
public Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction)
|
||||
{
|
||||
if (transaction == null) throw new ArgumentNullException(nameof(transaction));
|
||||
|
||||
var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId);
|
||||
|
||||
_integrationEventLogContext.Database.UseTransaction(transaction.GetDbTransaction());
|
||||
_integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry);
|
||||
|
||||
return _integrationEventLogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task MarkEventAsPublishedAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.Published);
|
||||
}
|
||||
|
||||
public Task MarkEventAsInProgressAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.InProgress);
|
||||
}
|
||||
|
||||
public Task MarkEventAsFailedAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.PublishedFailed);
|
||||
}
|
||||
|
||||
private Task UpdateEventStatus(Guid eventId, EventStateEnum status)
|
||||
{
|
||||
var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == eventId);
|
||||
eventLogEntry.State = status;
|
||||
|
||||
if (status == EventStateEnum.InProgress)
|
||||
eventLogEntry.TimesSent++;
|
||||
|
||||
_integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry);
|
||||
|
||||
return _integrationEventLogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
var tid = transactionId.ToString();
|
||||
|
||||
var result = await _integrationEventLogContext.IntegrationEventLogs
|
||||
.Where(e => e.TransactionId == tid && e.State == EventStateEnum.NotPublished).ToListAsync();
|
||||
|
||||
if (result != null && result.Any())
|
||||
if (disposing)
|
||||
{
|
||||
return result.OrderBy(o => o.CreationTime)
|
||||
.Select(e => e.DeserializeJsonContent(_eventTypes.Find(t => t.Name == e.EventTypeShortName)));
|
||||
_integrationEventLogContext?.Dispose();
|
||||
}
|
||||
|
||||
return new List<IntegrationEventLogEntry>();
|
||||
}
|
||||
|
||||
public Task SaveEventAsync(IntegrationEvent @event, IDbContextTransaction transaction)
|
||||
{
|
||||
if (transaction == null) throw new ArgumentNullException(nameof(transaction));
|
||||
|
||||
var eventLogEntry = new IntegrationEventLogEntry(@event, transaction.TransactionId);
|
||||
|
||||
_integrationEventLogContext.Database.UseTransaction(transaction.GetDbTransaction());
|
||||
_integrationEventLogContext.IntegrationEventLogs.Add(eventLogEntry);
|
||||
|
||||
return _integrationEventLogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task MarkEventAsPublishedAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.Published);
|
||||
}
|
||||
|
||||
public Task MarkEventAsInProgressAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.InProgress);
|
||||
}
|
||||
|
||||
public Task MarkEventAsFailedAsync(Guid eventId)
|
||||
{
|
||||
return UpdateEventStatus(eventId, EventStateEnum.PublishedFailed);
|
||||
}
|
||||
|
||||
private Task UpdateEventStatus(Guid eventId, EventStateEnum status)
|
||||
{
|
||||
var eventLogEntry = _integrationEventLogContext.IntegrationEventLogs.Single(ie => ie.EventId == eventId);
|
||||
eventLogEntry.State = status;
|
||||
|
||||
if (status == EventStateEnum.InProgress)
|
||||
eventLogEntry.TimesSent++;
|
||||
|
||||
_integrationEventLogContext.IntegrationEventLogs.Update(eventLogEntry);
|
||||
|
||||
return _integrationEventLogContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_integrationEventLogContext?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.BuildingBlocks.IntegrationEventLogEF.Utilities
|
||||
public class ResilientTransaction
|
||||
{
|
||||
public class ResilientTransaction
|
||||
private DbContext _context;
|
||||
private ResilientTransaction(DbContext context) =>
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
|
||||
public static ResilientTransaction New(DbContext context) =>
|
||||
new ResilientTransaction(context);
|
||||
|
||||
public async Task ExecuteAsync(Func<Task> action)
|
||||
{
|
||||
private DbContext _context;
|
||||
private ResilientTransaction(DbContext context) =>
|
||||
_context = context ?? throw new ArgumentNullException(nameof(context));
|
||||
|
||||
public static ResilientTransaction New(DbContext context) =>
|
||||
new ResilientTransaction(context);
|
||||
|
||||
public async Task ExecuteAsync(Func<Task> action)
|
||||
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||
var strategy = _context.Database.CreateExecutionStrategy();
|
||||
await strategy.ExecuteAsync(async () =>
|
||||
{
|
||||
//Use of an EF Core resiliency strategy when using multiple DbContexts within an explicit BeginTransaction():
|
||||
//See: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency
|
||||
var strategy = _context.Database.CreateExecutionStrategy();
|
||||
await strategy.ExecuteAsync(async () =>
|
||||
using (var transaction = _context.Database.BeginTransaction())
|
||||
{
|
||||
using (var transaction = _context.Database.BeginTransaction())
|
||||
{
|
||||
await action();
|
||||
transaction.Commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
await action();
|
||||
transaction.Commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user