C#动态添加属性以接口

Tob*_*ler 5 c# .net-core asp.net-core .net-standard

我有一个asp.net核心Web服务,它需要正确处理对象的接口属性(必须找出用于对JSON字符串反序列化的实现)。

因此,我创建了一个从interface-Attribute调用的类,例如:

[JsonConverter(typeof(InterfaceJsonConverter<IInputFormat>))]
public interface IInputFormat
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

定义InterfaceJsonConverter的模型类必须是不支持程序集加载的.NET Standard 1.4。但是,该技术用于找出哪个程序集具有Interface的实现,以及哪个实现最接近json-object。(我当然可以将接口直接添加到类中,而不用读取程序集,但我想要一个动态解决方案,我将永远不会错过任何实现)

(最初,我是在.net 4.6中开发的,在那儿效果很好)。

所以我现在想做的是:

  1. 创建一个新的.NET Standard 1.6库
  2. 将InterfaceJsonConverter放入该库中
  3. 从接口中删除属性
  4. 在运行时添加接口属性...

有什么办法可以存档吗?还是有更好的方法来处理ASP.NET Core中的接口?

出于完整性考虑,我将发布InterfaceJsonConverter的代码

public class InterfaceJsonConverter<T> : Newtonsoft.Json.JsonConverter
{

    public InterfaceJsonConverter()
    {
        this.DerivedTypes = GetTypesOfImplementedInterfaces(typeof(T));

    }
    readonly HashSet<Type> derivedTypes = new HashSet<Type>();

    public InterfaceJsonConverter(params Type[] types)
    {
        this.DerivedTypes = types;
    }

    public InterfaceJsonConverter(System.Reflection.Assembly assembly, string @namespace)
    {
        this.DerivedTypes = GetTypesInNamespace(assembly, @namespace);
    }

    private Type[] GetTypesOfImplementedInterfaces(Type type)
    {
        var parts = type.AssemblyQualifiedName.Split(',');
        var assemblyName = parts[1].Trim();
        var assemblies = AssemblyRepo.GetReferencingAssemblies(assemblyName);

        //var assemblies = AppDomain.CurrentDomain.GetAssemblies();

        var types = new List<Type>();
        foreach (var a in assemblies)
        {
            try
            {
                //type.GetInterfaces().Any(i => i.FullName == typeof(T).FullName)
                var currentTypes = a.GetTypes().Where(t => t.GetTypeInfo().IsAbstract==false && t.GetInterfaces().Any(i => i.FullName == type.FullName)).ToList();
                types.AddRange(currentTypes);

            }
            catch (System.Exception) { }// ignored}
        }

        if(types.Count==0)
            throw new System.Exception("No class found which implements interface [" + typeof(T) + "].");

        return types.ToArray();
    }

    private Type[] GetTypesInNamespace(System.Reflection.Assembly assembly, string @namespace)
    {
        //return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
        return assembly.GetTypes()
            .Where(
                type => String.Equals(type.Namespace, @namespace) 
                && type.GetInterfaces().Any(i => i.FullName == typeof (T).FullName)
                ).ToArray();
    }


    public IEnumerable<Type> DerivedTypes
    {
        get
        {
            return derivedTypes.ToArray();
        }
        set
        {
            if (value == null)
                throw new ArgumentNullException();
            derivedTypes.Clear();
            derivedTypes.UnionWith(value);
        }
    }

    JsonObjectContract FindContract(JObject obj, JsonSerializer serializer)
    {
        List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();
        foreach (var type in derivedTypes)
        {
            if (type.GetTypeInfo().IsAbstract)
                continue;
            var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract;
            if (contract == null)
                continue;
            if (obj.Properties().Select(p => p.Name).Where(n => n != "$type").Any(n => contract.Properties.GetClosestMatchProperty(n) == null))
                continue;
            if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count)
            {
                bestContracts.Clear();
                bestContracts.Add(contract);
            }
            else if (contract.Properties.Count == bestContracts[0].Properties.Count)
            {
                bestContracts.Add(contract);
            }
        }
        return bestContracts.Count > 0 ? bestContracts.Single() : null;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        var obj = JObject.Load(reader); // Throws an exception if the current token is not an object.
        if (obj["$type"] != null && serializer.TypeNameHandling != TypeNameHandling.None)
        {
            // Prevent infinite recursion when using an explicit converter in the list.
            var removed = serializer.Converters.Remove(this);
            try
            {
                // Kludge to prevent infinite recursion when using JsonConverterAttribute on the type: deserialize to object.
                return obj.ToObject(typeof (object), serializer);
            }
            finally
            {
                if (removed)
                    serializer.Converters.Add(this);
            }
        }

        var contract = FindContract(obj, serializer);
        if (contract == null)
            throw new JsonSerializationException("no contract found for " + obj.ToString());
        if (existingValue == null || !contract.UnderlyingType.IsInstanceOfType(existingValue))
            //!contract.UnderlyingType.IsAssignableFrom(existingValue.GetType()))
            existingValue = contract.DefaultCreator();
        //will call the default constructor(PARAMETERLESS)...MAKE SURE YOUR CLASS HAS ONE!!!!!
        using (var sr = obj.CreateReader())
        {
            serializer.Populate(sr, existingValue);
        }
        return existingValue;
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
Run Code Online (Sandbox Code Playgroud)