Сервер (база данных / сеть) не может инициировать соединение - только клиент может. Поэтому вам придется опросить базу данных до тех пор, пока не появится обновление. Вы можете создать веб-сервис, который проверяет базу данных и какой jQuery использует.
Изменить: Я стою исправлено. Можно поддерживать соединение AJAX открытым до тех пор, пока сервер не «подталкивает» данные к нему. См .: http://en.wikipedia.org/wiki/Reverse_Ajax
И, по-видимому, это действительно опрос: http://en.wikipedia.org/wiki / Push_technology # Long_polling . Если сервер еще не имеет данных для отправки, он держит соединение открытым до тех пор, пока оно не произойдет. Это не «чистая» технология push, потому что клиент не имеет порта прослушивания, к которому подключается сервер. Эффект схож, однако.
Редактировать 2: Вернемся к ответу на ваш вопрос ... Вам нужно будет выбрать, как «опросить» веб-службу. Затем веб-службе необходимо проверить базу данных, чтобы узнать, есть ли обновления. Проверка базы данных на обновления может оказаться сложной и действительно зависит от ваших требований. Вы можете запустить SQL-запрос, чтобы узнать, что-то изменилось, но как вы узнали? Для сравнения вам понадобится какой-то параметр (обычно дата). Если все сделано неправильно, вы можете пропустить некоторые обновления или иметь несколько обращений за одним обновлением. То, что сказал автократия, было бы хорошим способом получить уведомление об обновлениях. Вы можете сохранить этот список в базе данных, в памяти и т. Д. И очистить его, когда клиент получит обновления.
Вам не нужно писать код. Используйте MoreLINQ Пакетный метод, который поставляет исходную последовательность в размерные ковши (MoreLINQ доступен в виде пакета NuGet, который вы можете установить):
int size = 10;
var batches = sequence.Batch(size);
Что реализовано как:
public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
this IEnumerable<TSource> source, int size)
{
TSource[] bucket = null;
var count = 0;
foreach (var item in source)
{
if (bucket == null)
bucket = new TSource[size];
bucket[count++] = item;
if (count != size)
continue;
yield return bucket;
bucket = null;
count = 0;
}
if (bucket != null && count > 0)
yield return bucket.Take(count);
}
Это полностью ленивая, низкая накладная, однофункциональная реализация пакета, которая не делает никакого накопления. Основываясь на (и исправляет проблемы) в решении Ника Уэйли с помощью EricRoller.
Итерация происходит непосредственно из основного IEnumerable, поэтому элементы должны быть перечислены в строгом порядке и не доступны больше чем единожды. Если некоторые элементы не потребляются во внутреннем цикле, они отбрасываются (и попытка получить к ним доступ через сохраненный итератор будет бросать InvalidOperationException: Enumeration already finished.
).
Вы можете протестировать полный образец в .NET Fiddle .
public static class BatchLinq
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
{
int i = 0;
// Batch is a local function closing over `i` and `enumerator` that
// executes the inner batch enumeration
IEnumerable<T> Batch()
{
do yield return enumerator.Current;
while (++i < size && enumerator.MoveNext());
}
yield return Batch();
while (++i < size && enumerator.MoveNext()); // discard skipped items
}
}
}
done
, просто позвонив e.Count()
после yield return e
. Вам нужно будет перестроить цикл в BatchInner, чтобы не вызывать неопределенное поведение source.Current
, если i >= size
. Это устранит необходимость выделения нового BatchInner
для каждой партии.
– Eric Roller
3 January 2018 в 20:02
i
, поэтому это не обязательно более эффективно, чем определение отдельного класса, но я думаю немного чище.
– Eric Roller
7 April 2018 в 01:56
i
на самом деле не привязана к телу цикла (до тех пор, пока вы установите i=0
на каждую итерацию), вы можете вытащить его с помощью определения Batch (), чтобы избежать дополнительных распределений , подобных этому . По сравнению с измененной версией для бенчмаркинга (10k элементов, размер партии 3, только подсчет), похоже, что сокращение использования памяти с 640 - & gt; 430 kb в соответствии со статистикой скрипки (слабая метрика, по общему признанию).
– infogulch
8 April 2018 в 21:28
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
int maxItems)
{
return items.Select((item, inx) => new { item, inx })
.GroupBy(x => x.inx / maxItems)
.Select(g => g.Select(x => x.item));
}
}
, и использование будет:
List<int> list = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach(var batch in list.Batch(3))
{
Console.WriteLine(String.Join(",",batch));
}
OUTPUT:
0,1,2
3,4,5
6,7,8
9
GroupBy
начинает перечисление, ему не нужно полностью перечислить его источник? Это теряет ленивую оценку источника и, следовательно, в некоторых случаях все преимущества дозирования!
– ErikE
27 October 2015 в 19:37
Я написал пользовательскую реализацию IEnumerable, которая работает без linq и гарантирует единственное перечисление над данными. Он также выполняет все это, не требуя резервных списков или массивов, которые вызывают взрывы памяти на больших наборах данных.
Вот некоторые базовые тесты:
[Fact]
public void ShouldPartition()
{
var ints = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
var data = ints.PartitionByMaxGroupSize(3);
data.Count().Should().Be(4);
data.Skip(0).First().Count().Should().Be(3);
data.Skip(0).First().ToList()[0].Should().Be(0);
data.Skip(0).First().ToList()[1].Should().Be(1);
data.Skip(0).First().ToList()[2].Should().Be(2);
data.Skip(1).First().Count().Should().Be(3);
data.Skip(1).First().ToList()[0].Should().Be(3);
data.Skip(1).First().ToList()[1].Should().Be(4);
data.Skip(1).First().ToList()[2].Should().Be(5);
data.Skip(2).First().Count().Should().Be(3);
data.Skip(2).First().ToList()[0].Should().Be(6);
data.Skip(2).First().ToList()[1].Should().Be(7);
data.Skip(2).First().ToList()[2].Should().Be(8);
data.Skip(3).First().Count().Should().Be(1);
data.Skip(3).First().ToList()[0].Should().Be(9);
}
Метод расширения для разделения данных .
/// <summary>
/// A set of extension methods for <see cref="IEnumerable{T}"/>.
/// </summary>
public static class EnumerableExtender
{
/// <summary>
/// Splits an enumerable into chucks, by a maximum group size.
/// </summary>
/// <param name="source">The source to split</param>
/// <param name="maxSize">The maximum number of items per group.</param>
/// <typeparam name="T">The type of item to split</typeparam>
/// <returns>A list of lists of the original items.</returns>
public static IEnumerable<IEnumerable<T>> PartitionByMaxGroupSize<T>(this IEnumerable<T> source, int maxSize)
{
return new SplittingEnumerable<T>(source, maxSize);
}
}
Это реализующий класс
using System.Collections;
using System.Collections.Generic;
internal class SplittingEnumerable<T> : IEnumerable<IEnumerable<T>>
{
private readonly IEnumerable<T> backing;
private readonly int maxSize;
private bool hasCurrent;
private T lastItem;
public SplittingEnumerable(IEnumerable<T> backing, int maxSize)
{
this.backing = backing;
this.maxSize = maxSize;
}
public IEnumerator<IEnumerable<T>> GetEnumerator()
{
return new Enumerator(this, this.backing.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class Enumerator : IEnumerator<IEnumerable<T>>
{
private readonly SplittingEnumerable<T> parent;
private readonly IEnumerator<T> backingEnumerator;
private NextEnumerable current;
public Enumerator(SplittingEnumerable<T> parent, IEnumerator<T> backingEnumerator)
{
this.parent = parent;
this.backingEnumerator = backingEnumerator;
this.parent.hasCurrent = this.backingEnumerator.MoveNext();
if (this.parent.hasCurrent)
{
this.parent.lastItem = this.backingEnumerator.Current;
}
}
public bool MoveNext()
{
if (this.current == null)
{
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
else
{
if (!this.current.IsComplete)
{
using (var enumerator = this.current.GetEnumerator())
{
while (enumerator.MoveNext())
{
}
}
}
}
if (!this.parent.hasCurrent)
{
return false;
}
this.current = new NextEnumerable(this.parent, this.backingEnumerator);
return true;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public IEnumerable<T> Current
{
get { return this.current; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
private class NextEnumerable : IEnumerable<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly IEnumerator<T> backingEnumerator;
private int currentSize;
public NextEnumerable(SplittingEnumerable<T> splitter, IEnumerator<T> backingEnumerator)
{
this.splitter = splitter;
this.backingEnumerator = backingEnumerator;
}
public bool IsComplete { get; private set; }
public IEnumerator<T> GetEnumerator()
{
return new NextEnumerator(this.splitter, this, this.backingEnumerator);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class NextEnumerator : IEnumerator<T>
{
private readonly SplittingEnumerable<T> splitter;
private readonly NextEnumerable parent;
private readonly IEnumerator<T> enumerator;
private T currentItem;
public NextEnumerator(SplittingEnumerable<T> splitter, NextEnumerable parent, IEnumerator<T> enumerator)
{
this.splitter = splitter;
this.parent = parent;
this.enumerator = enumerator;
}
public bool MoveNext()
{
this.parent.currentSize += 1;
this.currentItem = this.splitter.lastItem;
var hasCcurent = this.splitter.hasCurrent;
this.parent.IsComplete = this.parent.currentSize > this.splitter.maxSize;
if (this.parent.IsComplete)
{
return false;
}
if (hasCcurent)
{
var result = this.enumerator.MoveNext();
this.splitter.lastItem = this.enumerator.Current;
this.splitter.hasCurrent = result;
}
return hasCcurent;
}
public void Reset()
{
throw new System.NotImplementedException();
}
public T Current
{
get { return this.currentItem; }
}
object IEnumerator.Current
{
get { return this.Current; }
}
public void Dispose()
{
}
}
}
}
Если вы начинаете с sequence
, определенного как IEnumerable<T>
, и знаете, что его можно безопасно перечислить несколько раз (например, потому что это массив или список), вы можете просто использовать этот простой шаблон для обработки элементы в партиях:
while (sequence.Any())
{
var batch = sequence.Take(10);
sequence = sequence.Skip(10);
// do whatever you need to do with each batch here
}
Итак, с функциональной шляпой, это выглядит тривиально .... но в C # есть некоторые существенные недостатки.
вы, вероятно, рассмотрите это как разворот IEnumerable (google it and you Вероятно, в некоторых документах Haskell, возможно, есть некоторые вещи F #, если вы знаете F #, косоглазие в документах Haskell, и это будет иметь смысл).
Unfold относится к fold ( «aggregate»), за исключением повторения через входной IEnumerable, он выполняет итерацию через выходные структуры данных (это аналогичная связь между IEnumerable и IObservable, на самом деле я думаю, что IObservable реализует «разворачивающийся», называемый generate ...)
в любом случае сначала вам нужен метод разворачивания, я думаю, что это работает;
static IEnumerable<T> Unfold<T, U>(Func<U, IEnumerable<Tuple<U, T>>> f, U seed)
{
var maybeNewSeedAndElement = f(seed);
return maybeNewSeedAndElement.SelectMany(x => new[] { x.Item2 }.Concat(Unfold(f, x.Item1)));
}
это немного тупо, потому что C # не реализует некоторые из функций, которые функциональные langauges принимают как должное ... но он в основном принимает семя, а затем генерирует ответ «Maybe» следующего элемента в IEnumerable и следующем семестре (возможно, не существует в C #, поэтому мы использовали IEnume (я не могу ругаться, чтобы подделать его), и конкатенирует остальную часть ответа (я не могу ручаться за сложность этого «O (n?)».
Как только вы это сделали,
static IEnumerable<IEnumerable<T>> Batch<T>(IEnumerable<T> xs, int n)
{
return Unfold(ys =>
{
var head = ys.Take(n);
var tail = ys.Skip(n);
return head.Take(1).Select(_ => Tuple.Create(tail, head));
},
xs);
}
все выглядит довольно чистым ... вы берете «n» элементы как «следующий» элемент в IEnumerable, а «tail» - это остальная часть необработанного списка.
, если в голове ничего нет ... вы закончили ... вы возвращаете «Nothing» (но поддельно как пустой IEnumerable>) ... иначе вы возвращаете элемент головы и хвост для обработки.
вы, вероятно, можете сделать это с помощью IObservable, вероятно, существует уже такой «пакетный» метод, и вы, вероятно, можете его использовать.
Если риск переполнения стека вызывает беспокойство (вероятно, он должен ), тогда вы должны реализовать в F # (и, вероятно, уже есть такая библиотека F # (FSharpX?)).
(Я только сделал некоторые рудиментарные тесты этого, так что могут быть нечетные ошибки в там).
static IEnumerable<IEnumerable<T>> TakeBatch<T>(IEnumerable<T> ts,int batchSize)
{
return from @group in ts.Select((x, i) => new { x, i }).ToLookup(xi => xi.i / batchSize)
select @group.Select(xi => xi.x);
}
Все вышеизложенное работает ужасно с большими партиями или небольшим объемом памяти. Пришлось написать свой собственный, который будет конвейер (нигде не заметил накопления элемента):
public static class BatchLinq {
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
if (size <= 0)
throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
using (IEnumerator<T> enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return TakeIEnumerator(enumerator, size);
}
private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
int i = 0;
do
yield return source.Current;
while (++i < size && source.MoveNext());
}
}
Изменить: Известная проблема с этим подходом заключается в том, что каждая партия должна быть перечислина и перечислина полностью, прежде чем перейти к следующей партии. Например, это не работает:
//Select first item of every 100 items
Batch(list, 100).Select(b => b.First())
Я присоединяюсь к этому очень поздно, но нашел что-то более интересное.
Таким образом, мы можем использовать здесь Skip
и Take
для лучшей производительности.
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int maxItems)
{
return items.Select((item, index) => new { item, index })
.GroupBy(x => x.index / maxItems)
.Select(g => g.Select(x => x.item));
}
public static IEnumerable<T> Batch2<T>(this IEnumerable<T> items, int skip, int take)
{
return items.Skip(skip).Take(take);
}
}
Далее я проверил 100000 записей. Зацикливание занимает больше времени в случае Batch
Code of console application.
static void Main(string[] args)
{
List<string> Ids = GetData("First");
List<string> Ids2 = GetData("tsriF");
Stopwatch FirstWatch = new Stopwatch();
FirstWatch.Start();
foreach (var batch in Ids2.Batch(5000))
{
// Console.WriteLine("Batch Ouput:= " + string.Join(",", batch));
}
FirstWatch.Stop();
Console.WriteLine("Done Processing time taken:= "+ FirstWatch.Elapsed.ToString());
Stopwatch Second = new Stopwatch();
Second.Start();
int Length = Ids2.Count;
int StartIndex = 0;
int BatchSize = 5000;
while (Length > 0)
{
var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
// Console.WriteLine("Second Batch Ouput:= " + string.Join(",", SecBatch));
Length = Length - BatchSize;
StartIndex += BatchSize;
}
Second.Stop();
Console.WriteLine("Done Processing time taken Second:= " + Second.Elapsed.ToString());
Console.ReadKey();
}
static List<string> GetData(string name)
{
List<string> Data = new List<string>();
for (int i = 0; i < 100000; i++)
{
Data.Add(string.Format("{0} {1}", name, i.ToString()));
}
return Data;
}
Записанное время Это как.
Сначала - 00: 00: 00.0708, 00: 00: 00.0660
Второе (выберите и пропустите) - 00: 00: 00.0008, 00: 00: 00.0008
GroupBy
полностью перечисляет до того, как он произведет одну строку. Это не очень хороший способ сделать дозирование.
– ErikE
27 April 2016 в 20:43
Такой же подход, как MoreLINQ, но с использованием List вместо Array. Я не проводил бенчмаркинг, но читаемость важна для некоторых людей:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
List<T> batch = new List<T>();
foreach (var item in source)
{
batch.Add(item);
if (batch.Count >= size)
{
yield return batch;
batch.Clear();
}
}
if (batch.Count > 0)
{
yield return batch;
}
}
size
к вашему new List
, чтобы оптимизировать его размер.
– ErikE
26 September 2017 в 00:58
batch.Clear();
на batch = new List<T>();
– NetMage
17 November 2017 в 21:57
Batch(new int[] { 1, 2 }, 1000000)
– Nick Whaley 12 July 2013 в 19:56