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