zoukankan      html  css  js  c++  java
  • NOP源码分析六--实体、数据的分层与处理。

    也许我应该先研究下架构,但现在还是接着研究NOP吧,不能半途而废啊。

    之前粗略研究过如何添加一个属性,http://www.cnblogs.com/runit/p/3842611.html,基本一样。我们以ScheduleTask为例。

    下面是一个实体类,没有多余功能,就是提供了实体类的各个属性。

    namespace Nop.Core.Domain.Tasks
    {
        public class ScheduleTask : BaseEntity
        {
            /// <summary>
            /// Gets or sets the name
            /// </summary>
            public string Name { get; set; }
    
            /// <summary>
            /// Gets or sets the run period (in seconds)
            /// </summary>
            public int Seconds { get; set; }
    
            /// <summary>
            /// Gets or sets the type of appropriate ITask class
            /// </summary>
            public string Type { get; set; }
    
            /// <summary>
            /// Gets or sets the value indicating whether a task is enabled
            /// </summary>
            public bool Enabled { get; set; }
    
            /// <summary>
            /// Gets or sets the value indicating whether a task should be stopped on some error
            /// </summary>
            public bool StopOnError { get; set; }
    
            public DateTime? LastStartUtc { get; set; }
    
            public DateTime? LastEndUtc { get; set; }
    
            public DateTime? LastSuccessUtc { get; set; }
        }
    }

    下面就是关系映射类,这个类就用到了上面的类,把上面的类映射到数据库相应的表名,以及Key是哪一个,哪些属性不能为空,以及长度上面的都在这里设定。

    前面我们提到的NopObjectContext(DBContext) 就是通过反射得到所有的NopEntityTypeConfiguration子类,并进行增删改查的。

    namespace Nop.Data.Mapping.Tasks
    {
        public partial class ScheduleTaskMap : NopEntityTypeConfiguration<ScheduleTask>
        {
            public ScheduleTaskMap()
            {
                this.ToTable("ScheduleTask");
                this.HasKey(t => t.Id);
                this.Property(t => t.Name).IsRequired();
                this.Property(t => t.Type).IsRequired();
            }
        }
    }

    上面2个是底层的,下面我们介绍界面层的。代码如下:

    namespace Nop.Admin.Models.Tasks
    {
        [Validator(typeof(ScheduleTaskValidator))]
        public partial class ScheduleTaskModel : BaseNopEntityModel
        {
            [NopResourceDisplayName("Admin.System.ScheduleTasks.Name")]
            [AllowHtml]
            public string Name { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.Seconds")]
            public int Seconds { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.Enabled")]
            public bool Enabled { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.StopOnError")]
            public bool StopOnError { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.LastStart")]
            public string LastStartUtc { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.LastEnd")]
            public string LastEndUtc { get; set; }
    
            [NopResourceDisplayName("Admin.System.ScheduleTasks.LastSuccess")]
            public string LastSuccessUtc { get; set; }
        }
    }

    这里我们看到,这个类做了Nop.Core.Domain实体类的相同属性,也就是重复了,这应该就是为了解耦、分离。

    同时他还有NopResourceDisplayName属性,用于告诉界面如何显示,AllowHtml,是否允许HTML等。

    在类的开始 有如下代码:[Validator(typeof(ScheduleTaskValidator))] ,这个应该就是验证类的了。我们看Ixia这个类

    namespace Nop.Admin.Validators.Tasks
    {
        public class ScheduleTaskValidator : BaseNopValidator<ScheduleTaskModel>
        {
            public ScheduleTaskValidator(ILocalizationService localizationService)
            {
                RuleFor(x => x.Name).NotEmpty().WithMessage(localizationService.GetResource("Admin.System.ScheduleTasks.Name.Required"));
                RuleFor(x => x.Seconds).GreaterThan(0).WithMessage(localizationService.GetResource("Admin.System.ScheduleTasks.Seconds.Positive"));
            }
        }
    }

    果然是验证代码,如果验证失败,则返回什么等。用到的验证是FluentValidation,http://www.cnblogs.com/whitewolf/archive/2012/05/27/2520593.html

    http://www.cnblogs.com/jes_shaw/p/3380940.html,上面是学习的连接,验证,及验证集成类的代码如下,就是一个FluentValidation验证。

    namespace Nop.Admin.Validators.Tasks
    {
        public class ScheduleTaskValidator : BaseNopValidator<ScheduleTaskModel>
        {
            public ScheduleTaskValidator(ILocalizationService localizationService)
            {
                RuleFor(x => x.Name).NotEmpty().WithMessage(localizationService.GetResource("Admin.System.ScheduleTasks.Name.Required"));
                RuleFor(x => x.Seconds).GreaterThan(0).WithMessage(localizationService.GetResource("Admin.System.ScheduleTasks.Seconds.Positive"));
            }
        }
    }

    继承自如下类,如下类继承自FluentValidation的标准验证抽象类,实现构造内验证。

    namespace Nop.Web.Framework.Validators
    {
        public abstract class BaseNopValidator<T> : AbstractValidator<T> where T : class
        {
            protected BaseNopValidator()
            {
                PostInitialize();
            }
    
            /// <summary>
            /// Developers can override this method in custom partial classes
            /// in order to add some custom initialization code to constructors
            /// </summary>
            protected virtual void PostInitialize()
            {
                
            }
        }
    }

    我们注意到,验证类用到了ILocalizationService类,通过DependencyRegistrar类查到其实现类是LocalizationService,这个类还挺复杂,慢慢来。

    namespace Nop.Services.Localization
    {
        /// <summary>
        /// Provides information about localization
        /// </summary>
        public partial class LocalizationService : ILocalizationService
        {
            #region Constants
    
            /// <summary>
            /// Key for caching
            /// </summary>
            /// <remarks>
            /// {0} : language ID
            /// </remarks>
            private const string LOCALSTRINGRESOURCES_ALL_KEY = "Nop.lsr.all-{0}";
            /// <summary>
            /// Key for caching
            /// </summary>
            /// <remarks>
            /// {0} : language ID
            /// {1} : resource key
            /// </remarks>
            private const string LOCALSTRINGRESOURCES_BY_RESOURCENAME_KEY = "Nop.lsr.{0}-{1}";
            /// <summary>
            /// Key pattern to clear cache
            /// </summary>
            private const string LOCALSTRINGRESOURCES_PATTERN_KEY = "Nop.lsr.";
    
            #endregion
    
            #region Fields
    
            private readonly IRepository<LocaleStringResource> _lsrRepository;
            private readonly IWorkContext _workContext;
            private readonly ILogger _logger;
            private readonly ILanguageService _languageService;
            private readonly ICacheManager _cacheManager;
            private readonly IDataProvider _dataProvider;
            private readonly IDbContext _dbContext;
            private readonly CommonSettings _commonSettings;
            private readonly LocalizationSettings _localizationSettings;
            private readonly IEventPublisher _eventPublisher;
    
            #endregion
    
            #region Ctor
    
            /// <summary>
            /// Ctor
            /// </summary>
            /// <param name="cacheManager">Cache manager</param>
            /// <param name="logger">Logger</param>
            /// <param name="workContext">Work context</param>
            /// <param name="lsrRepository">Locale string resource repository</param>
            /// <param name="languageService">Language service</param>
            /// <param name="dataProvider">Data provider</param>
            /// <param name="dbContext">Database Context</param>
            /// <param name="commonSettings">Common settings</param>
            /// <param name="localizationSettings">Localization settings</param>
            /// <param name="eventPublisher">Event published</param>
            public LocalizationService(ICacheManager cacheManager,
                ILogger logger, IWorkContext workContext,
                IRepository<LocaleStringResource> lsrRepository, 
                ILanguageService languageService,
                IDataProvider dataProvider, IDbContext dbContext, CommonSettings commonSettings,
                LocalizationSettings localizationSettings, IEventPublisher eventPublisher)
            {
                this._cacheManager = cacheManager;
                this._logger = logger;
                this._workContext = workContext;
                this._lsrRepository = lsrRepository;
                this._languageService = languageService;
                this._dataProvider = dataProvider;
                this._dbContext = dbContext;
                this._commonSettings = commonSettings;
                this._dataProvider = dataProvider;
                this._dbContext = dbContext;
                this._commonSettings = commonSettings;
                this._localizationSettings = localizationSettings;
                this._eventPublisher = eventPublisher;
            }
    
            #endregion
    
            #region Methods
    
            /// <summary>
            /// Deletes a locale string resource
            /// </summary>
            /// <param name="localeStringResource">Locale string resource</param>
            public virtual void DeleteLocaleStringResource(LocaleStringResource localeStringResource)
            {
                if (localeStringResource == null)
                    throw new ArgumentNullException("localeStringResource");
    
                _lsrRepository.Delete(localeStringResource);
    
                //cache
                _cacheManager.RemoveByPattern(LOCALSTRINGRESOURCES_PATTERN_KEY);
    
                //event notification
                _eventPublisher.EntityDeleted(localeStringResource);
            }
    
            /// <summary>
            /// Gets a locale string resource
            /// </summary>
            /// <param name="localeStringResourceId">Locale string resource identifier</param>
            /// <returns>Locale string resource</returns>
            public virtual LocaleStringResource GetLocaleStringResourceById(int localeStringResourceId)
            {
                if (localeStringResourceId == 0)
                    return null;
    
                return _lsrRepository.GetById(localeStringResourceId);
            }
    
            /// <summary>
            /// Gets a locale string resource
            /// </summary>
            /// <param name="resourceName">A string representing a resource name</param>
            /// <returns>Locale string resource</returns>
            public virtual LocaleStringResource GetLocaleStringResourceByName(string resourceName)
            {
                if (_workContext.WorkingLanguage != null)
                    return GetLocaleStringResourceByName(resourceName, _workContext.WorkingLanguage.Id);
    
                return null;
            }
    
            /// <summary>
            /// Gets a locale string resource
            /// </summary>
            /// <param name="resourceName">A string representing a resource name</param>
            /// <param name="languageId">Language identifier</param>
            /// <param name="logIfNotFound">A value indicating whether to log error if locale string resource is not found</param>
            /// <returns>Locale string resource</returns>
            public virtual LocaleStringResource GetLocaleStringResourceByName(string resourceName, int languageId,
                bool logIfNotFound = true)
            {
                var query = from lsr in _lsrRepository.Table
                            orderby lsr.ResourceName
                            where lsr.LanguageId == languageId && lsr.ResourceName == resourceName
                            select lsr;
                var localeStringResource = query.FirstOrDefault();
    
                if (localeStringResource == null && logIfNotFound)
                    _logger.Warning(string.Format("Resource string ({0}) not found. Language ID = {1}", resourceName, languageId));
                return localeStringResource;
            }
    
            /// <summary>
            /// Gets all locale string resources by language identifier
            /// </summary>
            /// <param name="languageId">Language identifier</param>
            /// <returns>Locale string resources</returns>
            public virtual IList<LocaleStringResource> GetAllResources(int languageId)
            {
                var query = from l in _lsrRepository.Table
                            orderby l.ResourceName
                            where l.LanguageId == languageId
                            select l;
                var locales = query.ToList();
                return locales;
            }
    
            /// <summary>
            /// Inserts a locale string resource
            /// </summary>
            /// <param name="localeStringResource">Locale string resource</param>
            public virtual void InsertLocaleStringResource(LocaleStringResource localeStringResource)
            {
                if (localeStringResource == null)
                    throw new ArgumentNullException("localeStringResource");
                
                _lsrRepository.Insert(localeStringResource);
    
                //cache
                _cacheManager.RemoveByPattern(LOCALSTRINGRESOURCES_PATTERN_KEY);
    
                //event notification
                _eventPublisher.EntityInserted(localeStringResource);
            }
    
            /// <summary>
            /// Updates the locale string resource
            /// </summary>
            /// <param name="localeStringResource">Locale string resource</param>
            public virtual void UpdateLocaleStringResource(LocaleStringResource localeStringResource)
            {
                if (localeStringResource == null)
                    throw new ArgumentNullException("localeStringResource");
    
                _lsrRepository.Update(localeStringResource);
    
                //cache
                _cacheManager.RemoveByPattern(LOCALSTRINGRESOURCES_PATTERN_KEY);
    
                //event notification
                _eventPublisher.EntityUpdated(localeStringResource);
            }
    
            /// <summary>
            /// Gets all locale string resources by language identifier
            /// </summary>
            /// <param name="languageId">Language identifier</param>
            /// <returns>Locale string resources</returns>
            public virtual Dictionary<string, KeyValuePair<int,string>> GetAllResourceValues(int languageId)
            {
                string key = string.Format(LOCALSTRINGRESOURCES_ALL_KEY, languageId);
                return _cacheManager.Get(key, () =>
                {
                    //we use no tracking here for performance optimization
                    //anyway records are loaded only for read-only operations
                    var query = from l in _lsrRepository.TableNoTracking
                                orderby l.ResourceName
                                where l.LanguageId == languageId
                                select l;
                    var locales = query.ToList();
                    //format: <name, <id, value>>
                    var dictionary = new Dictionary<string, KeyValuePair<int, string>>();
                    foreach (var locale in locales)
                    {
                        var resourceName = locale.ResourceName.ToLowerInvariant();
                        if (!dictionary.ContainsKey(resourceName))
                            dictionary.Add(resourceName, new KeyValuePair<int, string>(locale.Id, locale.ResourceValue));
                    }
                    return dictionary;
                });
            }
    
            /// <summary>
            /// Gets a resource string based on the specified ResourceKey property.
            /// </summary>
            /// <param name="resourceKey">A string representing a ResourceKey.</param>
            /// <returns>A string representing the requested resource string.</returns>
            public virtual string GetResource(string resourceKey)
            {
                if (_workContext.WorkingLanguage != null)
                    return GetResource(resourceKey, _workContext.WorkingLanguage.Id);
                
                return "";
            }
            
            /// <summary>
            /// Gets a resource string based on the specified ResourceKey property.
            /// </summary>
            /// <param name="resourceKey">A string representing a ResourceKey.</param>
            /// <param name="languageId">Language identifier</param>
            /// <param name="logIfNotFound">A value indicating whether to log error if locale string resource is not found</param>
            /// <param name="defaultValue">Default value</param>
            /// <param name="returnEmptyIfNotFound">A value indicating whether an empty string will be returned if a resource is not found and default value is set to empty string</param>
            /// <returns>A string representing the requested resource string.</returns>
            public virtual string GetResource(string resourceKey, int languageId,
                bool logIfNotFound = true, string defaultValue = "", bool returnEmptyIfNotFound = false)
            {
                string result = string.Empty;
                if (resourceKey == null)
                    resourceKey = string.Empty;
                resourceKey = resourceKey.Trim().ToLowerInvariant();
                if (_localizationSettings.LoadAllLocaleRecordsOnStartup)
                {
                    //load all records (we know they are cached)
                    var resources = GetAllResourceValues(languageId);
                    if (resources.ContainsKey(resourceKey))
                    {
                        result = resources[resourceKey].Value;
                    }
                }
                else
                {
                    //gradual loading
                    string key = string.Format(LOCALSTRINGRESOURCES_BY_RESOURCENAME_KEY, languageId, resourceKey);
                    string lsr = _cacheManager.Get(key, () =>
                    {
                        var query = from l in _lsrRepository.Table
                                    where l.ResourceName == resourceKey
                                    && l.LanguageId == languageId
                                    select l.ResourceValue;
                        return query.FirstOrDefault();
                    });
    
                    if (lsr != null) 
                        result = lsr;
                }
                if (String.IsNullOrEmpty(result))
                {
                    if (logIfNotFound)
                        _logger.Warning(string.Format("Resource string ({0}) is not found. Language ID = {1}", resourceKey, languageId));
                    
                    if (!String.IsNullOrEmpty(defaultValue))
                    {
                        result = defaultValue;
                    }
                    else
                    {
                        if (!returnEmptyIfNotFound)
                            result = resourceKey;
                    }
                }
                return result;
            }
    
            /// <summary>
            /// Export language resources to xml
            /// </summary>
            /// <param name="language">Language</param>
            /// <returns>Result in XML format</returns>
            public virtual string ExportResourcesToXml(Language language)
            {
                if (language == null)
                    throw new ArgumentNullException("language");
                var sb = new StringBuilder();
                var stringWriter = new StringWriter(sb);
                var xmlWriter = new XmlTextWriter(stringWriter);
                xmlWriter.WriteStartDocument();
                xmlWriter.WriteStartElement("Language");
                xmlWriter.WriteAttributeString("Name", language.Name);
    
    
                var resources = GetAllResources(language.Id);
                foreach (var resource in resources)
                {
                    xmlWriter.WriteStartElement("LocaleResource");
                    xmlWriter.WriteAttributeString("Name", resource.ResourceName);
                    xmlWriter.WriteElementString("Value", null, resource.ResourceValue);
                    xmlWriter.WriteEndElement();
                }
    
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndDocument();
                xmlWriter.Close();
                return stringWriter.ToString();
            }
    
            /// <summary>
            /// Import language resources from XML file
            /// </summary>
            /// <param name="language">Language</param>
            /// <param name="xml">XML</param>
            public virtual void ImportResourcesFromXml(Language language, string xml)
            {
                if (language == null)
                    throw new ArgumentNullException("language");
    
                if (String.IsNullOrEmpty(xml))
                    return;
                if (_commonSettings.UseStoredProceduresIfSupported && _dataProvider.StoredProceduredSupported)
                {
                    //SQL 2005 insists that your XML schema incoding be in UTF-16.
                    //Otherwise, you'll get "XML parsing: line 1, character XXX, unable to switch the encoding"
                    //so let's remove XML declaration
                    var inDoc = new XmlDocument();
                    inDoc.LoadXml(xml);
                    var sb = new StringBuilder();
                    using (var xWriter = XmlWriter.Create(sb, new XmlWriterSettings { OmitXmlDeclaration = true }))
                    {
                        inDoc.Save(xWriter);
                        xWriter.Close();
                    }
                    var outDoc = new XmlDocument();
                    outDoc.LoadXml(sb.ToString());
                    xml = outDoc.OuterXml;
    
                    //stored procedures are enabled and supported by the database.
                    var pLanguageId = _dataProvider.GetParameter();
                    pLanguageId.ParameterName = "LanguageId";
                    pLanguageId.Value = language.Id;
                    pLanguageId.DbType = DbType.Int32;
    
                    var pXmlPackage = _dataProvider.GetParameter();
                    pXmlPackage.ParameterName = "XmlPackage";
                    pXmlPackage.Value = xml;
                    pXmlPackage.DbType = DbType.Xml;
    
                    //long-running query. specify timeout (600 seconds)
                    _dbContext.ExecuteSqlCommand("EXEC [LanguagePackImport] @LanguageId, @XmlPackage", false, 600, pLanguageId, pXmlPackage);
                }
                else
                {
                    //stored procedures aren't supported
                    var xmlDoc = new XmlDocument();
                    xmlDoc.LoadXml(xml);
    
                    var nodes = xmlDoc.SelectNodes(@"//Language/LocaleResource");
                    foreach (XmlNode node in nodes)
                    {
                        string name = node.Attributes["Name"].InnerText.Trim();
                        string value = "";
                        var valueNode = node.SelectSingleNode("Value");
                        if (valueNode != null)
                            value = valueNode.InnerText;
    
                        if (String.IsNullOrEmpty(name))
                            continue;
    
                        //do not use "Insert"/"Update" methods because they clear cache
                        //let's bulk insert
                        var resource = language.LocaleStringResources.FirstOrDefault(x => x.ResourceName.Equals(name, StringComparison.InvariantCultureIgnoreCase));
                        if (resource != null)
                            resource.ResourceValue = value;
                        else
                        {
                            language.LocaleStringResources.Add(
                                new LocaleStringResource
                                {
                                    ResourceName = name,
                                    ResourceValue = value
                                });
                        }
                    }
                    _languageService.UpdateLanguage(language);
                }
    
                //clear cache
                _cacheManager.RemoveByPattern(LOCALSTRINGRESOURCES_PATTERN_KEY);
            }
    
            #endregion
        }
    }

    好复杂,不是么?我们看一下他的构造如下:

    public LocalizationService(ICacheManager cacheManager,
                ILogger logger, IWorkContext workContext,
                IRepository<LocaleStringResource> lsrRepository, 
                ILanguageService languageService,
                IDataProvider dataProvider, IDbContext dbContext, CommonSettings commonSettings,
                LocalizationSettings localizationSettings, IEventPublisher eventPublisher)
            {
                this._cacheManager = cacheManager;
                this._logger = logger;
                this._workContext = workContext;
                this._lsrRepository = lsrRepository;
                this._languageService = languageService;
                this._dataProvider = dataProvider;
                this._dbContext = dbContext;
                this._commonSettings = commonSettings;
                this._dataProvider = dataProvider;
                this._dbContext = dbContext;
                this._commonSettings = commonSettings;
                this._localizationSettings = localizationSettings;
                this._eventPublisher = eventPublisher;
            }

    东西太多,比如:缓存管理、日志、工作上下文、存储仓库、数据提供、。。。。很多我们都没有研究,看一下就行了。我们略过这里。用到什么再研究也不晚。我们找到GetResource方法:

    /// <summary>
            /// Gets a resource string based on the specified ResourceKey property.
            /// </summary>
            /// <param name="resourceKey">A string representing a ResourceKey.</param>
            /// <returns>A string representing the requested resource string.</returns>
            public virtual string GetResource(string resourceKey)
            {
                if (_workContext.WorkingLanguage != null)
                    return GetResource(resourceKey, _workContext.WorkingLanguage.Id);
                
                return "";
            }

    用到了IWorkContext 查找依赖注册的是:WebWorkContext,查看代码 构造函数依然复杂,略过。。。。查看WorkingLanguage方法。

    /// <summary>
            /// Get or set current user working language
            /// </summary>
            public virtual Language WorkingLanguage
            {
                get
                {
                    if (_cachedLanguage != null)
                        return _cachedLanguage;
                    
                    Language detectedLanguage = null;
                    if (_localizationSettings.SeoFriendlyUrlsForLanguagesEnabled)
                    {
                        //get language from URL
                        detectedLanguage = GetLanguageFromUrl();
                    }
                    if (detectedLanguage == null && _localizationSettings.AutomaticallyDetectLanguage)
                    {
                        //get language from browser settings
                        //but we do it only once
                        if (!this.CurrentCustomer.GetAttribute<bool>(SystemCustomerAttributeNames.LanguageAutomaticallyDetected, 
                            _genericAttributeService, _storeContext.CurrentStore.Id))
                        {
                            detectedLanguage = GetLanguageFromBrowserSettings();
                            if (detectedLanguage != null)
                            {
                                _genericAttributeService.SaveAttribute(this.CurrentCustomer, SystemCustomerAttributeNames.LanguageAutomaticallyDetected,
                                     true, _storeContext.CurrentStore.Id);
                            }
                        }
                    }
                    if (detectedLanguage != null)
                    {
                        //the language is detected. now we need to save it
                        if (this.CurrentCustomer.GetAttribute<int>(SystemCustomerAttributeNames.LanguageId,
                            _genericAttributeService, _storeContext.CurrentStore.Id) != detectedLanguage.Id)
                        {
                            _genericAttributeService.SaveAttribute(this.CurrentCustomer, SystemCustomerAttributeNames.LanguageId,
                                detectedLanguage.Id, _storeContext.CurrentStore.Id);
                        }
                    }
    
                    var allLanguages = _languageService.GetAllLanguages(storeId: _storeContext.CurrentStore.Id);
                    //find current customer language
                    var languageId = this.CurrentCustomer.GetAttribute<int>(SystemCustomerAttributeNames.LanguageId,
                        _genericAttributeService, _storeContext.CurrentStore.Id);
                    var language = allLanguages.FirstOrDefault(x => x.Id == languageId);
                    if (language == null)
                    {
                        //it not specified, then return the first (filtered by current store) found one
                        language = allLanguages.FirstOrDefault();
                    }
                    if (language == null)
                    {
                        //it not specified, then return the first found one
                        language = _languageService.GetAllLanguages().FirstOrDefault();
                    }
    
                    //cache
                    _cachedLanguage = language;
                    return _cachedLanguage;
                }
                set
                {
                    var languageId = value != null ? value.Id : 0;
                    _genericAttributeService.SaveAttribute(this.CurrentCustomer,
                        SystemCustomerAttributeNames.LanguageId,
                        languageId, _storeContext.CurrentStore.Id);
    
                    //reset cache
                    _cachedLanguage = null;
                }
            }

    开始是缓存,先略过吧。LocalizationSettings在依赖注册里竟然没有。。。。断了。。

  • 相关阅读:
    Python之路Day14
    Python 之路Day13
    PYthon之路Day12
    三层与“养猪”
    参数化查询---解决sql漏洞注入
    关于在asp.net中的调试
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(21)-权限管理系统-跑通整个系统
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(20)-权限管理系统-根据权限获取菜单
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(19)-权限管理系统-用户登录
    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(18)-权限管理系统-表数据
  • 原文地址:https://www.cnblogs.com/runit/p/4186633.html
Copyright © 2011-2022 走看看