• NOP源码分析七---继续


    上次我们研究到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);
            }
        }
    }
  • 相关阅读:
    汕头市队赛 SRM1X T1
    夏令营
    路上路径求和
    USACO 刷题记录bzoj
    整除
    Xor路
    超低延时安防直播系统webrtc-client在浏览器播放没有音频的问题如何排查解决?
    如何使用TSINGSEE青犀视频同屏功能组件EasyScreenLive通过sdk推流到腾讯云直播?
    网络穿透/云端组网/视频拉转推服务EasyNTS上云网关运维中数据库检测功能的介绍
    【解决方案】变电站智慧消防如何实现远程集中监控?EasyCVR变电站安全综合管理系统搭建
  • 原文地址:https://www.cnblogs.com/runit/p/4193857.html
Copyright © 2020-2023  润新知