Кэш веб-сервисов для длительных процессов Идеи дизайна

Джон и Тим уже объяснили, почему это не работает.

Предполагая, что код фильтра внутри Filter не является тривиальным, вы можете изменить Filter так, чтобы он возвращал выражение, которое EF может перевести .

Предположим, что у вас есть этот код:

context.Table.Where(x => x.Name.Length > 500);

Теперь вы можете создать метод, который возвращает это выражение:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length)
{
    return x => x.Name.Length > length;
}

Использование было бы похоже это:

context.Table.Where(FilterByNameLength(500));

Выражение, которое вы строите внутри FilterByNameLength, может быть сколь угодно сложным, если вы можете передать его непосредственно на Where.

-1
задан Richard Watts 23 January 2019 в 20:24
поделиться

5 ответов

Вы можете установить политики истечения срока действия для кэша и загрузки по требованию. Вы также можете иметь более одного уровня кэширования, один в качестве распределенного и один локальный в качестве локальной версии всегда будет самым быстрым. Я предпочитаю загрузку по запросу, чем опрос, так как при опросе вы всегда обновляете данные, даже когда никто не слушает. Если вы используете многоуровневую систему, вы можете опросить распределенный кеш и загрузить локальный кеш по требованию. Это примерно так же эффективно, как вы получите.

0
ответ дан Vincent 23 January 2019 в 20:24
поделиться

Вы можете кэшировать свои данные с помощью Redis, а если данные меняются, вы можете обновить кеш с помощью зависимости sql. Я думаю, вам просто нужны Redis и sql зависимости.

0
ответ дан Abdulsamet Şentürk 23 January 2019 в 20:24
поделиться

Если бы я реализовал ваш сценарий, я бы не предпочел опрос, поскольку меньше смысла делать повторные вызовы в службу и сохранять занятость службы / сети. Кроме того, если вам нужно внедрить новый клиент, вам придется снова выполнить опрос.

Вместо этого используйте кэширование на основе словаря в статическом классе. Вы используете существующие библиотеки, такие как CacheManager. Общая идея заключается в создании ключа с использованием параметров, используемых для вызова службы. Затем сохраните результаты, полученные после обработки, в ConcurrentDictionary, который сам заботится о доступе для нескольких потоков.

Очистить сохраненный результат только после обновления базовой таблицы базы данных (?) Или, если это слишком сложно, через каждые 30 секунд.

Кроме того, вы можете также внедрить аналогичный механизм кэширования на уровне доступа к данным, чтобы сократить 4 секунды, которые у вас есть в настоящее время. Очистите кэшированные данные после изменений базовых данных (операции добавления, обновления, удаления, вставки)!

0
ответ дан bit 23 January 2019 в 20:24
поделиться

Мы реализуем шаблон опроса в ASP.NET, который может применяться к вашему варианту использования.

В нашем Global.ashx мы имеем:

protected void Application_Start(object sender, EventArgs e)
{
  ConfigurationMonitor.Start();
}

где ConfiguraitonMonitor выглядит примерно так:

public static class ConfigurationMonitor
{
    private static readonly Timer timer = new Timer(PollingInterval);
    public static bool MonitoringEnabled
    {
        get
        {
            return ((timer.Enabled || Working) ? true : false);
        }
    }
    private static int _PollingInterval;
    public static int PollingInterval
    {
        get
        {
            if (_PollingInterval == 0)
            {
                _PollingInterval = (Properties.Settings.Default.ConfigurationPollingIntervalMS > 0) ? Properties.Settings.Default.ConfigurationPollingIntervalMS : 5000;
            }
            return (_PollingInterval);

        }
        set { _PollingInterval = value; }
    }


    private static bool _Working = false;
    public static bool Working
    {
        get { return (_Working); }
    }
    public static void Start()
    {
        Start(PollingInterval);
    }

    /// <summary>
    /// Scans each DLL in a folder, building a list of the ConfigurationMonitor methods to call.
    /// </summary>
    private static List<ConfigurationMonitorAttribute> _MonitorMethods;
    private static List<ConfigurationMonitorAttribute> MonitorMethods
    {
        get
        {
            if (_MonitorMethods == null)
            {
                _MonitorMethods = new List<ConfigurationMonitorAttribute>();
                MonitorMethodsMessage = string.Empty;
                foreach (var assemblyFile in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"), Properties.Settings.Default.ConfigurtionMonitorDLLPath))
                {
                    var assembly = Assembly.LoadFrom(assemblyFile);
                    foreach (ConfigurationMonitorAttribute monitor in assembly.GetCustomAttributes(typeof(ConfigurationMonitorAttribute), inherit: false))
                    {
                        _MonitorMethods.Add(monitor);
                    }
                }
            }
            return (_MonitorMethods);
        }
    }

    /// <summary>
    /// Resets and instanciates MonitorMethods property to refresh dlls being monitored
    /// </summary>
    public static void LoadMonitoringMethods()
    {
        _MonitorMethods = null;
        List<ConfigurationMonitorAttribute> monitorMethods = MonitorMethods;
    }

    /// <summary>
    /// Initiates a timer to monitor for configuration changes.
    /// This method is invoke on web application startup.
    /// </summary>
    /// <param name="pollingIntervalMS"></param>
    public static void Start(int pollingIntervalMS)
    {
        if (Properties.Settings.Default.ConfigurationMonitoring)
        {
            if (!timer.Enabled)
            {
                LoadMonitoringMethods();
                timer.Interval = pollingIntervalMS;
                timer.Enabled = true;
                timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
                timer.Start();
            }
            else
            {
                timer.Interval = pollingIntervalMS;
            }
        }
    }
    public static void Stop()
    {
        if (Properties.Settings.Default.ConfigurationMonitoring)
        {
            if (timer.Enabled)
            {
                timer.Stop();
            }
        }
    }

    /// <summary>
    /// Monitors CE table for changes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
        timer.Enabled = false;
        PollForChanges();
        timer.Enabled = true;
    }
    public static DateTime PollForChanges()
    {
        LastPoll = PollForChanges(LastPoll);
        return (LastPoll);
    }
    public static DateTime PollForChanges(DateTime lastPollDate)
    {
        try
        {
            _Working = true;
            foreach (ConfigurationMonitorAttribute monitor in MonitorMethods)
            {
                try
                {
                    lastPollDate = monitor.InvokeMethod(lastPollDate);
                    if (lastPollDate > LastRefreshDate)
                        LastRefreshDate = lastPollDate;
                }
                catch (System.Exception ex)
                {
                    // log the exception; my code omitted for brevity
                }
            }
        }
        catch (System.Exception ex)
        {
            // log the exception; my code omitted for brevity

        }
        finally
        {
            _Working = false;
        }
        return (lastPollDate);
    }

    #region Events
    /// <summary>
    /// Event raised when an AppDomain reset should occur
    /// </summary>
    public static event AppDomainChangeEvent AppDomainChanged;
    public static void OnAppDomainChanged(string configFile, IDictionary<string, object> properties)
    {
        if (AppDomainChanged != null) AppDomainChanged(null, new AppDomainArgs(configFile, properties));
    }
    #endregion
}

Когда у нас есть сценарий использования, который хочет чтобы «участвовать» в этом механизме опроса, мы помечаем некоторый метод с атрибутом:

[assembly: ConfigurationMonitorAttribute(typeof(bar), "Monitor")]
namespace foo
{
  public class bar 
  {
    public static DateTime Monitor(DateTime lastPoll)
    {
      // do your expensive work here, setting values in your cache
    }
  }
}

Наш паттерн с методом, вызванным ConfigurationMonitor, возвращающим DateTime, был довольно странным краем дело. Конечно, вы могли бы пойти с void методом.

где ConfigurationMonitorAttribute примерно такой:

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ConfigurationMonitorAttribute : Attribute
{
    private Type _type;
    private string _methodName;

    public ConfigurationMonitorAttribute(Type type, string methodName)
    {
        _type = type;
        _methodName = methodName;
    }

    public Type Type
    {
        get
        {
            return _type;
        }
    }

    public string MethodName
    {
        get
        {
            return _methodName;
        }
    }

    private MethodInfo _Method;
    protected MethodInfo Method
    {
        get
        {
            if (_Method == null)
            {
                _Method = Type.GetMethod(MethodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
                if (_Method == null)
                    throw new ArgumentException(string.Format("The type {0} doesn't have a static method named {1}.", Type, MethodName));

            }
            return _Method;
        }
    }
    public DateTime InvokeMethod(DateTime lastPoll)
    {
        try
        {
            return (DateTime)Method.Invoke(null, new object[] { lastPoll });
        }
        catch (System.Exception err)
        {
            new qbo.Exception.ThirdPartyException(string.Format("Attempting to monitor {0}/{1} raised an error.", _type, _methodName), err);
        }
        return lastPoll;
    }
}
0
ответ дан Eric Patrick 23 January 2019 в 20:24
поделиться

Есть несколько других вещей, о которых нужно подумать в этом сценарии, кроме просто сказать «Я хочу добавить кеширование».

  1. Если вы работаете в Azure или в веб-ферме, вам нужен централизованный кеш (REDIS или тому подобное), так как кэш памяти будет уничтожен и воссоздан на вашем сайте, и локально для один сервер в ферме, поэтому вы не обязательно увидите прирост производительности.

  2. Если вы настраиваете кэш REDIS, убедитесь, что вы позаботились о его настройке. Кодирование должно быть таким же, чтобы учесть соединения, и если вы сделаете это неправильно, вы в конечном итоге переполните свой пул соединений.

  3. Это больше относится к вашей ситуации, но даже 4 секунды, чтобы вернуть 70 тыс. Записей, кажутся высокими. Проходили ли вы план выполнения, чтобы увидеть, есть ли отсутствующие индексы или оптимизации с CTE, которые можно применить?

0
ответ дан Rob 23 January 2019 в 20:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: