Skip to content

Commit 2850f3a

Browse files
committed
Implemented full dependency chain with auto-wiring. Updated example controller with something more realistic
1 parent fd67f53 commit 2850f3a

33 files changed

Lines changed: 454 additions & 48 deletions

src/OpenPerpetuum.Api.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenPerpetuum.Core.Extensio
99
EndProject
1010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenPerpetuum.Core.DataServices", "OpenPerpetuum.Core.DataServices\OpenPerpetuum.Core.DataServices.csproj", "{E11E68A3-3E71-44DB-8536-21813AA9D1A9}"
1111
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenPerpetuum.Core.Foundation", "OpenPerpetuum.Core.Foundation\OpenPerpetuum.Core.Foundation.csproj", "{D86CC47A-88C2-49DB-8B36-ED14471D03A2}"
13+
EndProject
1214
Global
1315
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1416
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
2729
{E11E68A3-3E71-44DB-8536-21813AA9D1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
2830
{E11E68A3-3E71-44DB-8536-21813AA9D1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
2931
{E11E68A3-3E71-44DB-8536-21813AA9D1A9}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{D86CC47A-88C2-49DB-8B36-ED14471D03A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{D86CC47A-88C2-49DB-8B36-ED14471D03A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{D86CC47A-88C2-49DB-8B36-ED14471D03A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{D86CC47A-88C2-49DB-8B36-ED14471D03A2}.Release|Any CPU.Build.0 = Release|Any CPU
3036
EndGlobalSection
3137
GlobalSection(SolutionProperties) = preSolution
3238
HideSolutionNode = FALSE
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using OpenPerpetuum.Core.Foundation.Processing;
3+
4+
namespace OpenPerpetuum.Api.Controllers
5+
{
6+
public class ApiControllerBase : Controller
7+
{
8+
private readonly ICoreContext CoreContext;
9+
10+
protected ICommandProcessor CommandProcessor => CoreContext.CommandProcessor;
11+
protected IQueryProcessor QueryProcessor => CoreContext.QueryProcessor;
12+
protected IGenericContext GenericContext => CoreContext.GenericContext;
13+
protected IIdGeneratorService IdGenerator => CoreContext.IdGenerator;
14+
15+
public ApiControllerBase(ICoreContext coreContext)
16+
{
17+
CoreContext = coreContext;
18+
}
19+
}
20+
}
Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,27 @@
1-
using System;
1+
using Microsoft.AspNetCore.Mvc;
2+
using OpenPerpetuum.Core.Foundation.Processing;
23
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Threading.Tasks;
5-
using Microsoft.AspNetCore.Mvc;
64

75
namespace OpenPerpetuum.Api.Controllers
86
{
9-
[Route("api/[controller]")]
7+
[Route("api/[controller]")]
108
[ApiController]
11-
public class ValuesController : ControllerBase
9+
public class ValuesController : ApiControllerBase
1210
{
13-
// GET api/values
14-
[HttpGet]
15-
public ActionResult<IEnumerable<string>> Get()
16-
{
17-
return new string[] { "value1", "value2" };
18-
}
11+
public ValuesController(ICoreContext coreContext) : base(coreContext)
12+
{ }
1913

20-
// GET api/values/5
14+
// GET api/values
2115
[HttpGet("{id}")]
22-
public ActionResult<string> Get(int id)
16+
public ActionResult<IEnumerable<string>> Get(int id)
2317
{
24-
return "value";
25-
}
18+
if (id < 10)
19+
return BadRequest(new { Message = "Use ID 10 - 100 to generate NotFound. USe ID 101+ to return OK" });
2620

27-
// POST api/values
28-
[HttpPost]
29-
public void Post([FromBody] string value)
30-
{
31-
}
21+
if (id <= 100)
22+
return NotFound();
3223

33-
// PUT api/values/5
34-
[HttpPut("{id}")]
35-
public void Put(int id, [FromBody] string value)
36-
{
37-
}
38-
39-
// DELETE api/values/5
40-
[HttpDelete("{id}")]
41-
public void Delete(int id)
42-
{
24+
return Ok(new string[] { "value1", "value2", GenericContext.CurrentDateTime.ToString("yyyy/MM/dd HH:mm:ss") });
4325
}
4426
}
4527
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using Microsoft.Extensions.DependencyModel;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Collections.ObjectModel;
5+
using System.Linq;
6+
using System.Reflection;
7+
8+
namespace OpenPerpetuum.Api.DependencyInstallers
9+
{
10+
internal sealed class AssemblyLoader
11+
{
12+
private static readonly Lazy<AssemblyLoader> lazy = new Lazy<AssemblyLoader>(() => new AssemblyLoader());
13+
public static AssemblyLoader Instance { get { return lazy.Value; } }
14+
15+
private readonly List<Assembly> runtimeAssemblies = new List<Assembly>();
16+
private AssemblyLoader()
17+
{
18+
var platform = Environment.OSVersion.Platform.ToString();
19+
runtimeAssemblies = DependencyContext.Default.GetRuntimeAssemblyNames(platform).Where(an => an.Name.StartsWith("OpenPerpetuum")).Select(Assembly.Load).ToList();
20+
}
21+
22+
public ReadOnlyCollection<Assembly> RuntimeAssemblies => runtimeAssemblies.AsReadOnly();
23+
24+
}
25+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using OpenPerpetuum.Core.DataServices.Context;
2+
using OpenPerpetuum.Core.DataServices.CQRS;
3+
using OpenPerpetuum.Core.DataServices.Database.Interfaces;
4+
using OpenPerpetuum.Core.Foundation.Processing;
5+
using SimpleInjector;
6+
using System.Collections.Generic;
7+
using System.Reflection;
8+
9+
namespace OpenPerpetuum.Api.DependencyInstallers
10+
{
11+
public static class PerpetuumInstaller
12+
{
13+
public static void RegisterPerpetuumApiTypes(this Container container)
14+
{
15+
IEnumerable<Assembly> asm = AssemblyLoader.Instance.RuntimeAssemblies;
16+
container.Register(typeof(ICommandHandler<>), asm);
17+
// Example of how to add decorators to the handlers via IoC -> container.RegisterDecorator(typeof(ICommandHandler<>), typeof(Extension.DecoratedHandler<>));
18+
container.Register(typeof(IQueryHandler<,>), asm);
19+
20+
// Manually wire the processors. These are being auto-wired to allow for inaccessible class registration
21+
container.Register<ICommandProcessor, BasicCommandProcessor>();
22+
container.Register<IQueryProcessor, BasicQueryProcessor>();
23+
container.Register<IIdGeneratorService, IdGeneratorService>();
24+
container.Register<IGenericContext, GenericContext>();
25+
container.Register<IDataContext, DataContext>();
26+
container.Register<ICoreContext, CoreContext>();
27+
}
28+
}
29+
}

src/OpenPerpetuum.Api/OpenPerpetuum.Api.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
</ItemGroup>
2727

2828
<ItemGroup>
29+
<ProjectReference Include="..\OpenPerpetuum.Core.DataServices\OpenPerpetuum.Core.DataServices.csproj" />
30+
<ProjectReference Include="..\OpenPerpetuum.Core.Foundation\OpenPerpetuum.Core.Foundation.csproj" />
2931
<ProjectReference Include="..\OpenPerpetuum.Core\OpenPerpetuum.Core.Extensions\OpenPerpetuum.Core.Extensions.csproj" />
3032
</ItemGroup>
3133

src/OpenPerpetuum.Api/Program.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.IO;
4-
using System.Linq;
5-
using System.Threading.Tasks;
6-
using Microsoft.AspNetCore;
1+
using Microsoft.AspNetCore;
72
using Microsoft.AspNetCore.Hosting;
8-
using Microsoft.Extensions.Configuration;
9-
using Microsoft.Extensions.Logging;
103

114
namespace OpenPerpetuum.Api
125
{
13-
public class Program
6+
public class Program
147
{
158
public static void Main(string[] args)
169
{

src/OpenPerpetuum.Api/Startup.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using Microsoft.Extensions.Logging;
2020
using Microsoft.Extensions.Logging.EventLog;
2121
using Microsoft.Extensions.Options;
22+
using OpenPerpetuum.Api.DependencyInstallers;
2223
using OpenPerpetuum.Core.Extensions;
2324
using SimpleInjector;
2425
using SimpleInjector.Integration.AspNetCore.Mvc;
@@ -201,10 +202,11 @@ private void InitialiseContainer(IApplicationBuilder app, ILoggerFactory loggerF
201202
container.RegisterInstance<Func<IViewBufferScope>>(() => app.GetRequestService<IViewBufferScope>());
202203
container.RegisterInstance(typeof(IServiceProvider), container); // Self registration; basically enables witchcraft...
203204

204-
// Add Middleware here!
205-
// Note that the order in which you enable them in Configure/ConfigureServices is important!
205+
// Add Middleware here!
206+
// Note that the order in which you enable them in Configure/ConfigureServices is important!
206207

207-
// Add "Other Stuff" here! (I typically use Dependency Installers rather than list all my deps here)
208+
// Add "Other Stuff" here! (I typically use Dependency Installers rather than list all my deps here)
209+
container.RegisterPerpetuumApiTypes();
208210
}
209211

210212
// This should stop the API from returning 302 redirects (attempts to present you a login page) for 401 Unauthorised
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using OpenPerpetuum.Core.Foundation.Processing;
2+
using System;
3+
4+
namespace OpenPerpetuum.Core.DataServices.CQRS
5+
{
6+
public class BasicCommandProcessor : ICommandProcessor
7+
{
8+
private readonly IServiceProvider serviceProvider;
9+
10+
public BasicCommandProcessor(IServiceProvider serviceProvider)
11+
{
12+
this.serviceProvider = serviceProvider;
13+
}
14+
15+
public void Process<TCommand>(TCommand command) where TCommand : ICommand
16+
{
17+
var handlerType = typeof(ICommandHandler<>).MakeGenericType(typeof(TCommand));
18+
19+
dynamic handler = serviceProvider.GetService(handlerType);
20+
21+
if (handler == null)
22+
throw new EntryPointNotFoundException($"Unable to find the requested service \"{handlerType.FullName}\"");
23+
24+
handler.Handle((dynamic)command);
25+
}
26+
}
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using OpenPerpetuum.Core.Foundation.Processing;
2+
using System;
3+
4+
namespace OpenPerpetuum.Core.DataServices.CQRS
5+
{
6+
public class BasicQueryProcessor : IQueryProcessor
7+
{
8+
private readonly IServiceProvider serviceProvider;
9+
10+
public BasicQueryProcessor(IServiceProvider serviceProvider)
11+
{
12+
this.serviceProvider = serviceProvider;
13+
}
14+
15+
public TResult Process<TResult>(IQuery<TResult> query)
16+
{
17+
var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
18+
// I would rather use GetRequiredService but that requires a dependency on the whole MS IoC chain
19+
dynamic handler = serviceProvider.GetService(handlerType);
20+
if (handler == null)
21+
throw new EntryPointNotFoundException($"Unable to find the requested service \"{handlerType.FullName}\"");
22+
23+
return handler.Handle((dynamic)query);
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)