Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add startupable #16

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<ItemGroup>
<ProjectReference Include="..\Agoda.IoC.Core\Agoda.IoC.Core.csproj" />
<ProjectReference Include="..\Agoda.IoC.AutofacExt\Agoda.IoC.AutofacExt.csproj" />
<ProjectReference Include="..\ProjectsUnderTest\Agoda.IoC.ProjectUnderTest.Valid2\Agoda.IoC.ProjectUnderTest.Valid2.csproj" />
<ProjectReference Include="..\ProjectsUnderTest\Agoda.IoC.ProjectUnderTest.Valid\Agoda.IoC.ProjectUnderTest.Valid.csproj" />
</ItemGroup>

Expand Down
19 changes: 15 additions & 4 deletions src/Agoda.IoC.AutofacExt.UnitTests/AutoWireAssemblyExtTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using Agoda.IoC.ProjectUnderTest.Valid;
using Agoda.IoC.ProjectUnderTest.Valid2;
using Autofac;
using Autofac.Core;
using Autofac.Core.Lifetime;
Expand All @@ -27,13 +28,17 @@ public void SetUp()
_containerBuilder = new ContainerBuilder();
_container = _containerBuilder.AutoWireAssembly(new[]
{
typeof(NoAttribute).Assembly
}, false).Build();
typeof(NoAttribute).Assembly,
typeof(ServiceThatStartsUp).Assembly
}, false).Build()
.UseAgodaIoCStartupable();
_containerBuilderMocked = new ContainerBuilder();
_containerMocked = _containerBuilderMocked.AutoWireAssembly(new[]
{
typeof(NoAttribute).Assembly
}, true).Build();
typeof(NoAttribute).Assembly,
typeof(ServiceThatStartsUp).Assembly
}, true).Build()
.UseAgodaIoCStartupable();
}

[Test]
Expand Down Expand Up @@ -235,5 +240,11 @@ public void LookforAutowire_GenericWithFactory()
&& x.Lifetime is CurrentScopeLifetime)
.ShouldBeTrue();
}

[Test]
public void WhenRegistarteStartupableClass_ShouldRunStartupmethods()
{
_container.Resolve<IServiceThatStartsUp>().Somedata.ShouldBe(1);
}
}
}
26 changes: 25 additions & 1 deletion src/Agoda.IoC.AutofacExt/AutoWireAssemblyExt.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Agoda.IoC.Core;
using Autofac;
using Autofac.Core;

namespace Agoda.IoC.AutofacExt
{
Expand Down Expand Up @@ -87,7 +89,6 @@ public static ContainerBuilder AutoWireAssembly<T>(this ContainerBuilder service
if (toType.IsGenericTypeDefinition)
{
services.RegisterGeneric(toType).As(reg.FromType).SingleInstance();

}
else
{
Expand All @@ -106,5 +107,28 @@ public static ContainerBuilder AutoWireAssembly<T>(this ContainerBuilder service
}
return services;
}

public static IContainer UseAgodaIoCStartupable(this IContainer container)
{
var listOfStartupableServiceRegistrations =
container.ComponentRegistry.Registrations
.Where(x => typeof(IStartupable).IsAssignableFrom(x.Target.Activator.LimitType))
.SelectMany(y => y.Services);

foreach (var StartupableService in listOfStartupableServiceRegistrations)
{
((IStartupable)container.ResolveService(StartupableService)).Start();
}

return container;
}


private static bool ContainsType(IEnumerable<Autofac.Core.Service> services, Type t)
{
return services.Where(y => y is TypedService)
.Any(z => ((TypedService)z).ServiceType == t);
}

}
}
7 changes: 7 additions & 0 deletions src/Agoda.IoC.Core/IStartupable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Agoda.IoC.Core
{
public interface IStartupable
{
void Start();
}
}
4 changes: 2 additions & 2 deletions src/Agoda.IoC.Core/RegistrationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class RegistrationContext

public bool ReplaceServices { get; }
public (bool IsValid, string ErrorMessage) Validation { get; }

public bool IsStartupable { get; private set; }
private readonly IList<Type> _baseTypes;
private readonly Type _originalToType;
private readonly Type _genericArgument;
Expand All @@ -42,7 +42,7 @@ public RegistrationContext(
IsIntercepted = wrapper?.HasInterceptors(this) ?? false;
_genericArgument = attribute.GenericArgument;
_baseTypes = ContainerAttributeUtils.GetBaseTypes(attribute, toType).ToList();

IsStartupable = typeof(IStartupable).IsAssignableFrom(toType);
// we might have to fiddle with the ToType to keep Unity happy, so we keep a copy of the original for error reporting
_originalToType = ToType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Agoda.IoC.Core;
using Agoda.IoC.ProjectUnderTest.Valid;
using Agoda.IoC.ProjectUnderTest.Valid2;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -272,5 +273,12 @@ public void LookforAutowire_ReplaceServiceChecks()
svr = _notReplaceContainer.BuildServiceProvider().GetRequiredService<IReplaceService>();
svr.DoWork.ShouldBe(nameof(ReplaceServiceOneWork));
}
[Test]
public void WhenRegistarteStartupableClass_ShouldRunStartupmethods()
{
var app = _container.BuildServiceProvider();
app.UseAgodaIoCStartupable();
app.GetService<IServiceThatStartsUp>().Somedata.ShouldBe(1);
}
}
}
22 changes: 22 additions & 0 deletions src/Agoda.IoC.NetCore/NetCoreComponentResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using Agoda.IoC.Core;
using Microsoft.Extensions.DependencyInjection;

namespace Agoda.IoC.NetCore
{
public class NetCoreComponentResolver : IComponentResolver
{
private readonly IServiceProvider _serviceProvider;

public NetCoreComponentResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public T Resolve<T>()
{
return _serviceProvider.GetService<T>();

}
}
}
36 changes: 20 additions & 16 deletions src/Agoda.IoC.NetCore/StartupExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Agoda.IoC.NetCore
{
Expand All @@ -25,6 +26,18 @@ public static IServiceCollection AutoWireAssembly(
return rtn;
}

public static IServiceProvider UseAgodaIoCStartupable(this IServiceProvider app)
{
foreach (var startupable in app.GetServices<IStartupable>())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Not sure if GetServices returns null or an empty list.

{
if (startupable == null)
throw new Exception(
"No service found that inherit from IStartupable but UseAgodaIoCStartupable was called");
startupable.Start();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe for a future iteration however it would be nice if you could specify the degree of parallelism.

}
return app;
}

public static IServiceCollection AutoWireAssembly<T>(
this IServiceCollection services, Assembly[] assemblies,
ServiceLifetime serviceLifetime,
Expand Down Expand Up @@ -77,6 +90,13 @@ public static IServiceCollection AutoWireAssembly<T>(
{
services.Add(serviceDescriptor);
}

if (reg.IsStartupable)
{
services.Add(new ServiceDescriptor(typeof(IStartupable),
x => x.GetService(reg.FromType),
ServiceLifetime.Singleton));
}
}
return services;
}
Expand Down Expand Up @@ -167,20 +187,4 @@ private static bool Validate(List<RegistrationContext> registrations, ContainerR
return isValid;
}
}

public class NetCoreComponentResolver : IComponentResolver
{
private readonly IServiceProvider _serviceProvider;

public NetCoreComponentResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public T Resolve<T>()
{
return _serviceProvider.GetService<T>();

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,23 @@ public class ReplaceServiceTwoWork : IReplaceService

public string DoWork { get; set; } = nameof(ReplaceServiceTwoWork);
}

public interface IServiceThatStartsUp
{
int Somedata { get; set; }
}

[RegisterSingleton(For = typeof(IServiceThatStartsUp))]
public class ServiceThatStartsUp : IServiceThatStartsUp, IStartupable
{
public ServiceThatStartsUp()
{
Somedata = 0;
}
public int Somedata { get; set; }
public void Start()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, it would be nice if the startup process could run asynchronously. I would expect that most of the implementation will communicate with other services etc. therefore it might be better to start returning a Task from the start.

{
Somedata++;
}
}
}