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);
        }
    }
}
posted @ 2019-11-16 08:53  C#生命周期  阅读(291)  评论(0编辑  收藏  举报