Как выполнить обновление представления во время выполнения при использовании ResourceViewLocationProvider - PullRequest
0 голосов
/ 16 января 2019

У меня 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 don т такая же проблема. Я хотел бы горячо обновить представления так же, как мы можем сделать с 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, я могу обновить View так, как я хочу, но вот проблема: 1. Теперь мне нужно вручную отобразить все Location, Name, Extension для ViewLocationResult для использования, и их слишком много (243 ...), я хотел бы использовать какую-то встроенную функцию для идентификации изменения, что-то вроде функции IsStale в ViewLocationResult, но я не знал t know which and how... 2. those static class are ugly and I think it could be problematic but I didn лучшего способа их замены.

Может ли кто-нибудь любезно дать мне подсказку, заранее спасибо.

1 Ответ

0 голосов
/ 18 февраля 2019

Ну, я наконец-то выяснил, как это сделать сам, на случай, если кто-то еще захочет использовать тот же метод, что и я. Вот как вы обновляете свое представление в памяти:

  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. В вашем Bootstrapper зарегистрируйте новый ViewLocationResultProvider с tinyIoc или эквивалентным
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. Расскажите Нэнси о новом 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, о которых идет речь в моем вопросе, но, поскольку я в итоге создаю редактор представлений для своих колледжей, чтобы загружать их представления, я не Не нужно больше это решать.

...