Skip to content

Commit 01f3d04

Browse files
Merge pull request #9 from StuartFerguson/task/#8_multipersistence
Add in multi persistence support to shared
2 parents b6d6bce + dacf1c2 commit 01f3d04

16 files changed

Lines changed: 922 additions & 0 deletions
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace Shared.EventStore.EventStore
2+
{
3+
using System;
4+
using DomainDrivenDesign.EventStore;
5+
6+
public class AggregateRepositoryManager : IAggregateRepositoryManager
7+
{
8+
#region Fields
9+
10+
/// <summary>
11+
/// The event store context manager
12+
/// </summary>
13+
private readonly IEventStoreContextManager EventStoreContextManager;
14+
15+
#endregion
16+
17+
#region Constructors
18+
19+
/// <summary>
20+
/// Initializes a new instance of the <see cref="AggregateRepositoryManager" /> class.
21+
/// </summary>
22+
/// <param name="eventStoreContextManager">The event store context manager.</param>
23+
public AggregateRepositoryManager(IEventStoreContextManager eventStoreContextManager)
24+
{
25+
this.EventStoreContextManager = eventStoreContextManager;
26+
}
27+
28+
#endregion
29+
30+
#region Methods
31+
32+
/// <summary>
33+
/// Gets the aggregate repository.
34+
/// </summary>
35+
/// <typeparam name="T"></typeparam>
36+
/// <param name="identifier">The identifier.</param>
37+
/// <returns></returns>
38+
public IAggregateRepository<T> GetAggregateRepository<T>(Guid identifier) where T : Aggregate, new()
39+
{
40+
IEventStoreContext context = this.EventStoreContextManager.GetEventStoreContext(identifier.ToString());
41+
42+
return new AggregateRepository<T>(context);
43+
}
44+
45+
#endregion
46+
}
47+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Shared.EventStore.EventStore
6+
{
7+
using DomainDrivenDesign.EventStore;
8+
9+
public interface IAggregateRepositoryManager
10+
{
11+
#region Methods
12+
13+
/// <summary>
14+
/// Gets the aggregate repository.
15+
/// </summary>
16+
/// <typeparam name="T"></typeparam>
17+
/// <param name="identifier">The identifier.</param>
18+
/// <returns></returns>
19+
IAggregateRepository<T> GetAggregateRepository<T>(Guid identifier) where T : Aggregate, new();
20+
21+
#endregion
22+
}
23+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace Shared.EventStore.EventStore
6+
{
7+
using System.Diagnostics;
8+
using System.Threading;
9+
using DomainDrivenDesign.EventStore;
10+
using Microsoft.Extensions.Logging;
11+
using Shared.Repositories;
12+
13+
public interface IEventStoreContextManager
14+
{
15+
IEventStoreContext GetEventStoreContext(String connectionIdentifier);
16+
}
17+
18+
public class EventStoreContextManager : IEventStoreContextManager
19+
{
20+
#region Fields
21+
22+
/// <summary>
23+
/// The event store context function
24+
/// </summary>
25+
private readonly Func<String, IEventStoreContext> EventStoreContextFunc;
26+
27+
private readonly IConnectionStringConfigurationRepository ConnectionStringConfigurationRepository;
28+
29+
/// <summary>
30+
/// The event store contexts
31+
/// </summary>
32+
private readonly Dictionary<String, IEventStoreContext> EventStoreContexts;
33+
34+
/// <summary>
35+
/// The context
36+
/// </summary>
37+
private readonly IEventStoreContext Context;
38+
39+
//TODO static?
40+
/// <summary>
41+
/// The padlock
42+
/// </summary>
43+
private readonly Object padlock = new Object();
44+
45+
/// <summary>
46+
/// Occurs when [trace generated].
47+
/// </summary>
48+
public event TraceHandler TraceGenerated;
49+
50+
#endregion
51+
52+
#region Constructors
53+
54+
/// <summary>
55+
/// Initializes a new instance of the <see cref="EventStoreContextManager" /> class.
56+
/// </summary>
57+
/// <param name="eventStoreContextFunc">The event store context function.</param>
58+
public EventStoreContextManager(Func<String, IEventStoreContext> eventStoreContextFunc,
59+
IConnectionStringConfigurationRepository connectionStringConfigurationRepository)
60+
{
61+
this.EventStoreContexts = new Dictionary<String, IEventStoreContext>();
62+
this.EventStoreContextFunc = eventStoreContextFunc;
63+
this.ConnectionStringConfigurationRepository = connectionStringConfigurationRepository;
64+
}
65+
66+
/// <summary>
67+
/// Initializes a new instance of the <see cref="EventStoreContextManager"/> class.
68+
/// </summary>
69+
/// <param name="eventStoreContext">The event store context.</param>
70+
public EventStoreContextManager(IEventStoreContext eventStoreContext)
71+
{
72+
this.Context = eventStoreContext;
73+
}
74+
75+
#endregion
76+
77+
#region Methods
78+
79+
public IEventStoreContext GetEventStoreContext(String connectionIdentifier)
80+
{
81+
if (this.Context != null)
82+
{
83+
return this.Context;
84+
}
85+
86+
this.WriteTrace($"No resolved context found, about to resolve one using connectionIdentifier {connectionIdentifier}");
87+
88+
if (this.EventStoreContexts.ContainsKey(connectionIdentifier))
89+
{
90+
return this.EventStoreContexts[connectionIdentifier.ToString()];
91+
}
92+
93+
this.WriteTrace($"Creating a new EventStoreContext for connectionIdentifier {connectionIdentifier}");
94+
95+
lock (this.padlock)
96+
{
97+
if (!this.EventStoreContexts.ContainsKey(connectionIdentifier))
98+
{
99+
// This will need to now look up the ES Connection string from persistence
100+
String connectionString = this.ConnectionStringConfigurationRepository.GetConnectionString(connectionIdentifier, ConnectionStringType.EventStore, CancellationToken.None).Result;
101+
102+
//this.WriteTrace($"Connection String is {connectionString}");
103+
104+
//IEventStoreContext eventStoreContext = this.EventStoreContextFunc(connectionString);
105+
106+
//this.EventStoreContexts.Add(connectionStringId, eventStoreContext);
107+
}
108+
109+
return this.EventStoreContexts[connectionIdentifier];
110+
}
111+
}
112+
113+
114+
/// <summary>
115+
/// Writes the trace.
116+
/// </summary>
117+
/// <param name="trace">The trace.</param>
118+
private void WriteTrace(String trace)
119+
{
120+
if (this.TraceGenerated != null)
121+
{
122+
this.TraceGenerated(trace, LogLevel.Information);
123+
}
124+
}
125+
126+
private void GuardAgainstNoConnectionIdentifier(String connectionIdentifier)
127+
{
128+
//Check if the connectionStringIdentifier is present
129+
if (String.IsNullOrEmpty(connectionIdentifier))
130+
{
131+
throw new ArgumentException("Value cannot be empty.", nameof(connectionIdentifier));
132+
}
133+
}
134+
135+
#endregion
136+
}
137+
}

Shared.EventStore/Shared.EventStore.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
<ItemGroup>
1414
<ProjectReference Include="..\Shared.DomainDrivenDesign\Shared.DomainDrivenDesign.csproj" />
15+
<ProjectReference Include="..\Shared\Shared.csproj" />
1516
</ItemGroup>
1617

1718
</Project>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations;
4+
using System.Text;
5+
6+
namespace Shared.EntityFramework.ConnectionStringConfiguration
7+
{
8+
using System.ComponentModel.DataAnnotations.Schema;
9+
10+
public class ConnectionStringConfiguration
11+
{
12+
/// <summary>
13+
/// Gets or sets the identifier.
14+
/// </summary>
15+
/// <value>
16+
/// The identifier.
17+
/// </value>
18+
[Key]
19+
public Guid Id { get; set; }
20+
21+
/// <summary>
22+
/// Gets or sets the connection string identifier.
23+
/// </summary>
24+
/// <value>
25+
/// The connection string identifier.
26+
/// </value>
27+
[Required]
28+
[Column("externalIdentifier")]
29+
public String ExternalIdentifier { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets the connection string type identifier.
33+
/// </summary>
34+
/// <value>
35+
/// The connection string type identifier.
36+
/// </value>
37+
public Int32 ConnectionStringTypeId { get; set; }
38+
39+
/// <summary>
40+
/// Gets or sets the type of the connection string.
41+
/// </summary>
42+
/// <value>
43+
/// The type of the connection string.
44+
/// </value>
45+
[ForeignKey(nameof(ConnectionStringTypeId))]
46+
public virtual ConnectionStringType ConnectionStringType { get; set; }
47+
48+
49+
/// <summary>
50+
/// Gets or sets the connection string.
51+
/// </summary>
52+
/// <value>
53+
/// The connection string.
54+
/// </value>
55+
[Required]
56+
[Column("connectionString")]
57+
public String ConnectionString { get; set; }
58+
}
59+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace Shared.EntityFramework.ConnectionStringConfiguration
7+
{
8+
public class ConnectionStringConfigurationContext : DbContext
9+
{
10+
#region Fields
11+
12+
/// <summary>
13+
/// The connection string
14+
/// </summary>
15+
private readonly String ConnectionString;
16+
17+
#endregion
18+
19+
#region Constructors
20+
21+
/// <summary>
22+
/// Initializes a new instance of the <see cref="ConnectionStringConfigurationContext" /> class using the connection string called ReadModelContext in the app.config file.
23+
/// </summary>
24+
public ConnectionStringConfigurationContext()
25+
{
26+
// Paramaterless constructor required for migrations.
27+
}
28+
29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="ConnectionStringConfigurationContext" /> class using the connection string passed in.
31+
/// </summary>
32+
/// <param name="connectionString">The connection string.</param>
33+
public ConnectionStringConfigurationContext(String connectionString)
34+
{
35+
this.ConnectionString = connectionString;
36+
}
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="ConnectionStringConfigurationContext"/> class.
40+
/// </summary>
41+
/// <param name="dbContextOptions">The database context options.</param>
42+
public ConnectionStringConfigurationContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
43+
{
44+
}
45+
46+
#endregion
47+
48+
/// <summary>
49+
/// Gets or sets the connection string configuration.
50+
/// </summary>
51+
/// <value>
52+
/// The connection string configuration.
53+
/// </value>
54+
public DbSet<ConnectionStringConfiguration> ConnectionStringConfiguration { get; set; }
55+
56+
/// <summary>
57+
/// Gets or sets the type of the connection string.
58+
/// </summary>
59+
/// <value>
60+
/// The type of the connection string.
61+
/// </value>
62+
public DbSet<ConnectionStringType> ConnectionStringType { get; set; }
63+
64+
/// <summary>
65+
/// <para>
66+
/// Override this method to configure the database (and other options) to be used for this context.
67+
/// This method is called for each instance of the context that is created.
68+
/// </para>
69+
/// <para>
70+
/// In situations where an instance of <see cref="T:Microsoft.Data.Entity.Infrastructure.DbContextOptions" /> may or may not have been passed
71+
/// to the constructor, you can use <see cref="P:Microsoft.Data.Entity.DbContextOptionsBuilder.IsConfigured" /> to determine if
72+
/// the options have already been set, and skip some or all of the logic in
73+
/// <see cref="M:Microsoft.Data.Entity.DbContext.OnConfiguring(Microsoft.Data.Entity.DbContextOptionsBuilder)" />.
74+
/// </para>
75+
/// </summary>
76+
/// <param name="optionsBuilder">A builder used to create or modify options for this context. Databases (and other extensions)
77+
/// typically define extension methods on this object that allow you to configure the context.</param>
78+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
79+
{
80+
if (!string.IsNullOrWhiteSpace(this.ConnectionString))
81+
{
82+
optionsBuilder.UseSqlServer(this.ConnectionString);
83+
}
84+
85+
base.OnConfiguring(optionsBuilder);
86+
}
87+
88+
/// <summary>
89+
/// Override this method to further configure the model that was discovered by convention from the entity types
90+
/// exposed in <see cref="T:Microsoft.EntityFrameworkCore.DbSet`1" /> properties on your derived context. The resulting model may be cached
91+
/// and re-used for subsequent instances of your derived context.
92+
/// </summary>
93+
/// <param name="modelBuilder">The builder being used to construct the model for this context. Databases (and other extensions) typically
94+
/// define extension methods on this object that allow you to configure aspects of the model that are specific
95+
/// to a given database.</param>
96+
/// <remarks>
97+
/// If a model is explicitly set on the options for this context (via <see cref="M:Microsoft.EntityFrameworkCore.DbContextOptionsBuilder.UseModel(Microsoft.EntityFrameworkCore.Metadata.IModel)" />)
98+
/// then this method will not be run.
99+
/// </remarks>
100+
protected override void OnModelCreating(ModelBuilder modelBuilder)
101+
{
102+
modelBuilder.Entity<ConnectionStringConfiguration>().HasIndex(c => new
103+
{
104+
c.ExternalIdentifier,
105+
c.ConnectionStringTypeId
106+
}).IsUnique();
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)