关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件

之前看过MVC4.0的源码,里面就有Binder。作用是将前台页面传递过来的键值对/字典表绑定到特定的对象。此处的Binder几乎是同样的作用——将IConfiguration映射为特定的类型<T>.

我们进入正文之前还是看一个例子比较好,这样能充分了解Binder。

public class ComplexOptions
{
public ComplexOptions()
{
Virtual = "complex";
}
public int Integer { get; set; }
public bool Boolean { get; set; }
public virtual string Virtual { get; set; }
} public class Test
{
public IConfiguration BuildConfiguration()
{
var dic = new Dictionary<string, string>
{
{"Integer", "-2"},
{"Boolean", "TRUe"},
{"Nested:Integer", ""}
};
var builder = new ConfigurationBuilder(new MemoryConfigurationSource(dic));
return config = builder.Build();
}
public void BinderTest()
{
var config = BuildConfiguration();
var options = ConfigurationBinder.Bind<ComplexOptions>(config);
Assert.True(options.Boolean);
Assert.Equal(-, options.Integer);
}
}

上面例子比较简单,实际上对象可能是复合对象(对象的属性还是对象),也可能是数组、枚举、结构体等。所以我们创建的过程中需要对属性遍历,如果是复合对象,还涉及到递归的过程,所以我把整个过程绘制成类似流程图的图标。如下图所示:

下面我们正式介绍工程的源码,按照惯例还是上工程结构图(就那么一个文件,搞什么搞)

整个工程只有ConfigurationBinder这一个类,调用过程上面“流程图”已经画出来了。

 public static class ConfigurationBinder
{
public static TModel Bind<TModel>(IConfiguration configuration) where TModel : new()
{
var model = new TModel();
Bind(model, configuration);
return model;
} public static void Bind(object model, IConfiguration configuration)
{
if (model == null)
{
return;
} BindObjectProperties(model, configuration);
} private static void BindObjectProperties(object obj, IConfiguration configuration)
{
foreach (var property in GetAllProperties(obj.GetType().GetTypeInfo()))
{
BindProperty(property, obj, configuration);
}
} private static void BindProperty(PropertyInfo property, object propertyOwner, IConfiguration configuration)
{
configuration = configuration.GetConfigurationSection(property.Name); if (property.GetMethod == null || !property.GetMethod.IsPublic)
{
// We don't support set only properties
return;
} var propertyValue = property.GetValue(propertyOwner);
var hasPublicSetter = property.SetMethod != null && property.SetMethod.IsPublic; if (propertyValue == null && !hasPublicSetter)
{
// Property doesn't have a value and we cannot set it so there is no
// point in going further down the graph
return;
} propertyValue = BindType(
property.PropertyType,
propertyValue,
configuration); if (propertyValue != null && hasPublicSetter)
{
property.SetValue(propertyOwner, propertyValue);
}
} private static object BindType(Type type, object typeInstance, IConfiguration configuration)
{
var configValue = configuration.Get(null);
var typeInfo = type.GetTypeInfo(); if (configValue != null)
{
// Leaf nodes are always reinitialized
return CreateValueFromConfiguration(type, configValue, configuration);
}
else
{
var subkeys = configuration.GetConfigurationSections();
if (subkeys.Count() != )
{
if (typeInstance == null)
{
if (typeInfo.IsInterface || typeInfo.IsAbstract)
{
throw new InvalidOperationException(Resources.FormatError_CannotActivateAbstractOrInterface(type));
} bool hasParameterlessConstructor = typeInfo.DeclaredConstructors.Any(ctor => ctor.IsPublic && ctor.GetParameters().Length == );
if (!hasParameterlessConstructor)
{
throw new InvalidOperationException(Resources.FormatError_MissingParameterlessConstructor(type));
} try
{
typeInstance = Activator.CreateInstance(type);
}
catch (Exception ex)
{
throw new InvalidOperationException(Resources.FormatError_FailedToActivate(type), ex);
}
} var collectionInterface = GetGenericOpenInterfaceImplementation(typeof(IDictionary<,>), type);
if (collectionInterface != null)
{
// Dictionary
BindDictionary(typeInstance, collectionInterface, configuration);
}
else
{
collectionInterface = GetGenericOpenInterfaceImplementation(typeof(ICollection<>), type);
if (collectionInterface != null)
{
// ICollection
BindCollection(typeInstance, collectionInterface, configuration);
}
else
{
// Something else
BindObjectProperties(typeInstance, configuration);
}
}
}
return typeInstance;
}
} private static void BindDictionary(object dictionary, Type iDictionaryType, IConfiguration configuration)
{
var iDictionaryTypeInfo = iDictionaryType.GetTypeInfo(); // It is guaranteed to have a two and only two parameters
// because this is an IDictionary<K,V>
var keyType = iDictionaryTypeInfo.GenericTypeArguments[];
var valueType = iDictionaryTypeInfo.GenericTypeArguments[]; if (keyType != typeof(string))
{
// We only support string keys
return;
} var addMethod = iDictionaryTypeInfo.GetDeclaredMethod("Add");
var subkeys = configuration.GetConfigurationSections().ToList(); foreach (var keyProperty in subkeys)
{
var keyConfiguration = keyProperty.Value; var item = BindType(
type: valueType,
typeInstance: null,
configuration: keyConfiguration);
if (item != null)
{
addMethod.Invoke(dictionary, new[] { keyProperty.Key, item });
}
}
} private static void BindCollection(object collection, Type iCollectionType, IConfiguration configuration)
{
var iCollectionTypeInfo = iCollectionType.GetTypeInfo(); // It is guaranteed to have a one and only one parameter
// because this is an ICollection<T>
var itemType = iCollectionTypeInfo.GenericTypeArguments[]; var addMethod = iCollectionTypeInfo.GetDeclaredMethod("Add");
var subkeys = configuration.GetConfigurationSections().ToList(); foreach (var keyProperty in subkeys)
{
var keyConfiguration = keyProperty.Value; try
{
var item = BindType(
type: itemType,
typeInstance: null,
configuration: keyConfiguration);
if (item != null)
{
addMethod.Invoke(collection, new[] { item });
}
}
catch
{
}
}
} private static object CreateValueFromConfiguration(Type type, string value, IConfiguration configuration)
{
var typeInfo = type.GetTypeInfo(); if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return CreateValueFromConfiguration(Nullable.GetUnderlyingType(type), value, configuration);
} var configurationValue = configuration.Get(key: null); try
{
if (typeInfo.IsEnum)
{
return Enum.Parse(type, configurationValue);
}
else
{
return Convert.ChangeType(configurationValue, type);
}
}
catch (Exception ex)
{
throw new InvalidOperationException(Resources.FormatError_FailedBinding(configurationValue, type), ex);
}
} private static Type GetGenericOpenInterfaceImplementation(Type expectedOpenGeneric, Type actual)
{
var interfaces = actual.GetTypeInfo().ImplementedInterfaces;
foreach (var interfaceType in interfaces)
{
if (interfaceType.GetTypeInfo().IsGenericType &&
interfaceType.GetGenericTypeDefinition() == expectedOpenGeneric)
{
return interfaceType;
}
} return null;
} private static IEnumerable<PropertyInfo> GetAllProperties(TypeInfo type)
{
var allProperties = new List<PropertyInfo>(); do
{
allProperties.AddRange(type.DeclaredProperties);
type = type.BaseType.GetTypeInfo();
}
while (type != typeof(object).GetTypeInfo()); return allProperties;
}
}

ConfigurationBinder

我们对其中几个方法,进行简单的说明:

  • GetAllProperties。系统获取属性的时候,不光要获取当前类的属性也要获取基类的属性。
  • 绑定属性时,将需要被绑定的对象作为参数传入进去,由于是引用类型,所以不用返回值也能更改其属性、类似的还有ArrayList等。
    • BindProperty(PropertyInfo property, object propertyOwner, IConfiguration configuration)。此处的propertyOwner值会被调用方法中修改。
  • 将字符串转换成枚举的方法:
    • Enum.Parse(type, configurationValue);、
  • 将对象转变类型的方法:
    • Convert.ChangeType(configurationValue, type);
  • 判断泛型的方法
    •   

      private static Type GetGenericOpenInterfaceImplementation(Type expectedOpenGeneric, Type actual)
      {
      var interfaces = actual.GetTypeInfo().ImplementedInterfaces;
      foreach (var interfaceType in interfaces)
      {
      if (interfaceType.GetTypeInfo().IsGenericType &&
      interfaceType.GetGenericTypeDefinition() == expectedOpenGeneric)
      {
      return interfaceType;
      }
      } return null;
      }

[Asp.net 5] Configuration-新一代的配置文件(神奇的Binder)的更多相关文章

  1. C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作,无法为请求的 Configuration 对象创建配置文件。

    应用程序配置文件,对于asp.net是 web.config,对于WINFORM程序是 App.Config(ExeName.exe.config). 配置文件,对于程序本身来说,就是基础和依据,其本 ...

  2. [Asp.net 5] Configuration-新一代的配置文件

    微软新一代asp.net(vnext),也叫asp.net 5,开源代码都放在网址https://github.com/aspnet下. 本文介绍的是Configuration工程,下载路径为http ...

  3. [Asp.net 5] Configuration-新一代的配置文件(接口定义与基础实现)

    关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件 本系列文章讲的是asp.net 5(Asp.net VNext)中的配置文件部分,工程下载地址为:https: ...

  4. [Asp.net 5] Configuration-新一代的配置文件(ConfigurationSource的多种实现)

    关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件 在前面我们介绍了,系统中用IConfigurationSource表示不同配置文件的来源,起到读取.设置.加载 ...

  5. Asp.net Core 和类库读取配置文件信息

    Asp.net Core 和类库读取配置文件信息 看干货请移步至.net core 读取配置文件公共类 首先开一个脑洞,Asp.net core 被使用这么长时间了,但是关于配置文件(json)的读取 ...

  6. 无法为请求的 Configuration 对象创建配置文件 错误原因

    Configuration config = WebConfigurationManager.OpenWebConfiguration("~"); 无法为请求的 Configura ...

  7. [译]ASP.NET 5 Configuration

    原文:https://docs.asp.net/en/latest/fundamentals/configuration.html ASP.NET 5支持多种配置选项. 应用的配置文件可以是JSON, ...

  8. ASP.NET网站开发中的配置文件

    来源:微信公众号CodeL 1.配置文件层次分类 Machine.config:  对.netframework整体的配置 web.config(framework目录下):  对所有项目所公有的应用 ...

  9. Asp .Net Core 读取appsettings.json配置文件

         Asp .Net Core 如何读取appsettings.json配置文件?最近也有学习到如何读取配置文件的,主要是通过 IConfiguration,以及在Program中初始化完成的. ...

随机推荐

  1. 设计模式之美:Type Object(类型对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Type Object 的经典介绍. 实现方式(二):Type Object 在游戏设计中的使用. 意图 允许在运行时动态灵活的 ...

  2. Spring-Context之八:一些依赖注入的小技巧

    Spring框架在依赖注入方面是非常灵活和强大的,多了解点一些注入的方式.方法,绝对能优化配置. idref idref属性可以传入一个bean的名称,虽然它是指向一个bean的引用,但是得到的是该b ...

  3. 细说.NET中的多线程 (二 线程池)

    上一章我们了解到,由于线程的创建,销毁都是需要耗费大量资源和时间的,开发者应该非常节约的使用线程资源.最好的办法是使用线程池,线程池能够避免当前进行中大量的线程导致操作系统不停的进行线程切换,当线程数 ...

  4. 扩展GridView控件——为内容项添加拖放及分组功能

    引言 相信大家对GridView都不陌生,是非常有用的控件,用于平铺有序的显示多个内容项.打开任何WinRT应用或者是微软合作商的网站,都会在APP中发现GridView的使用.“Tiles”提供了一 ...

  5. 爱上MVC系列~前端验证与后端数据有效性验证

    回到目录 有一句话,在10年前就是真理,到现在也一直都是,“前端验证可以没有,但后端验证必须要有”,这句话相信大家都没有意见吧,前端验证一般指通过JS方式实现的,友好的,个性的验证方式,而后端验证是指 ...

  6. Android开发学习之路-使用Handler和Message更新UI

    在Android中,在非主线程中更新UI控件是不安全的,app在运行时会直接Crash,所以当我们需要在非主线程中更新UI控件,那么就需要用到Handler和Message来实现 Demo中,使用到一 ...

  7. 带你走近AngularJS - 创建自定义指令

    带你走近AngularJS系列: 带你走近AngularJS - 基本功能介绍 带你走近AngularJS - 体验指令实例 带你走近AngularJS - 创建自定义指令 ------------- ...

  8. DNS正向解析与反向解析

    DNS:(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网, 而不去记住能够被机器直接读取的IP数串.通过主机名,最 ...

  9. WPF入门教程系列六——布局介绍与Canvas(一)

    从这篇文章开始是对WPF中的界面如何布局做一个较简单的介绍,大家都知道:UI是做好一个软件很重要的因素,如果没有一个漂亮的UI,功能做的再好也无法吸引很多用户使用,而且没有漂亮的界面,那么普通用户会感 ...

  10. Python内建的对象列表

    Python内建的对象列表 刚写Python肯定会遇到这样的情况,想写些什么,但又不知从何写起... 在我看来问题在于我们不知道有什么东东可以拿来玩,这里列出Python的内建对象,稍微归类了一下,多 ...