.net core中自带了依赖注入框架,asp.net core或worker框架下可以直接使用, 控制台程序可以通过加入Microsoft.Extensions.DependencyInjection程序包来支持。自带的di框架功能还行, 但是一个不方便的地方是没有提供服务自动发现、注册的接口,稍微大的程序都是需要通过反射自己写一个发现程序的。
今天网上找到了一个第三方实现的服务发现的库Scrutor,还比较好用, 示例如下:
var collection = new ServiceCollection();
collection.Scan(scan => scan
// We start out with all types in the assembly of ITransientService
.FromAssemblyOf<ITransientService>()
// AddClasses starts out with all public, non-abstract types in this assembly.
// These types are then filtered by the delegate passed to the method.
// In this case, we filter out only the classes that are assignable to ITransientService.
.AddClasses(classes => classes.AssignableTo<ITransientService>())
// We then specify what type we want to register these classes as.
// In this case, we want to register the types as all of its implemented interfaces.
// So if a type implements 3 interfaces; A, B, C, we'd end up with three separate registrations.
.AsImplementedInterfaces()
// And lastly, we specify the lifetime of these registrations.
.WithTransientLifetime()
// Here we start again, with a new full set of classes from the assembly above.
// This time, filtering out only the classes assignable to IScopedService.
.AddClasses(classes => classes.AssignableTo<IScopedService>())
// Now, we just want to register these types as a single interface, IScopedService.
.As<IScopedService>()
// And again, just specify the lifetime.
.WithScopedLifetime()
// Generic interfaces are also supported too, e.g. public interface IOpenGeneric<T>
.AddClasses(classes => classes.AssignableTo(typeof(IOpenGeneric<>)))
.AsImplementedInterfaces()
// And you scan generics with multiple type parameters too
// e.g. public interface IQueryHandler<TQuery, TResult>
.AddClasses(classes => classes.AssignableTo(typeof(IQueryHandler<,>)))
.AsImplementedInterfaces());
这个类本身是比较好用的,但是它仍需要手工制定程序集作为扫描范围。 这个程序集的范围并不是所有程序集,往往只有用户代码程序集, asp.net core程序内部是有这个功能的,例如它内部就能扫描所有用户程序集,生成ApiController,网上搜了一下,发现还是一个内部接口,不过可以通过反射来调用:
public static Assembly[] GetAssemblies()
{
var manager = new ApplicationPartManager();
var method = manager.GetType()
.GetMethod("PopulateDefaultParts",
BindingFlags.NonPublic | BindingFlags.Instance);
//加载入口程序集的依赖项树中的所有非官方包的依赖程序集
var assembly = Assembly.GetEntryAssembly().FullName;
method.Invoke(manager, new object[] { assembly });
return manager.ApplicationParts.OfType<AssemblyPart>().Select(i => i.Assembly).ToArray();
}