上次我们研究到LocalizationSettings,但在依赖注入里没有搜索到注册类。后来才发现,原来所有的继承自ISetting的类 都是通过如下注册的:
builder.RegisterSource(new SettingsSource());
此方法的声明如下:
// // 摘要: // Add a registration source to the container. // // 参数: // builder: // The builder to register the registration source via. // // registrationSource: // The registration source to add. public static void RegisterSource(this ContainerBuilder builder, IRegistrationSource registrationSource);
从声明可以看出 这是一个ContainerBuilder 的扩展方法,添加一个注册源到容器,而注册源就是IRegistrationSource ,IRegistrationSource 接口的声明如下:
namespace Autofac.Core { // 摘要: 当请求未注册的服务时,允许在运行时注册(懒惰注册,就是后注册吧) // Allows registrations to be made on-the-fly when unregistered services are // requested (lazy registrations.) public interface IRegistrationSource { // 摘要: 获得指示注册提供的源在其它组件上是否是1:1的适配器? // Gets whether the registrations provided by this source are 1:1 adapters on // top of other components (I.e. like Meta, Func or Owned.) bool IsAdapterForIndividualComponents { get; } // 摘要:为一个未注册的服务取回(检索)一个注册 ,到被使用的容器。 // Retrieve registrations for an unregistered service, to be used by the container. // // 参数: // service:服务的请求? // The service that was requested. // 一个函数它将为这个服务返回现有的注册。 // registrationAccessor: // A function that will return existing registrations for a service. // // 返回结果:注册提供的服务。 // Registrations providing the service. // // 备注:如果源查询一个服务S,同时它返回一个组件继承自s和s’,那么它将不会为S货S’再次查询。意思是说,如果这个源能返回其它的继承自s’。它应该返回他们。
翻译不懂啊。。。。 // If the source is queried for service s, and it returns a component that implements // both s and s', then it will not be queried again for either s or s'. This // means that if the source can return other implementations of s', it should // return these, plus the transitive closure of other components implementing // their additional services, along with the implementation of s. It is not // an error to return components that do not implement service. IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor); } }
翻译看不太懂,但主要就是实现RegistrationsFor方法,返回IComponentRegistration的集合,IComponentRegistration的声明:
namespace Autofac.Core { // 摘要: // Describes a logical component within the container. public interface IComponentRegistration : IDisposable { // 摘要: // The activator used to create instances. IInstanceActivator Activator { get; } // // 摘要: // A unique identifier for this component (shared in all sub-contexts.) This // value also appears in Services. Guid Id { get; } // // 摘要: // The lifetime associated with the component. IComponentLifetime Lifetime { get; } // // 摘要: // Additional data associated with the component. IDictionary<string, object> Metadata { get; } // // 摘要: // Whether the instances of the component should be disposed by the container. InstanceOwnership Ownership { get; } // // 摘要: // The services provided by the component. IEnumerable<Service> Services { get; } // // 摘要: // Whether the component instances are shared or not. InstanceSharing Sharing { get; } // // 摘要: // The component registration upon which this registration is based. IComponentRegistration Target { get; } // 摘要: // Fired when the activation process for a new instance is complete. event EventHandler<ActivatedEventArgs<object>> Activated; // // 摘要: // Fired when a new instance is being activated. The instance can be wrapped // or switched at this time by setting the Instance property in the provided // event arguments. event EventHandler<ActivatingEventArgs<object>> Activating; // // 摘要: // Fired when a new instance is required. The instance can be provided in order // to skip the regular activator, by setting the Instance property in the provided // event arguments. event EventHandler<PreparingEventArgs> Preparing; // 摘要: // Called by the container once an instance has been fully constructed, including // any requested objects that depend on the instance. // // 参数: // context: // The context in which the instance was activated. // // parameters: // The parameters supplied to the activator. // // instance: // The instance. [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This is the method that would raise the event.")] void RaiseActivated(IComponentContext context, IEnumerable<Parameter> parameters, object instance); // // 摘要: // Called by the container once an instance has been constructed. // // 参数: // context: // The context in which the instance was activated. // // parameters: // The parameters supplied to the activator. // // instance: // The instance. [SuppressMessage("Microsoft.Design", "CA1007:UseGenericsWhereAppropriate")] [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This is the method that would raise the event.")] [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "2#", Justification = "The method may change the object as part of activation.")] void RaiseActivating(IComponentContext context, IEnumerable<Parameter> parameters, ref object instance); // // 摘要: // Called by the container when an instance is required. // // 参数: // context: // The context in which the instance will be activated. // // parameters: // Parameters for activation. These may be modified by the event handler. [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This is the method that would raise the event.")] [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#", Justification = "The method may change the backing store of the parameter collection.")] void RaisePreparing(IComponentContext context, ref IEnumerable<Parameter> parameters); } }
不用管它,研究实现类:
public class SettingsSource : IRegistrationSource { static readonly MethodInfo BuildMethod = typeof(SettingsSource).GetMethod( "BuildRegistration", BindingFlags.Static | BindingFlags.NonPublic); public IEnumerable<IComponentRegistration> RegistrationsFor( Service service, Func<Service, IEnumerable<IComponentRegistration>> registrations) { var ts = service as TypedService; if (ts != null && typeof(ISettings).IsAssignableFrom(ts.ServiceType)) { var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); yield return (IComponentRegistration)buildMethod.Invoke(null, null); } } static IComponentRegistration BuildRegistration<TSettings>() where TSettings : ISettings, new() { return RegistrationBuilder .ForDelegate((c, p) => { var currentStoreId = c.Resolve<IStoreContext>().CurrentStore.Id; //uncomment the code below if you want load settings per store only when you have two stores installed. //var currentStoreId = c.Resolve<IStoreService>().GetAllStores().Count > 1 // c.Resolve<IStoreContext>().CurrentStore.Id : 0; //although it's better to connect to your database and execute the following SQL: //DELETE FROM [Setting] WHERE [StoreId] > 0 return c.Resolve<ISettingService>().LoadSetting<TSettings>(currentStoreId); }) .InstancePerLifetimeScope() .CreateRegistration(); } public bool IsAdapterForIndividualComponents { get { return false; } } }
RegistrationsFor方法的第一句:var ts = service as TypedService;
// 摘要标识服务相符的类型 // Identifies a service according to a type to which it can be assigned. public sealed class TypedService : Service, IServiceWithType, IEquatable<TypedService>
如果ts是ISetting的实例,执行
var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); yield return (IComponentRegistration)buildMethod.Invoke(null, null);
其中buildMethod声明如下:
static readonly MethodInfo BuildMethod = typeof(SettingsSource).GetMethod( "BuildRegistration", BindingFlags.Static | BindingFlags.NonPublic);
就是获取静态的或者非公共的 BuildRegistration方法,声明如下:
static IComponentRegistration BuildRegistration<TSettings>() where TSettings : ISettings, new() { return RegistrationBuilder .ForDelegate((c, p) => { var currentStoreId = c.Resolve<IStoreContext>().CurrentStore.Id; //uncomment the code below if you want load settings per store only when you have two stores installed. //var currentStoreId = c.Resolve<IStoreService>().GetAllStores().Count > 1 // c.Resolve<IStoreContext>().CurrentStore.Id : 0; //although it's better to connect to your database and execute the following SQL: //DELETE FROM [Setting] WHERE [StoreId] > 0 return c.Resolve<ISettingService>().LoadSetting<TSettings>(currentStoreId); }) .InstancePerLifetimeScope() .CreateRegistration(); }
这是核心的代码,通过ts.ServiceType传入泛型并调用该方法,返回IComponentRegistration。
第一句用到的静态类的说明如下:
// 摘要: 静态工厂方法,简化创建和处理IRegistrationBuilder // Static factory methods to simplify the creation and handling of IRegistrationBuilder{L,A,R}. public static class RegistrationBuilder
调用方法的声明如下:
// // 摘要: // Creates a registration builder for the provided delegate. // // 参数: // delegate: // Delegate to register. // // 类型参数: // T: // Instance type returned by delegate. // // 返回结果: // A registration builder. public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> ForDelegate<T>(Func<Autofac.IComponentContext, IEnumerable<Parameter>, T> @delegate);
继续往下看:通过var currentStoreId = c.Resolve<IStoreContext>().CurrentStore.Id; 返回的ID ,用ISettingService的实现 ,通过这个ID加载配置,先看是如何得到这个ID 的。
先得到IStoreContext的实现类。调用CurrentStore方法。代码如下:
public virtual Store CurrentStore { get { if (_cachedStore != null) return _cachedStore; //ty to determine the current store by HTTP_HOST当前存储是基于HTTP_HOST。 var host = _webHelper.ServerVariables("HTTP_HOST"); var allStores = _storeService.GetAllStores(); var store = allStores.FirstOrDefault(s => s.ContainsHostValue(host)); if (store == null) { //load the first found store store = allStores.FirstOrDefault(); } if (store == null) throw new Exception("No store could be loaded"); _cachedStore = store; return _cachedStore; } }
上面调用IWebHelper的方法。我们看他的实现类WebHelper:
/// <summary> /// Gets server variable by name /// </summary> /// <param name="name">Name</param> /// <returns>Server variable</returns> public virtual string ServerVariables(string name) { string result = string.Empty; try { if (!IsRequestAvailable(_httpContext)) return result; //put this method is try-catch //as described here http://www.nopcommerce.com/boards/t/21356/multi-store-roadmap-lets-discuss-update-done.aspx?p=6#90196 if (_httpContext.Request.ServerVariables[name] != null) { result = _httpContext.Request.ServerVariables[name]; } } catch { result = string.Empty; } return result; }
最终调用 _httpContext.Request.ServerVariables[name];获得Web服务器变量的集合的指定值。
这里看来没什么可研究了 。看下一句。找到ISettingService的实现类SettingService的方法LoadSetting:
/// <summary> /// Load settings /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="storeId">Store identifier for which settigns should be loaded</param> public virtual T LoadSetting<T>(int storeId = 0) where T : ISettings, new() { var settings = Activator.CreateInstance<T>(); foreach (var prop in typeof(T).GetProperties()) { // get properties we can read and write to if (!prop.CanRead || !prop.CanWrite) continue; var key = typeof(T).Name + "." + prop.Name; //load by store var setting = GetSettingByKey<string>(key, storeId: storeId, loadSharedValueIfNotFound: true); if (setting == null) continue; if (!CommonHelper.GetNopCustomTypeConverter(prop.PropertyType).CanConvertFrom(typeof(string))) continue; if (!CommonHelper.GetNopCustomTypeConverter(prop.PropertyType).IsValid(setting)) continue; object value = CommonHelper.GetNopCustomTypeConverter(prop.PropertyType).ConvertFromInvariantString(setting); //set property prop.SetValue(settings, value, null); } return settings; }
第一句获得T的实例,T是传过来类型 通过ts.ServiceType获得的。
然后遍历属性,通过如下方法获得
var setting = GetSettingByKey<string>(key, storeId: storeId, loadSharedValueIfNotFound: true);
后面是判断 如果值是空、或者不能从string转换。。。。就继续。。。
最后就是设置实例的值 然后返回实例。我们主要看上面那个方法,如何得到 值(setting) 的:
/// <summary> /// Get setting value by key /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">Key</param> /// <param name="defaultValue">Default value</param> /// <param name="storeId">Store identifier</param> /// <param name="loadSharedValueIfNotFound">A value indicating whether a shared (for all stores) value should be loaded if a value specific for a certain is not found</param> /// <returns>Setting value</returns> public virtual T GetSettingByKey<T>(string key, T defaultValue = default(T), int storeId = 0, bool loadSharedValueIfNotFound = false) { if (String.IsNullOrEmpty(key)) return defaultValue; var settings = GetAllSettingsCached(); key = key.Trim().ToLowerInvariant(); if (settings.ContainsKey(key)) { var settingsByKey = settings[key]; var setting = settingsByKey.FirstOrDefault(x => x.StoreId == storeId); //load shared value? if (setting == null && storeId > 0 && loadSharedValueIfNotFound) setting = settingsByKey.FirstOrDefault(x => x.StoreId == 0); if (setting != null) return CommonHelper.To<T>(setting.Value); } return defaultValue; }
第一句如果KEY为空 返回类型的默认值,通过调用可知 是string.
然后调用GetAllSettingsCached方法:
/// <summary> /// Gets all settings /// </summary> /// <returns>Setting collection</returns> protected virtual IDictionary<string, IList<SettingForCaching>> GetAllSettingsCached() { //cache string key = string.Format(SETTINGS_ALL_KEY); return _cacheManager.Get(key, () => { //we use no tracking here for performance optimization //anyway records are loaded only for read-only operations var query = from s in _settingRepository.TableNoTracking orderby s.Name, s.StoreId select s; var settings = query.ToList(); var dictionary = new Dictionary<string, IList<SettingForCaching>>(); foreach (var s in settings) { var resourceName = s.Name.ToLowerInvariant(); var settingForCaching = new SettingForCaching { Id = s.Id, Name = s.Name, Value = s.Value, StoreId = s.StoreId }; if (!dictionary.ContainsKey(resourceName)) { //first setting dictionary.Add(resourceName, new List<SettingForCaching> { settingForCaching }); } else { //already added //most probably it's the setting with the same name but for some certain store (storeId > 0) dictionary[resourceName].Add(settingForCaching); } } return dictionary; }); }
获得KEY ,如下是KEY的声明:
private const string SETTINGS_ALL_KEY = "Nop.setting.all";
调用_cacheManager.Get方法 获得键值对集合。刚才找ICacheManager的实现类,结果找不到对应的GET方法,原来这个GET方法是扩展方法。如下:
namespace Nop.Core.Caching { /// <summary> /// Extensions /// </summary> public static class CacheExtensions { /// <summary> /// Variable (lock) to support thread-safe /// </summary> private static readonly object _syncObject = new object(); /// <summary> /// Get a cached item. If it's not in the cache yet, then load and cache it /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="cacheManager">Cache manager</param> /// <param name="key">Cache key</param> /// <param name="acquire">Function to load item if it's not in the cache yet</param> /// <returns>Cached item</returns> public static T Get<T>(this ICacheManager cacheManager, string key, Func<T> acquire) { return Get(cacheManager, key, 60, acquire); } /// <summary> /// Get a cached item. If it's not in the cache yet, then load and cache it /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="cacheManager">Cache manager</param> /// <param name="key">Cache key</param> /// <param name="cacheTime">Cache time in minutes (0 - do not cache)</param> /// <param name="acquire">Function to load item if it's not in the cache yet</param> /// <returns>Cached item</returns> public static T Get<T>(this ICacheManager cacheManager, string key, int cacheTime, Func<T> acquire) { lock (_syncObject) { if (cacheManager.IsSet(key)) { return cacheManager.Get<T>(key); } var result = acquire(); if (cacheTime > 0) cacheManager.Set(key, result, cacheTime); return result; } } } }
锁定,然后调用IsSet(key).
/// <summary>获取一个值,指示是否与指定键相关联的值缓存。, /// Gets a value indicating whether the value associated with the specified key is cached /// </summary> /// <param name="key">key</param> /// <returns>Result</returns> bool IsSet(string key);
如果有就返回,如果木有,就调用acquire方法并通过缓存管理类进行设置缓存 缓存时间60分。
我们再简单看一下缓存类是如何实现的MemoryCacheManager:
其实就是调用.net内部的缓存类实现的:
namespace Nop.Core.Caching { /// <summary> /// Represents a manager for caching between HTTP requests (long term caching) /// </summary> public partial class MemoryCacheManager : ICacheManager { protected ObjectCache Cache { get { return MemoryCache.Default; } } /// <summary> /// Gets or sets the value associated with the specified key. /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">The key of the value to get.</param> /// <returns>The value associated with the specified key.</returns> public virtual T Get<T>(string key) { return (T)Cache[key]; } /// <summary> /// Adds the specified key and object to the cache. /// </summary> /// <param name="key">key</param> /// <param name="data">Data</param> /// <param name="cacheTime">Cache time</param> public virtual void Set(string key, object data, int cacheTime) { if (data == null) return; var policy = new CacheItemPolicy(); policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); Cache.Add(new CacheItem(key, data), policy); } /// <summary> /// Gets a value indicating whether the value associated with the specified key is cached /// </summary> /// <param name="key">key</param> /// <returns>Result</returns> public virtual bool IsSet(string key) { return (Cache.Contains(key)); } /// <summary> /// Removes the value with the specified key from the cache /// </summary> /// <param name="key">/key</param> public virtual void Remove(string key) { Cache.Remove(key); } /// <summary> /// Removes items by pattern /// </summary> /// <param name="pattern">pattern</param> public virtual void RemoveByPattern(string pattern) { var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); var keysToRemove = new List<String>(); foreach (var item in Cache) if (regex.IsMatch(item.Key)) keysToRemove.Add(item.Key); foreach (string key in keysToRemove) { Remove(key); } } /// <summary> /// Clear all cache data /// </summary> public virtual void Clear() { foreach (var item in Cache) Remove(item.Key); } } }