【问题标题】:how to perform a runtime view update when using ResourceViewLocationProvider使用 ResourceViewLocationProvider 时如何执行运行时视图更新
【发布时间】:2019-02-18 09:45:08
【问题描述】:

m runing a nancyfx with owin on centos 6.5 with mono 5.10.0.140, I change the default ViewLocationProvider to ResourceViewLocationProvider for the default ViewLocationProvider causes memory leak of somekind after running for days, and the ResourceViewLocationProvider dont 也有同样的问题。我想像使用默认 ViewLocationProvider 一样热更新视图,但在谷歌搜索时似乎不可能。

我确实找到了部分解决方案,通过实现自定义 IViewLocator 和 IViewCache,我确实实现了一些热更新。但是除了那些丑陋的静态类之外,感觉并不对劲

//Here is what I did in the custom IViewLocator
//...class definition fallback viewlocator and other staffs
private static ConcurrentDictionary<string, ViewLocationResult> _cachedViewLocationResults;
//..other code      
    public ViewLocationResult LocateView(string viewName, NancyContext context)
    {
//...lock and others
        if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
        {
             return _cachedViewLocationResults[viewName];
        }
//...lock and others
       return fallbackViewLocator.LocateView(viewName, context);
   }
//...other class
//here is how I update Views
public static void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
{    
    lock (CacheLock)
    {
        if(_cachedViewLocationResults == null)_cachedViewLocationResults = new ConcurrentDictionary<string, ViewLocationResult>();
        foreach (var replace in replacements)
        {
           _cachedViewLocationResults.AddOrUpdate(replace.Key, x=>replacements[x], (x,y)=>y);
        }
   }
}

//IViewLocator 结束

//here is what I did in the custom IViewCache
//another static for ViewCache to tell if the view has been updated
public static List<ViewLocationResult> Exceptions { get; private set; }
//...some other code
//here is how I ignore the old cache
public TCompiledView GetOrAdd<TCompiledView>(ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
{
    if (Exceptions.Any(x=>x.Name == viewLocationResult.Name && x.Location == viewLocationResult.Location && x.Extension == viewLocationResult.Extension))
    {
        object old;
        this.cache.TryRemove(viewLocationResult, out old);
        Exceptions.Remove(viewLocationResult);
    }            
    return (TCompiledView)this.cache.GetOrAdd(viewLocationResult, x => valueFactory(x));
}

有了这些实现和引导程序上的一些设置以及一些 mysql 更新的路由器,我可以按照我想要的方式更新视图,但问题是: 1.现在我必须手动映射 ViewLocationResult 使用的所有位置、名称、扩展名,并且它们太多(243 ...),我想使用某种内置函数来识别更改,类似于 ViewLocationResult 的 IsStale 函数,但我不知道t know which and how... 2. those static class are ugly and I think it could be problematic but I didnt 知道替换它们的更好方法。

谁能给我一个提示,提前谢谢。

【问题讨论】:

    标签: c# razor static-methods nancy


    【解决方案1】:

    好吧,我终于自己弄清楚了如何做到这一点,以防万一其他人想使用与我相同的方法,这是您在内存中更新视图的方法:

    1. 制作界面
        public interface INewViewLocationResultProvider
        {
            bool UseCachedView { get; set; }
            ViewLocationResult GetNewerVersion(string viewName, NancyContext context);
            void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements);
        }
    
    1. 创建一个新的 ViewLocationResultProvider
    public class ConcurrentNewViewLocationResultProvider : INewViewLocationResultProvider
        {
            private Dictionary<string, ViewLocationResult> _cachedViewLocationResults;
            private readonly object _cacheLock = new object();
            public bool UseCachedView { get; set; }
    
            public ConcurrentNewViewLocationResultProvider()
            {
                lock (_cacheLock)
                {
                    if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
                }
            }
    
            public ViewLocationResult GetNewerVersion(string viewName, NancyContext context)
            {
                if (UseCachedView)
                {
                    if (Monitor.TryEnter(_cacheLock, TimeSpan.FromMilliseconds(20)))
                    {
                        try
                        {
                            if (_cachedViewLocationResults != null && _cachedViewLocationResults.ContainsKey(viewName))
                            {
                                return _cachedViewLocationResults[viewName];
                            }
                        }
                        finally
                        {
                            Monitor.Exit(_cacheLock);
                        }
                    }
                }
    
                return null;
            }
    
            public void UpdateCachedView(IDictionary<string, ViewLocationResult> replacements)
            {
                lock (_cacheLock)
                {
                    if(_cachedViewLocationResults == null)_cachedViewLocationResults = new Dictionary<string, ViewLocationResult>();
                    foreach (var replace in replacements)
                    {
                        if (_cachedViewLocationResults.ContainsKey(replace.Key))
                        {
                            _cachedViewLocationResults[replace.Key] = replace.Value;
                        }
                        else
                        {
                            _cachedViewLocationResults.Add(replace.Key,replace.Value);
                        }                   
                    }
                }
            }
        }
    
    1. 在您的引导程序中,使用 tinyIoc 或等效项注册新的 ViewLocationResultProvider
    container.Register<INewViewLocationResultProvider, ConcurrentNewViewLocationResultProvider>().AsSingleton();
    
    1. 从 ViewLocationResult 创建派生类
        public class OneTimeUsedViewLocationResult : ViewLocationResult
        {
            private bool _used = false;
            public OneTimeUsedViewLocationResult(string location, string name, string extension, Func<TextReader> contents)
                : base(location, name, extension, contents)
            {
            }
    
            public override bool IsStale()
            {
                if (_used) return false;
                _used = true;
                return true;
            }
        }
    
    1. 还有一个新的 IViewLocator:
    public class CachedViewLocator : IViewLocator
        {
            private readonly INewViewLocationResultProvider _newVersion;
            private readonly DefaultViewLocator _fallbackViewLocator;
            public CachedViewLocator(IViewLocationProvider viewLocationProvider, IEnumerable<IViewEngine> viewEngines, INewViewLocationResultProvider newVersion)
            {
                _fallbackViewLocator = new DefaultViewLocator(viewLocationProvider, viewEngines);
                _newVersion = newVersion;
            }
    
            public ViewLocationResult LocateView(string viewName, NancyContext context)
            {
                if (_newVersion.UseCachedView)
                {
                    var result = _newVersion.GetNewerVersion(viewName, context);
                    if (result != null) return result;
                }
                return _fallbackViewLocator.LocateView(viewName, context);
            }
    
            public IEnumerable<ViewLocationResult> GetAllCurrentlyDiscoveredViews()
            {
                return _fallbackViewLocator.GetAllCurrentlyDiscoveredViews();
            }
    
        }
    }
    
    1. 告诉 nancy 新的 ViewLocator
            protected override NancyInternalConfiguration InternalConfiguration
            {
                get
                {
                    return NancyInternalConfiguration.WithOverrides
                    (
                        nic =>
                        {
                            nic.ViewLocationProvider = typeof(ResourceViewLocationProvider);//use this or your equivalent
                            nic.ViewLocator = typeof(CachedViewLocator);
                        }
                    );
                }            
            }
    
    1. 然后您可以通过这样的 API 对其进行更新:
    public class YourModule : NancyModule
    {
        public YourModule(INewViewLocationResultProvider provider)
        {
           Get["/yourupdateinterface"] = param =>
           {
              if(!provider.UseCachedView) return HttpStatusCode.BadRequest;//in case you turn off the hot update
              //you can serialize your OneTimeUsedViewLocationResult with Newtonsoft.Json and store those views in any database, like mysql, redis, and load them here
              //data mock up
              TextReader tr = new StringReader(Resources.TextMain);                
              var vlr = new OneTimeUsedViewLocationResult("","index","cshtml",()=>tr);
              var dir = new Dictionary<string, ViewLocationResult> {{"index",vlr}};
              //mock up ends
              provider.UpdateCachedView(dir);
              return HttpStatusCode.OK;
           }
        }
    
    }
    

    注意:上面的那些代码并不能解决我的问题中提到的 ViewLocationResult 事物的所有位置、名称、扩展名的手动映射,但是由于我最终为我的大学构建了一个视图编辑器来上传他们的视图,所以我没有不需要再解决了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-12
      • 1970-01-01
      • 2021-05-08
      • 2018-02-10
      相关资源
      最近更新 更多