Здравствуйте!
Я имею для хранения тегов на сайте примерно такую систему:
1) Общий статический класс для доступа к репозиториям. Он соответственно общий для всех юзеров и всех потоков.
public static class RepositoryManager
{
public static TagRepository TagRepository { get; private set; }
static RepositoryManager()
{
TagRepository = new TagRepository();
}
}
2) Сам класс репозитория имеет дублирующую функциональность: работает из бд и с кешем. При инициализации загружает данные из бд в кеш. Судя по отладчику все загружается и сохраняется.
public sealed class TagRepository : BigSqlRepository<TagData>
{
private TagCacheCollection CacheCollection { get; set; }
public TagRepository()
{
CacheCollection = new TagCacheCollection(GetList());
}
public override TagData GetById(Int64 id)
{
return CacheCollection.GetTag(id);
}
}
3) Собственно сам класс кеширования
public class TagCacheCollection
{
private ConcurrentDictionary<Int64, TagData> DataDictById { get; set; }
public TagCacheCollection(IEnumerable<TagData> initialCollection)
{
DataDictById = new ConcurrentDictionary<Int64, TagData>(initialCollection.Select(i =>
new KeyValuePair<Int64, TagData>(i.Id, i))); /////Вот тут все работает и загружается
}
public TagData GetTag(Int64 id)
{
return DataDictById[id]; ////Вот тут падает
}
}
И собственно возникает такая проблема:
1) Сначала было так:
Несмотря на то, что данные загружаются в DataDictById нормально, при вызове GetTag в DataDictById этих данные не оказывается. То есть DataDictById остается вообще пустой.
Хотя и инициализируется и дергается DataDictById в одном и том же запросе, то есть на теоретические проблемы многопоточности из-за использования статического класса тут скинуть не удастся.
2) Потом неожиданно, после прохождения всего пути отладчиком, все теги закинулись и все стало работать хорошо и правильно. Я пробовал перезагружать студию, перекомпилировать — все работало. Я добавил новые теги. Из бд они опять же брались, отладчик показывал, что взялись все теги, но в GetTag на этот раз оказались только старые теги и оно опять падает.
Собственно вопрос: что за чудеса не виражах такие?
public TagCacheCollection(IEnumerable<TagData> initialCollection)
{
DataDictById = new ConcurrentDictionary<Int64, TagData>(initialCollection.Select(i =>
new KeyValuePair<Int64, TagData>(i.Id, i))); /////Вот тут все работает и загружается
}
данные из БД достаются не всегда/не все/не сразу. А потом, когда Вы мышкой наводите на initialCollection под отладчиком — они извлекаются все и правильно.
Вставьте отладочный код:
public TagCacheCollection(IEnumerable<TagData> initialCollection)
{
var myActualData = initialCollection.ToList();
Debug.Assert(/* и здесь проверить, что из БД досталось все, что должно было */);
DataDictById = new ConcurrentDictionary<Int64, TagData>(myActualData.Select(i =>
new KeyValuePair<Int64, TagData>(i.Id, i)));
}
Здравствуйте, 80LevelElf, Вы писали:
LE>Собственно вопрос: что за чудеса не виражах такие?
Если несколько app domain, тогда могут быть чудеса, т.к. в каждом RepositoryManager будет свой, и то наврядли — данные в конструторах должны загрузиться одинаковые.
Попробуй watch в дебаге поставить на RepositoryManager.TagRepository.CacheCollection.DataDictById, будет видно когда изменяется.
Здравствуйте, tapatoon, Вы писали:
T>Здравствуйте, 80LevelElf, Вы писали:
LE>>Собственно вопрос: что за чудеса не виражах такие?
T>Если несколько app domain, тогда могут быть чудеса, т.к. в каждом RepositoryManager будет свой, и то наврядли — данные в конструторах должны загрузиться одинаковые. T>Попробуй watch в дебаге поставить на RepositoryManager.TagRepository.CacheCollection.DataDictById, будет видно когда изменяется.
Ну вот простой пример, как все это меняется (предположим мы добавили 1 элемент):
var tagRepository = new TagRepository(); //Тут показывает, что х + 1 элементов
tagRepository.GetById(id); //А вот здесь уже только х элементов
Здравствуйте, scale_tone, Вы писали:
_>Здравствуйте, 80LevelElf, Вы писали:
_>Могу предположить, что вот в этом месте:
_>
_>public TagCacheCollection(IEnumerable<TagData> initialCollection)
_>{
_> DataDictById = new ConcurrentDictionary<Int64, TagData>(initialCollection.Select(i =>
_> new KeyValuePair<Int64, TagData>(i.Id, i))); /////Вот тут все работает и загружается
_>}
_>
_>данные из БД достаются не всегда/не все/не сразу. А потом, когда Вы мышкой наводите на initialCollection под отладчиком — они извлекаются все и правильно.
_>Вставьте отладочный код:
_>
_>public TagCacheCollection(IEnumerable<TagData> initialCollection)
_>{
_> var myActualData = initialCollection.ToList();
_> Debug.Assert(/* и здесь проверить, что из БД досталось все, что должно было */);
_> DataDictById = new ConcurrentDictionary<Int64, TagData>(myActualData.Select(i =>
_> new KeyValuePair<Int64, TagData>(i.Id, i)));
_>}
_>
Я про это уже думал и проверял таким образом. Проверил еще раз с помощью вашего кода — тоже самое. При загрузке — загружается все, при выборке остаются только старые элементы.
Здравствуйте, scale_tone, Вы писали:
_>Могу предположить, что вот в этом месте:
_>
_>public TagCacheCollection(IEnumerable<TagData> initialCollection)
_>{
_> DataDictById = new ConcurrentDictionary<Int64, TagData>(initialCollection.Select(i =>
_> new KeyValuePair<Int64, TagData>(i.Id, i))); /////Вот тут все работает и загружается
_>}
_>
_>данные из БД достаются не всегда/не все/не сразу.
Конструктор ConcurrentDictionary втаскивает IEnumerable eager кодом в foreach. Если не втащит — кинет исключение.
Здравствуйте, samius, Вы писали:
_>>данные из БД достаются не всегда/не все/не сразу. S>Конструктор ConcurrentDictionary втаскивает IEnumerable eager кодом в foreach. Если не втащит — кинет исключение.
Это понятно и очевидно. Предполагалось, что этот самый IEnumerable может при разных обращениях выдавать разные наборы значений.
Здравствуйте, 80LevelElf, Вы писали:
LE>Я про это уже думал и проверял таким образом. Проверил еще раз с помощью вашего кода — тоже самое. При загрузке — загружается все, при выборке остаются только старые элементы.
Ну, тогда нужен весь остальной код (в частности, метода GetList()), а также подробности и содержимое БД.
LE>var tagRepository = new TagRepository(); //Тут показывает, что х + 1 элементов
LE>tagRepository.GetById(id); //А вот здесь уже только х элементов
LE>
Если этот код не в конструкторе RepositoryManager, то вполне возможно проблема в этом — создаётся новый инстанс TagRepository, а по задумке надо использовать RepositoryManager.TagRepository.
А если в конструкторе... Напиши обёртку для колекции, ставь бряки в функциях, которые её изменяют.
Здравствуйте, 80LevelElf, Вы писали:
LE>Ну вот простой пример, как все это меняется (предположим мы добавили 1 элемент): LE>
LE>var tagRepository = new TagRepository(); //Тут показывает, что х + 1 элементов
LE>tagRepository.GetById(id); //А вот здесь уже только х элементов
LE>
Странный у Вас пример. Не согласуется с идеей синглтонности в исходном коде...
Здравствуйте, scale_tone, Вы писали:
_>Здравствуйте, 80LevelElf, Вы писали:
LE>>Я про это уже думал и проверял таким образом. Проверил еще раз с помощью вашего кода — тоже самое. При загрузке — загружается все, при выборке остаются только старые элементы.
_>Ну, тогда нужен весь остальной код (в частности, метода GetList()), а также подробности и содержимое БД.
GetList у меня реализовывается с помощью Linq2DB, то есть примерно так:
public virtual List<T> GetList()
{
using (var db = new DataBase())
{
return db.GetTable<T>().ToList();
}
}
Здравствуйте, scale_tone, Вы писали:
_>Здравствуйте, 80LevelElf, Вы писали:
LE>>Ну вот простой пример, как все это меняется (предположим мы добавили 1 элемент): LE>>
LE>>var tagRepository = new TagRepository(); //Тут показывает, что х + 1 элементов
LE>>tagRepository.GetById(id); //А вот здесь уже только х элементов
LE>>
_>Странный у Вас пример. Не согласуется с идеей синглтонности в исходном коде...
Ну я же просто пример привел, мол смотрите, даже если в 1 строке создать DAL, а во 2 сразу же использовать, то получается такая фигня.
Здравствуйте, tapatoon, Вы писали:
T>Здравствуйте, 80LevelElf, Вы писали:
LE>>
LE>>var tagRepository = new TagRepository(); //Тут показывает, что х + 1 элементов
LE>>tagRepository.GetById(id); //А вот здесь уже только х элементов
LE>>
T>Если этот код не в конструкторе RepositoryManager, то вполне возможно проблема в этом — создаётся новый инстанс TagRepository, а по задумке надо использовать RepositoryManager.TagRepository.
Это был просто пример, мол смотрите даже если создать DAL а потом сразу вызвать будет такая фигня.
T>А если в конструкторе... Напиши обёртку для колекции, ставь бряки в функциях, которые её изменяют.
Да я это коллекцию меняю только один раз(не считая инициализации) и то добавляю. Так что в моем коде удалятся там точно ничего не должно.
Если интересно, вот ссылка на полный код: https://github.com/80LevelElf/ZaBugrom/tree/master/CommonDAL
Здравствуйте, 80LevelElf, Вы писали:
LE>Здравствуйте! LE>Я имею для хранения тегов на сайте примерно такую систему: LE>1) Общий статический класс для доступа к репозиториям. Он соответственно общий для всех юзеров и всех потоков. LE>
LE> public static class RepositoryManager
LE> {
LE> public static TagRepository TagRepository { get; private set; }
LE> static RepositoryManager()
LE> {
LE> TagRepository = new TagRepository();
LE> }
LE> }
LE>
LE>2) Сам класс репозитория имеет дублирующую функциональность: работает из бд и с кешем. При инициализации загружает данные из бд в кеш. Судя по отладчику все загружается и сохраняется. LE>
LE> public sealed class TagRepository : BigSqlRepository<TagData>
LE> {
LE> private TagCacheCollection CacheCollection { get; set; }
LE> public TagRepository()
LE> {
LE> CacheCollection = new TagCacheCollection(GetList());
LE> }
LE> public override TagData GetById(Int64 id)
LE> {
LE> return CacheCollection.GetTag(id);
LE> }
LE> }
LE>
LE>3) Собственно сам класс кеширования LE>
LE> public class TagCacheCollection
LE> {
LE> private ConcurrentDictionary<Int64, TagData> DataDictById { get; set; }
LE> public TagCacheCollection(IEnumerable<TagData> initialCollection)
LE> {
LE> DataDictById = new ConcurrentDictionary<Int64, TagData>(initialCollection.Select(i =>
LE> new KeyValuePair<Int64, TagData>(i.Id, i))); /////Вот тут все работает и загружается
LE> }
LE> public TagData GetTag(Int64 id)
LE> {
LE> return DataDictById[id]; ////Вот тут падает
LE> }
LE> }
LE>
LE>И собственно возникает такая проблема: LE>1) Сначала было так: LE>Несмотря на то, что данные загружаются в DataDictById нормально, при вызове GetTag в DataDictById этих данные не оказывается. То есть DataDictById остается вообще пустой. LE>Хотя и инициализируется и дергается DataDictById в одном и том же запросе, то есть на теоретические проблемы многопоточности из-за использования статического класса тут скинуть не удастся. LE>2) Потом неожиданно, после прохождения всего пути отладчиком, все теги закинулись и все стало работать хорошо и правильно. Я пробовал перезагружать студию, перекомпилировать — все работало. Я добавил новые теги. Из бд они опять же брались, отладчик показывал, что взялись все теги, но в GetTag на этот раз оказались только старые теги и оно опять падает. LE>Собственно вопрос: что за чудеса не виражах такие?
В результате нашел косяк:
Конструктор статического класса вызывался далеко на всегда. То есть вообще как-то странно получается: скомпилировал проект, он запустился, статический класс используется, а статический конструктор не вызывается! Пора еще раз перечитать теорию.
Всем спасибо за помощь!