Skip to content

Instantly share code, notes, and snippets.

@gusarov
Created December 23, 2024 21:07
Show Gist options
  • Save gusarov/cf4aed5cff52b4404e28d173b18083ac to your computer and use it in GitHub Desktop.
Save gusarov/cf4aed5cff52b4404e28d173b18083ac to your computer and use it in GitHub Desktop.
Prevent duplicated service registration before build using decorator
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace CompanyName;
internal class Program
{
private static void Main(string[] args)
{
var appBuidler = Host.CreateApplicationBuilder();
var serviceCollection = new ConstrainedServiceCollection(appBuidler.Services);
serviceCollection.AddSingleton<IService, Serivce1>();
serviceCollection.AddSingleton<IService, Serivce2>();
var app = appBuidler.Build();
app.Run();
}
}
interface IService
{
}
class Serivce1 : IService
{
}
class Serivce2 : IService
{
}
class ConstrainedServiceCollection : IServiceCollection
{
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
private readonly IServiceCollection _underlyingServices;
private readonly Dictionary<Type, ServiceDescriptor> _byType = new Dictionary<Type, ServiceDescriptor>();
private bool _isReadOnly;
public ConstrainedServiceCollection(IServiceCollection underlyingServices)
{
_underlyingServices = underlyingServices;
}
public int Count => _descriptors.Count;
public bool IsReadOnly => _isReadOnly;
public void MakeReadOnly()
{
_isReadOnly = true;
}
public ServiceDescriptor this[int index]
{
get
{
return _descriptors[index];
}
set
{
CheckReadOnly();
_descriptors[index] = value;
}
}
private void CheckReadOnly()
{
if (_isReadOnly)
{
throw new Exception();
}
}
void CheckExisting(ServiceDescriptor item)
{
if (!_byType.TryAdd(item.ServiceType, item))
{
var existing = _byType[item.ServiceType];
throw new Exception($"Can not add Service {item.ServiceType.Name} implemented as {item.ImplementationType?.Name} because it is already registered as {existing.ImplementationType?.Name}");
}
}
public void Clear()
{
CheckReadOnly();
_descriptors.Clear();
}
public bool Contains(ServiceDescriptor item)
{
return _descriptors.Contains(item);
}
public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
{
_descriptors.CopyTo(array, arrayIndex);
}
public bool Remove(ServiceDescriptor item)
{
CheckReadOnly();
return _descriptors.Remove(item);
}
public IEnumerator<ServiceDescriptor> GetEnumerator()
{
return _descriptors.GetEnumerator();
}
void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item)
{
CheckReadOnly();
CheckExisting(item);
_descriptors.Add(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int IndexOf(ServiceDescriptor item)
{
return _descriptors.IndexOf(item);
}
public void Insert(int index, ServiceDescriptor item)
{
CheckReadOnly();
CheckExisting(item);
_descriptors.Insert(index, item);
}
public void RemoveAt(int index)
{
CheckReadOnly();
_descriptors.RemoveAt(index);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment