From 6f1575dc3d9d2ddfd26b5c334feda5d6a1acd41a Mon Sep 17 00:00:00 2001 From: Drew Noakes Date: Mon, 29 Aug 2016 19:35:18 +0100 Subject: [PATCH] Move types into their own files. --- Servant/Lifestyle.cs | 42 +++++++++++++ Servant/Servant.cs | 115 ------------------------------------ Servant/Servant.csproj | 4 ++ Servant/ServantException.cs | 45 ++++++++++++++ Servant/TypeEntry.cs | 41 +++++++++++++ Servant/TypeProvider.cs | 101 +++++++++++++++++++++++++++++++ 6 files changed, 233 insertions(+), 115 deletions(-) create mode 100644 Servant/Lifestyle.cs create mode 100644 Servant/ServantException.cs create mode 100644 Servant/TypeEntry.cs create mode 100644 Servant/TypeProvider.cs diff --git a/Servant/Lifestyle.cs b/Servant/Lifestyle.cs new file mode 100644 index 0000000..cab9127 --- /dev/null +++ b/Servant/Lifestyle.cs @@ -0,0 +1,42 @@ +#region License +// +// Servant +// +// Copyright 2016 Drew Noakes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// More information about this project is available at: +// +// https://github.com/drewnoakes/servant +// +#endregion + +namespace Servant +{ + /// + /// Specifies how instances are reused between dependants. + /// + public enum Lifestyle + { + /// + /// Only a single instance of the service will be created. + /// + Singleton, + + /// + /// A new instance of the service will be created for each dependant. + /// + Transient + } +} \ No newline at end of file diff --git a/Servant/Servant.cs b/Servant/Servant.cs index b7b4ca5..ac2cdca 100644 --- a/Servant/Servant.cs +++ b/Servant/Servant.cs @@ -25,111 +25,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using JetBrains.Annotations; namespace Servant { - /// - /// Specifies how instances are reused between dependants. - /// - public enum Lifestyle - { - /// - /// Only a single instance of the service will be created. - /// - Singleton, - - /// - /// A new instance of the service will be created for each dependant. - /// - Transient - } - - internal sealed class TypeEntry - { - public Type DeclaredType { get; } - - [CanBeNull] public TypeProvider Provider { get; set; } - - public TypeEntry(Type declaredType) - { - DeclaredType = declaredType; - } - } - - internal sealed class TypeProvider - { - public Lifestyle Lifestyle { get; } - public IReadOnlyList Dependencies { get; } - - private readonly Servant _servant; - private readonly Func> _factory; - private readonly Type _declaredType; - - [CanBeNull] private object _singletonInstance; - - public TypeProvider(Servant servant, Func> factory, Type declaredType, Lifestyle lifestyle, IReadOnlyList dependencies) - { - _servant = servant; - _factory = factory; - _declaredType = declaredType; - Lifestyle = lifestyle; - Dependencies = dependencies; - } - - public async Task GetAsync() - { - // TODO make concurrency-safe here to avoid double-allocation of singleton - - if (Lifestyle == Lifestyle.Singleton && _singletonInstance != null) - return _singletonInstance; - - // find arguments - var argumentTasks = new List>(); - foreach (var dep in Dependencies) - { - if (dep.Provider == null) - { - // No provider exists for this dependency. - var message = $"Type \"{_declaredType}\" depends upon unregistered type \"{dep.DeclaredType}\"."; - - // See whether we have a super-type of the requested type. - var superTypes = _servant.GetRegisteredTypes().Where(type => type.IsAssignableFrom(dep.DeclaredType)).ToList(); - if (superTypes.Any()) - message += $" Did you mean to reference registered super type {string.Join(" or ", superTypes.Select(st => $"\"{st}\""))}?"; - - throw new ServantException(message); - } - argumentTasks.Add(dep.Provider.GetAsync()); - } - - await Task.WhenAll(argumentTasks); - - var instance = await _factory.Invoke(argumentTasks.Select(t => t.Result).ToArray()); - - if (instance == null) - throw new ServantException($"Instance for type \"{_declaredType}\" cannot be null."); - - if (!_declaredType.IsInstanceOfType(instance)) - throw new ServantException($"Instance produced for type \"{_declaredType}\" is not an instance of that type."); - - if (Lifestyle == Lifestyle.Singleton) - { - _singletonInstance = instance; - - var disposable = instance as IDisposable; - if (disposable != null) - _servant.PushDisposableSingleton(disposable); - } - - return instance; - } - } - /// /// Serves instances of specific types, resolving dependencies as required, and running any async initialisation. /// @@ -303,20 +204,4 @@ public void Dispose() disposable.Dispose(); } } - - /// - /// An exception raised by Servant. - /// - [ExcludeFromCodeCoverage] - public sealed class ServantException : Exception - { - /// - public ServantException() { } - - /// - public ServantException(string message) : base(message) { } - - /// - public ServantException(string message, Exception innerException) : base(message, innerException) { } - } } diff --git a/Servant/Servant.csproj b/Servant/Servant.csproj index 29c25ab..f5868cf 100644 --- a/Servant/Servant.csproj +++ b/Servant/Servant.csproj @@ -39,8 +39,10 @@ + + True @@ -48,6 +50,8 @@ ServantExtensions.Generated.tt + + diff --git a/Servant/ServantException.cs b/Servant/ServantException.cs new file mode 100644 index 0000000..6357c9f --- /dev/null +++ b/Servant/ServantException.cs @@ -0,0 +1,45 @@ +#region License +// +// Servant +// +// Copyright 2016 Drew Noakes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// More information about this project is available at: +// +// https://github.com/drewnoakes/servant +// +#endregion + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Servant +{ + /// + /// An exception raised by Servant. + /// + [ExcludeFromCodeCoverage] + public sealed class ServantException : Exception + { + /// + public ServantException() { } + + /// + public ServantException(string message) : base(message) { } + + /// + public ServantException(string message, Exception innerException) : base(message, innerException) { } + } +} \ No newline at end of file diff --git a/Servant/TypeEntry.cs b/Servant/TypeEntry.cs new file mode 100644 index 0000000..66ca20c --- /dev/null +++ b/Servant/TypeEntry.cs @@ -0,0 +1,41 @@ +#region License +// +// Servant +// +// Copyright 2016 Drew Noakes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// More information about this project is available at: +// +// https://github.com/drewnoakes/servant +// +#endregion + +using System; +using JetBrains.Annotations; + +namespace Servant +{ + internal sealed class TypeEntry + { + public Type DeclaredType { get; } + + [CanBeNull] public TypeProvider Provider { get; set; } + + public TypeEntry(Type declaredType) + { + DeclaredType = declaredType; + } + } +} \ No newline at end of file diff --git a/Servant/TypeProvider.cs b/Servant/TypeProvider.cs new file mode 100644 index 0000000..4ea023e --- /dev/null +++ b/Servant/TypeProvider.cs @@ -0,0 +1,101 @@ +#region License +// +// Servant +// +// Copyright 2016 Drew Noakes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// More information about this project is available at: +// +// https://github.com/drewnoakes/servant +// +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; + +namespace Servant +{ + internal sealed class TypeProvider + { + public Lifestyle Lifestyle { get; } + public IReadOnlyList Dependencies { get; } + + private readonly Servant _servant; + private readonly Func> _factory; + private readonly Type _declaredType; + + [CanBeNull] private object _singletonInstance; + + public TypeProvider(Servant servant, Func> factory, Type declaredType, Lifestyle lifestyle, IReadOnlyList dependencies) + { + _servant = servant; + _factory = factory; + _declaredType = declaredType; + Lifestyle = lifestyle; + Dependencies = dependencies; + } + + public async Task GetAsync() + { + // TODO make concurrency-safe here to avoid double-allocation of singleton + + if (Lifestyle == Lifestyle.Singleton && _singletonInstance != null) + return _singletonInstance; + + // find arguments + var argumentTasks = new List>(); + foreach (var dep in Dependencies) + { + if (dep.Provider == null) + { + // No provider exists for this dependency. + var message = $"Type \"{_declaredType}\" depends upon unregistered type \"{dep.DeclaredType}\"."; + + // See whether we have a super-type of the requested type. + var superTypes = _servant.GetRegisteredTypes().Where(type => type.IsAssignableFrom(dep.DeclaredType)).ToList(); + if (superTypes.Any()) + message += $" Did you mean to reference registered super type {string.Join(" or ", superTypes.Select(st => $"\"{st}\""))}?"; + + throw new ServantException(message); + } + argumentTasks.Add(dep.Provider.GetAsync()); + } + + await Task.WhenAll(argumentTasks); + + var instance = await _factory.Invoke(argumentTasks.Select(t => t.Result).ToArray()); + + if (instance == null) + throw new ServantException($"Instance for type \"{_declaredType}\" cannot be null."); + + if (!_declaredType.IsInstanceOfType(instance)) + throw new ServantException($"Instance produced for type \"{_declaredType}\" is not an instance of that type."); + + if (Lifestyle == Lifestyle.Singleton) + { + _singletonInstance = instance; + + var disposable = instance as IDisposable; + if (disposable != null) + _servant.PushDisposableSingleton(disposable); + } + + return instance; + } + } +} \ No newline at end of file