// Returns type if it's a [Service] class, // otherwise searches for a [Service] implementing the specified interface private Type ClassOrVersionFromInterface(Type type, ServedAttribute servedAttribute = null) { if (type.IsInterface) { IEnumerable <Type> versions = this.instances.TypesAssignableFrom(type); // is there already an implementation for the interface? if (versions.Any()) { return(versions.First()); } else { versions = this.Version(type, servedAttribute); if (versions.Any()) { var t = versions.First(); MaskPass(t); return(t); } if (servedAttribute is null) { throw new ImplementationNotFoundException(type, $"can't find [Service] for interface {type.Name}"); } else { throw new ImplementationNotFoundException(type, $"can't find [Service] for {type.Name} v{servedAttribute.TargetVersion}"); } } } else { MaskPass(type); return(type); } void MaskPass(Type type) { if (this.settings.Mask.IsMasked(type)) { throw new InvalidOperationException($"Type {type.Name} is { (this.settings.Mask._isWhiteList ? "not whitelisted" : "blacklisted")}"); } //todo create custom exception } }
// returns the instantiated object private object InstTypeOrServiceEnum(Type type, ServedAttribute servedAttribute, ref List <object> used) { if ( this.settings.Injection._serveEnumerables && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable <>) ) { var generic = type.GetGenericArguments()[0]; // list of sorted valid types var validTypes = this.Version(generic, servedAttribute).ToArray(); // creates generic list using reflection var listType = typeof(List <>).MakeGenericType(generic); // cast to allow calling .Add() var instances = (IList)Activator.CreateInstance(listType); // gather instances if necessary foreach (var serviceType in validTypes) { var obj = this.OfTypeOrInstantiate(serviceType); instances.Add(obj); used.Add(obj); } return(instances); } else { var serviceType = this.ClassOrVersionFromInterface(type, servedAttribute); var obj = this.OfTypeOrInstantiate(serviceType); used.Add(obj); return(obj); } }
// performs versioning on the specified type private IEnumerable <Type> Version(Type targetType, ServedAttribute servedAttribute) { var targetVersion = servedAttribute?.TargetVersion ?? 0.0; var method = (this.settings.Injection._overrideTargetingMethod || servedAttribute is null || !(servedAttribute._targetingDefined)) ? this.settings.Injection._targetingMethod : servedAttribute.TargetingMethod; ////var candidateTypes = this.instances.TypesAssignableFrom(targetType); var candidateTypes = targetType .Assembly .GetTypes() .Where(t => t.IsClass && !t.IsAbstract && targetType.IsAssignableFrom(t)); return(method switch { ServedVersionTargetingMethod.None => candidateTypes, ServedVersionTargetingMethod.Exact => candidateTypes .Where ( t => t.GetCustomAttribute <ServiceAttribute>() .Version == targetVersion ), ServedVersionTargetingMethod.LatestMajor => candidateTypes .Where ( t => t.GetCustomAttribute <ServiceAttribute>() .Version >= targetVersion ) .OrderByDescending(t => t.GetCustomAttribute <ServiceAttribute>().Version), ServedVersionTargetingMethod.LatestMinor => candidateTypes .Where ( t => { var v = t.GetCustomAttribute <ServiceAttribute>().Version; return v >= targetVersion && v < Math.Floor(targetVersion + 1); } ) .OrderByDescending(t => t.GetCustomAttribute <ServiceAttribute>().Version), _ => throw new NotImplementedException() });