|
| 1 | +// Copyright (c) .NET Foundation. All rights reserved. |
| 2 | +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. |
| 3 | + |
| 4 | +using System; |
| 5 | +using Microsoft.Extensions.DependencyInjection; |
| 6 | + |
| 7 | +namespace Microsoft.AspNetCore.Components |
| 8 | +{ |
| 9 | + /// <summary> |
| 10 | + /// A base class that creates a service provider scope. |
| 11 | + /// </summary> |
| 12 | + /// <remarks> |
| 13 | + /// Use the <see cref="OwningComponentBase"/> class as a base class to author components that control |
| 14 | + /// the lifetime of a service provider scope. This is useful when using a transient or scoped service that |
| 15 | + /// requires disposal such as a repository or database abstraction. Using <see cref="OwningComponentBase"/> |
| 16 | + /// as a base class ensures that the service provider scope is disposed with the component. |
| 17 | + /// </remarks> |
| 18 | + public abstract class OwningComponentBase : ComponentBase, IDisposable |
| 19 | + { |
| 20 | + private IServiceScope _scope; |
| 21 | + |
| 22 | + [Inject] IServiceScopeFactory ScopeFactory { get; set; } |
| 23 | + |
| 24 | + /// <summary> |
| 25 | + /// Gets a value determining if the component and associated services have been disposed. |
| 26 | + /// </summary> |
| 27 | + protected bool IsDisposed { get; private set; } |
| 28 | + |
| 29 | + /// <summary> |
| 30 | + /// Gets the scoped <see cref="IServiceProvider"/> that is associated with this component. |
| 31 | + /// </summary> |
| 32 | + protected IServiceProvider ScopedServices |
| 33 | + { |
| 34 | + get |
| 35 | + { |
| 36 | + if (ScopeFactory == null) |
| 37 | + { |
| 38 | + throw new InvalidOperationException("Services cannot be accessed before the component is initialized."); |
| 39 | + } |
| 40 | + |
| 41 | + if (IsDisposed) |
| 42 | + { |
| 43 | + throw new ObjectDisposedException(GetType().Name); |
| 44 | + } |
| 45 | + |
| 46 | + _scope ??= ScopeFactory.CreateScope(); |
| 47 | + return _scope.ServiceProvider; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + void IDisposable.Dispose() |
| 52 | + { |
| 53 | + if (!IsDisposed) |
| 54 | + { |
| 55 | + _scope?.Dispose(); |
| 56 | + _scope = null; |
| 57 | + Dispose(disposing: true); |
| 58 | + IsDisposed = true; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + /// <inheritdoc /> |
| 63 | + protected virtual void Dispose(bool disposing) |
| 64 | + { |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + /// <summary> |
| 69 | + /// A base class that creates a service provider scope, and resolves a service of type <typeparamref name="TService"/>. |
| 70 | + /// </summary> |
| 71 | + /// <typeparam name="TService">The service type.</typeparam> |
| 72 | + /// <remarks> |
| 73 | + /// Use the <see cref="OwningComponentBase{TService}"/> class as a base class to author components that control |
| 74 | + /// the lifetime of a service or multiple services. This is useful when using a transient or scoped service that |
| 75 | + /// requires disposal such as a repository or database abstraction. Using <see cref="OwningComponentBase{TService}"/> |
| 76 | + /// as a base class ensures that the service and relates services that share its scope are disposed with the component. |
| 77 | + /// </remarks> |
| 78 | + public abstract class OwningComponentBase<TService> : OwningComponentBase, IDisposable |
| 79 | + { |
| 80 | + private TService _item; |
| 81 | + |
| 82 | + /// <summary> |
| 83 | + /// Gets the <typeparamref name="TService"/> that is associated with this component. |
| 84 | + /// </summary> |
| 85 | + protected TService Service |
| 86 | + { |
| 87 | + get |
| 88 | + { |
| 89 | + if (IsDisposed) |
| 90 | + { |
| 91 | + throw new ObjectDisposedException(GetType().Name); |
| 92 | + } |
| 93 | + |
| 94 | + // We cache this because we don't know the lifetime. We have to assume that it could be transient. |
| 95 | + _item ??= ScopedServices.GetRequiredService<TService>(); |
| 96 | + return _item; |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | +} |
0 commit comments