Skip to content

Commit f580637

Browse files
committed
@wip #679 basic RegisterAttribute and its basic registration implementation
1 parent 0b70866 commit f580637

File tree

4 files changed

+197
-32
lines changed

4 files changed

+197
-32
lines changed

src/DryIoc/Container.cs

+153-30
Original file line numberDiff line numberDiff line change
@@ -9435,6 +9435,62 @@ public static void RegisterMapping<TService, TRegisteredService>(this IContainer
94359435
IfAlreadyRegistered ifAlreadyRegistered, object serviceKey = null, object registeredServiceKey = null) =>
94369436
Registrator.RegisterMapping(container,
94379437
typeof(TService), typeof(TRegisteredService), ifAlreadyRegistered, serviceKey, registeredServiceKey);
9438+
9439+
/// <summary>Returns the number of the actual registrations made.</summary>
9440+
public static int RegisterByRegisterAttributes(this IRegistrator registrator, Type registrationAttributesTarget)
9441+
{
9442+
var attrs = registrationAttributesTarget.GetCustomAttributes<RegisterAttribute>(inherit: true);
9443+
var regCount = 0;
9444+
foreach (var attr in attrs)
9445+
{
9446+
var factory = CreateFactory(attr);
9447+
9448+
registrator.Register(factory, attr.ServiceType, attr.ServiceKey, attr.IfAlreadyRegistered,
9449+
isStaticallyChecked: attr.TypesAreStaticallyChecked);
9450+
9451+
++regCount;
9452+
}
9453+
return regCount;
9454+
}
9455+
9456+
internal static ReflectionFactory CreateFactory(RegisterAttribute attr)
9457+
{
9458+
var reuse = GetReuse(attr);
9459+
var made = Made.Default; // todo: @wip add features later
9460+
var setup = GetSetup(attr);
9461+
return ReflectionFactory.Of(attr.ImplementationType, reuse, made, setup);
9462+
}
9463+
9464+
internal static IReuse GetReuse(RegisterAttribute attr) =>
9465+
attr.ReuseType switch
9466+
{
9467+
var t when t == typeof(TransientReuse) => Reuse.Transient,
9468+
var t when t == typeof(SingletonReuse) => Reuse.Singleton,
9469+
var t when t == typeof(CurrentScopeReuse) =>
9470+
attr.ReuseScopeName == null & attr.ReuseScopeNames == null ? Reuse.Scoped :
9471+
attr.ReuseScopeName != null ? Reuse.ScopedTo(attr.ReuseScopeName) : Reuse.ScopedTo(attr.ReuseScopeNames),
9472+
// todo: @wip support this stuff later
9473+
// ScopedToServiceType;
9474+
// ScopedToServiceKey;
9475+
// ScopedOrSingleton;
9476+
var t => Throw.For<IReuse>(Error.RegisterAttributedUnsupportedReuseType, t)
9477+
};
9478+
9479+
internal static Setup GetSetup(RegisterAttribute attr)
9480+
{
9481+
// todo: @wip support other stuff later
9482+
var transientTracking = attr.TrackDisposableTransient;
9483+
if (transientTracking == DisposableTracking.TrackDisposableTransient)
9484+
return Setup.With(trackDisposableTransient: true);
9485+
9486+
if (transientTracking == DisposableTracking.AllowDisposableTransient)
9487+
return Setup.With(allowDisposableTransient: true);
9488+
9489+
if (transientTracking == DisposableTracking.ThrowOnRegisteringDisposableTransient)
9490+
return Setup.With(allowDisposableTransient: false);
9491+
9492+
return Setup.Default;
9493+
}
94389494
}
94399495

94409496
/// <summary>Extension methods for <see cref="IResolver"/>.</summary>
@@ -14573,6 +14629,33 @@ public interface IReuse : IConvertibleToExpression
1457314629
Expression Apply(Request request, Expression serviceFactoryExpr);
1457414630
}
1457514631

14632+
/// <summary>Transient reuse</summary>
14633+
public sealed class TransientReuse : IReuse
14634+
{
14635+
/// <inheritdoc />
14636+
public int Lifespan => 0;
14637+
14638+
/// <inheritdoc />
14639+
public object Name => null;
14640+
14641+
/// <inheritdoc />
14642+
public Expression Apply(Request _, Expression serviceFactoryExpr) => serviceFactoryExpr;
14643+
14644+
/// <inheritdoc />
14645+
public bool CanApply(Request request) => true;
14646+
14647+
private readonly Lazy<Expression> _transientReuseExpr = Lazy.Of<Expression>(() =>
14648+
Field(null, typeof(Reuse).GetField(nameof(Reuse.Transient))));
14649+
14650+
/// <inheritdoc />
14651+
public Expression ToExpression<S>(S state, Func<S, object, Expression> fallbackConverter) =>
14652+
_transientReuseExpr.Value;
14653+
14654+
/// <inheritdoc />
14655+
public override string ToString() => "TransientReuse";
14656+
}
14657+
14658+
1457614659
/// <summary>Returns container bound scope for storing singleton objects.</summary>
1457714660
public sealed class SingletonReuse : IReuse
1457814661
{
@@ -15112,30 +15195,9 @@ public static IReuse ScopedToService<TService>(object serviceKey = null) =>
1511215195
/// <summary>Obsolete: please prefer using `Scoped` without name instead.
1511315196
/// The usage of the named scopes is the less performant than the unnamed ones. e.g. ASP.NET Core does not use the named scope.</summary>
1511415197
public static readonly IReuse InWebRequest = ScopedTo(WebRequestScopeName);
15198+
}
1511515199

15116-
#region Implementation
15117-
15118-
private sealed class TransientReuse : IReuse
15119-
{
15120-
public int Lifespan => 0;
15121-
15122-
public object Name => null;
15123-
15124-
public Expression Apply(Request _, Expression serviceFactoryExpr) => serviceFactoryExpr;
15125-
15126-
public bool CanApply(Request request) => true;
15127-
15128-
private readonly Lazy<Expression> _transientReuseExpr = Lazy.Of<Expression>(() =>
15129-
Field(null, typeof(Reuse).GetField(nameof(Transient))));
15130-
15131-
public Expression ToExpression<S>(S state, Func<S, object, Expression> fallbackConverter) =>
15132-
_transientReuseExpr.Value;
1513315200

15134-
public override string ToString() => "TransientReuse";
15135-
}
15136-
15137-
#endregion
15138-
}
1513915201

1514015202
/// <summary>Policy to handle unresolved service.</summary>
1514115203
public enum IfUnresolved : byte
@@ -15871,7 +15933,8 @@ public static readonly int
1587115933
"service creation is failed with the exception and the exception was catched, but you're trying to resolve the failed service again. " + NewLine +
1587215934
"For all those reasons DryIoc has a timeout to prevent the infinite waiting. " + NewLine +
1587315935
$"You may change the default timeout via setting the static `Scope.{nameof(Scope.WaitForScopedServiceIsCreatedTimeoutMilliseconds)}`"),
15874-
ServiceTypeIsNull = Of("Registered service type is null");
15936+
ServiceTypeIsNull = Of("Registered service type is null"),
15937+
RegisterAttributedUnsupportedReuseType = Of("Not support reuse type {0} in the RegisterAttribute.");
1587515938

1587615939
#pragma warning restore 1591 // "Missing XML-comment"
1587715940

@@ -16691,15 +16754,75 @@ public class CompileTimeRegisterAttribute : Attribute
1669116754
{
1669216755
}
1669316756

16694-
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
16695-
internal class RegisterAttribute : Attribute // todo: @wip make it public
16757+
/// <summary>Per-registration rules to handle the disposable transient</summary>
16758+
public enum DisposableTracking : byte
16759+
{
16760+
/// <summary>Default behavior is to obey the container rules</summary>
16761+
UseContainerRules = 0,
16762+
/// <summary>Throws the Container exception when trying to register a disposable transient, overriding the container rules</summary>
16763+
ThrowOnRegisteringDisposableTransient,
16764+
/// <summary>Allow registering the disposable transient</summary>
16765+
AllowDisposableTransient,
16766+
/// <summary>Track disposable transient</summary>
16767+
TrackDisposableTransient
16768+
}
16769+
16770+
/// <summary>Base registration attribute,
16771+
/// there may be descendant predefined and custom attributes simplifying the registration setup.</summary>
16772+
public class RegisterAttribute : Attribute
1669616773
{
16697-
// public Type ServiceType;
16774+
/// <summary>By default no, because the attribute operates on the</summary>
16775+
public virtual bool TypesAreStaticallyChecked => false;
1669816776

16699-
// todo: @feature @wip implement the Register in the attribute
16700-
// void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked);
16701-
// public static ReflectionFactory Of(Type implementationType = null, IReuse reuse = null, Made made = null, Setup setup = null)
16702-
// todo: add RegisterMany
16777+
/// <summary>A service type</summary>
16778+
public Type ServiceType;
16779+
16780+
/// <summary>An implementation type</summary>
16781+
public Type ImplementationType;
16782+
16783+
/// <summary>One of the IReuse implementation types.</summary>
16784+
public Type ReuseType; // todo: @wip change to the ScopeType, move it from the AttributedModel
16785+
16786+
/// <summary>Optional name of the bound scope for the Scoped reuse</summary>
16787+
public string ReuseScopeName;
16788+
16789+
/// <summary>Optional names of the bound scopes fore the Scoped reuse. Maybe overridden by <see cref="ReuseScopeName"/></summary>
16790+
public object[] ReuseScopeNames;
16791+
16792+
public Type ScopedToServiceType;
16793+
public Type ScopedToServiceKey;
16794+
public bool ScopedOrSingleton;
16795+
16796+
/// <summary>A service key</summary>
16797+
public object ServiceKey;
16798+
16799+
/// <summary>Uses the global container rules by default</summary>
16800+
public IfAlreadyRegistered? IfAlreadyRegistered;
16801+
16802+
// public Made Made;
16803+
16804+
/// <summary>How to track the disposable transient</summary>
16805+
public DisposableTracking TrackDisposableTransient;
16806+
}
16807+
16808+
// todo: @feature @wip implement the Register in the attribute
16809+
// void Register(Factory factory, Type serviceType, object serviceKey, IfAlreadyRegistered? ifAlreadyRegistered, bool isStaticallyChecked);
16810+
// public static ReflectionFactory Of(Type implementationType = null, IReuse reuse = null, Made made = null, Setup setup = null)
16811+
// todo: add RegisterMany
16812+
/// <summary>A single registration attribute</summary>
16813+
public sealed class RegisterAttribute<TService, TImplementation, TReuse> : RegisterAttribute
16814+
where TReuse : IReuse
16815+
{
16816+
/// <summary>In this case there are compile-time type constraints for the Implementation and Service types</summary>
16817+
public override bool TypesAreStaticallyChecked => true;
16818+
16819+
/// <summary>Creates the registration attribute</summary>
16820+
public RegisterAttribute()
16821+
{
16822+
ServiceType = typeof(TService);
16823+
ImplementationType = typeof(TImplementation);
16824+
ReuseType = typeof(TReuse);
16825+
}
1670316826
}
1670416827

1670516828
/// <summary>Ports some methods from .Net 4.0/4.5</summary>

test/DryIoc.TestRunner/Program.cs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public class Program
99
{
1010
public static void Main()
1111
{
12+
new RegisterAttributeTests().Run();
13+
1214
// new RulesTests().Run();
1315

1416
// new GHIssue672_Wrong_decorator_parameter_with_custom_args().Run();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
namespace DryIoc.UnitTests
5+
{
6+
[TestFixture]
7+
public class RegisterAttributeTests : ITest
8+
{
9+
public int Run()
10+
{
11+
Can_register_service_with_tracking_disposable_reuse();
12+
13+
return 1;
14+
}
15+
16+
[Test]
17+
public void Can_register_service_with_tracking_disposable_reuse()
18+
{
19+
var c = new Container();
20+
21+
var count = c.RegisterByRegisterAttributes(typeof(Registrations));
22+
Assert.AreEqual(1, count);
23+
24+
var ad = c.Resolve<ID>();
25+
Assert.IsNotNull(ad);
26+
27+
c.Dispose();
28+
Assert.IsTrue(((AD)ad).IsDisposed);
29+
}
30+
31+
[Register<ID, AD, TransientReuse>(TrackDisposableTransient = DisposableTracking.TrackDisposableTransient)]
32+
public static class Registrations { }
33+
34+
public interface ID { }
35+
36+
class AD : ID, IDisposable
37+
{
38+
public bool IsDisposed;
39+
public void Dispose() => IsDisposed = true;
40+
}
41+
}
42+
}

test/DryIoc.UnitTests/RulesTests.cs

-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ public class RulesTests : ITest
1414
{
1515
public int Run()
1616
{
17-
I_can_override_global_disposable_transient_allowance_and_prohibit_its_registration_despite_local_setting();
18-
1917
Given_service_with_two_ctors_I_can_specify_what_ctor_to_choose_for_resolve();
2018
I_should_be_able_to_add_rule_to_resolve_not_registered_service();
2119
I_can_remove_rule_to_resolve_not_registered_service();

0 commit comments

Comments
 (0)