2

Здравствуйте. В проекте использую синглтон (хотя я не совсем понимаю, как его правильно применять).

public sealed class DataManager
{
    private DataManager()
    {
        Fabric = new FabricRepository();
        FabricProduct = new FabricProductRepository();
        Fitting = new FittingRepository();
        FittingProduct = new FittingProductRepository();
        Order = new OrderRepository();
        ProductOrder = new ProductOrderRepository();
        Product = new ProductRepository();
        StockFabric = new StockFabricRepository();
        StockFitting = new StockFittingRepository();
        User = new UserRepository();
    }

    public FabricRepository Fabric { get; set; }
    public FabricProductRepository FabricProduct { get; set; }
    public FittingRepository Fitting { get; set; }
    public FittingProductRepository FittingProduct { get; set; }
    public OrderRepository Order { get; set; }
    public ProductOrderRepository ProductOrder { get; set; }
    public ProductRepository Product { get; set; }
    public StockFabricRepository StockFabric { get; set; }
    public StockFittingRepository StockFitting { get; set; }
    public UserRepository User { get; set; }

    static DataManager _active = null;

    public static DataManager Instance
    {
        get
        {
            if (_active == null)
                _active = new DataManager();

            return _active;
        }
    }
}

То есть он лежит у меня в одном месте с моими репозиториями, через него я к ним и обращаюсь. Вот код конструктора моего generic repository

public BaseRepository()
{
    this.TableName = typeof(T).Name;
    FillTable();
}

private void FillTable()
{
    DataAdapter.SelectCommand = new SqlCommand("SELECT * FROM [" + this.TableName + "]", Config.con);
    DataAdapter.Fill(this.Table);
}

Правильно ли я понимаю, что при создании например в коде формы DataManager.Instance.User.Add(user) он проходит по всем конструкторам моих репозиториев и заполняет данными? То есть, при наличии даже небольшого числа данных будет задержка. Может просто создавать этот синглтон по мере необходимости? Буду благодарен за любые советы.

VladD
  • 206,799
  • 4
    Проблема-то у вас не столько в синглтоне, сколько в том что вы загружаете данные в конструкторе... – Pavel Mayorov Jan 09 '18 at 12:50
  • @PavelMayorov здесь логика какова - при создании экземпляра репозитория заполняется DataTable и с помощью него уже могу использовать такие методы как Add(T item), Delete... Вы предлагаете при работе с бд перед добавлением например, самому юзать метод FillTable()? – Ari100krat Jan 09 '18 at 13:45
  • Вы все верно начали думать. Пляшите не от класса, а от того как Вы собираетесь его использовать. Код сам напишется так, как ему удобно. – Алексей Обухов Jan 09 '18 at 13:59

2 Answers2

0

Он у вас и создаётся по необходимости - при первом обращении.

Вы можете сделать синглтоном каждый репозиторий в отдельности, и обращаться к ним так:

User.Instance.Add(...);
Product.Instance.Add(...);

Или сделать отложенное создание этих свойств. Пример отложенной инициализации поля Instance вы привели самостоятельно. Ничто не мешает поступить так же со всеми остальными.

Можно пойти ленивым путём и с небольшим оверхедом использовать готовое средство: Lazy.

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

Если по какой-то причине данные нужно загрузить один раз и держать постоянно в памяти - способ отложенной загрузки вы теперь знаете.

  • "Способ отложенной загрузки" это вы про вашу ссылку на lazy? – Ari100krat Jan 09 '18 at 14:46
  • @Ari100krat и про Lazy, если нужно потокобезопасность, и про ваше непотокобезопасное создание инстанса. – Lunar Whisper Jan 10 '18 at 09:58
  • "Если по какой-то причине данные нужно загрузить один раз и держать постоянно в памяти - способ отложенной загрузки вы теперь знаете." Я еще новичок и только познаю такие вещи как паттерны и рекурсия. Если не трудно, можете привести пример (хотя бы непотокобезопасный), как я могу решить данную проблему. Или тогда уже придется делать из каждого репозитория singleton. Спасибо, что вы мне и так многое подсказали. – Ari100krat Jan 10 '18 at 14:40
0

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

public class IAmSingletone
{
    private bool _initialized = false;
    private object _lock = new Object();

    public IAmSingletone()
    {
        Console.WriteLine("IAmSingletone created");
    }

    private void LoadALotOfData()
    {
        if (!_initialized)
        {
            lock (_lock)
            {
                if (!_initialized)
                {
                    Console.WriteLine("Doing loading stuff...");
                    Thread.Sleep(3000);
                    Console.WriteLine("Stuff loaded.");
                    _initialized=true;
                }
            }
        }
    }   

    public void DoWork()
    {
        LoadALotOfData();

        Console.WriteLine("Doing DoWork");
    }   
}

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

public class IAmClient
{
    IAmSingletone _singletone;

    public IAmClient(IAmSingletone singletone)
    {
        _singletone = singletone;
        Console.WriteLine("Creating client.");
    }

    public void CallForSongletone()
    {
        Console.WriteLine("Calliong _singletone.DoWork()");
        _singletone.DoWork();
    }
}

С клиентом все и так ясно - получает нужный ему класс и вызывает его методы когда надо. Теперь как это связать. (я использовал контейнер CastleWindsor)

var container = new WindsorContainer();

container.Register(Component.For<IAmSingletone>().LifeStyle.Singleton);
container.Register(Component.For<IAmClient>().LifeStyle.Transient);

for (var i = 0; i < 5; i++)
{
    var client = container.Resolve<IAmClient>();
    client.CallForSongletone();
}

Как видно, синглтон зарегестрирован как синглтон, обычный класс- как обычный класс. Вывод на консоль будет:

IAmSingletone created
Creating client.
Calliong _singletone.DoWork()
Doing loading stuff...
Stuff loaded.
Doing DoWork
Creating client.
Calliong _singletone.DoWork()
Doing DoWork
Creating client.
Calliong _singletone.DoWork()
Doing DoWork
Creating client.
Calliong _singletone.DoWork()
Doing DoWork
Creating client.
Calliong _singletone.DoWork()
Doing DoWork

Из вывода видно, что синглтон создан 1 раз, данные загружены 1 раз, и это для 5 разных клиентов. Преимущество этого метода - вы можете переключать время жизни объекта с обычного класса на синглтон и обратно просто меняя настройку IoC контейнера.

tym32167
  • 32,857
  • такое ощущение, что прочитал пролог к книге "Как завязать с IoC и начать жить". :) Данный пример очень наглядно демонстрирует подход "паттерны ради паттернов", и едва ли может быть рекомендован к прочтению новичками, которые не знают зачем им "переключать время жизни объекта с обычного класса на синглтон и обратно". Вот объяснить - для чего в синглтоне блокировка с двойной проверкой было бы неплохо. IoC здесь выступает пятым колесом и лишь усложняет ответ. – Lunar Whisper Jan 10 '18 at 10:03
  • @LunarWhisper Автор спросил как использовать синглтон. Большинство проектов, сложнее hello world, используют контейнеры. Я показал, как с помощью контейнера сделать класс синглтоном. Где тут паттерны ради паттернов? Разве знать о контейнерах и как их использовать - лишнее для новичка, что пишет программу, работающую с БД? Если автору будет интересно, я поясню про блокировку. Приведите мне стоящие и объективные аргументы против моего ответа и я с удовольствием его изменю или удалю, а пока ваш выпад выглядит надуманно и необоснованно. – tym32167 Jan 10 '18 at 11:25
  • ваше мнение субъективно. IoC взлетели на одной волне, и легли на свободную нишу архитектурных подходов. Это всё равно что говорить, что каждый кусок кода должен быть покрыт Unit-тестами. Что каждый законченный компонент должен лежать в своей сборке. Что весь код должен находиться под системой управления версиями. Во многих ситуациях это правильные и уместные советы, то они не имеют никакого отношения к вопросу автора. Плюс, по моему субъективному мнению, новички не должны заниматься проектированием и конфигурацией IoC-архитектуры. – Lunar Whisper Jan 10 '18 at 13:03
  • Вы выдумываете какие то тезисы за меня. Покажите мне, где я писал, что каждый проект обязан использовать IoC? Поглядите на ваш ответ. Вы описали один способ реализации, я описал другой. Я считаю, что оба имеют право на жизнь. Вы мне что-то другое пытаетесь доказать? Фраза, что большинство (а не каждый) сложных проектов используют IoC взята да, из личного опыта. И с чего вдруг реализация синглтона не имеет отношения к вопросу о реализации синглтона? Не нужно разводить демагогию о высших материях - приведите мне конкретный довод против моего ответа. – tym32167 Jan 10 '18 at 13:35
  • я из своего же опыта отвечу, что из-за стоимости поддержки IoC очень редко применяется в крупных энтерпрайз-приложениях.

    Мы с вами привели один и тот же вариант реализации. Вы сделали это более подробно. После этого, вокруг вашего синглтона, вы навернули IoC-конетйнер. Я пытаюсь донести до вас мысль, что к синглтону он никакого отношения не имеет. Более того, с введением IoC это уже не синглтон, вы сами говорите о его подмене. Наверняка, вам известны подводные камни IoC. И поэтому предлагать новичку разбить о них нос считаю неправильным, так как вопрос их вообще не касается.

    – Lunar Whisper Jan 11 '18 at 12:17
  • @LunarWhisper очевидно, что наш с вами опыт кардинально различается, и это хорошо. Я не буду спорить о применимости IoC контейнеров, так как это - отдельная тема для дискуссий. Я не знаю, что в вашем понимании синглтон, но в моем - синглтон это класс, от которого доступен только 1 инстанс. Его можно реализовать через статические классы, или какими либо другими способами. Вы показали один из способов, я показал другой. То, что в вашем понимании этоне является формально синглтоном не отменяет того факта, что приведенный мой пример используется повсеместно в куче проектов и имеет право на жизнь. – tym32167 Jan 11 '18 at 12:45
  • @LunarWhisper то. что вы считаете, что новичкам про это знать не нужно, это ваше мнение, но я (да и не только я) считаю по другому. Но и это отношения к дискуссии не имеет, ибо имхо автору вопроса лучше иметь из чего выбирать, чем не иметь. – tym32167 Jan 11 '18 at 12:49
  • @LunarWhisper ну и меня задел немного ваш первый комментарий про качество моего кода. Мне он кажется надменным и не обоснованным. То, что я показал в своем ответе, широко используется в разных проектах, и "паттернов ради паттернов" я тут совсем не вижу. А разговоры о применимости IoC и кому что можно учить, а что нельзя - это имхо не имеет отношения к заданному вопросу. – tym32167 Jan 11 '18 at 13:28
  • Одиночка (англ. Singleton) — порождающий шаблон проектирования, гарантирующий, что в приложении будет единственный экземпляр некоторого класса Вы же приводите в качестве примера один из способов конфигурации зависимости в IoC. Это был бы отличный ответ на вопрос: "как реализовать синглтон с использованием IoC". Но вы пишите: Обычно синглтонами делают классы на этапе настройки IoC контейнера Тем самым навязывая эту точку зрению человеку заведомо незнающему, который не может отличить реальные факты от вкусовых пристрастий. Мне кажется, что это неправильно и может нанести вред. – Lunar Whisper Jan 12 '18 at 10:40
  • @LunarWhisper Одиночка (англ. Singleton) — порождающий шаблон проектирования, гарантирующий, что в приложении будет единственный экземпляр некоторого класса - при настройке синглтона в IoC контейнере именно это и происходит - контейнер является сущностью, что гарантирует создание одного инстанса. Обычно синглтонами делают классы на этапе настройки IoC контейнера - именно так и происходит, когда используется IoC контейнер. И вы так и не показали мне, где у меня "паттерны ради паттернов", а вместо этого пытаетесь трактовать мои фразы в свою пользу. – tym32167 Jan 12 '18 at 10:54
  • @LunarWhisper я понял, что вы так и будете изворачиваться, так и не приведя мне ничего стоящего против моего кода - оно и понятно, так как такой код вполне нормален и широко используется. Я понимаю, что очень просто прибежать и закидать чужой код насмешками, и я был бы рад, если бы они были бы хотя бы обоснованными - это хотя бы помогло мне узнать что то новое. Ну, и мне было бы все равно, если бы насмешки эти накидал новичек, но от юзера с хорошей репутацией я таки ждал более конструктивной дискуссии. Не вижу никакого смысла продолжать диалог. Всего хорошего. – tym32167 Jan 12 '18 at 11:03
  • я не вижу в своих словах никаких насмешек, а вот вы ведёте себя странно и неадекватно. Я несколько раз описал проблему вашего решения. Конкретно и чётко. Даже привёл цитату. Контейнеров внутри AppDomain может быть несколько. Публичный конструктор может быть вызван кем угодно и сколько угодно раз. У вас НЕ синглтон. Чтобы избежать этих проблем, вы должны гарантировать уникальность вашего контейнера. Как это сделать? Через синглтон. Ваше решение не является синглтоном.* – Lunar Whisper Jan 12 '18 at 14:55
  • @LunarWhisper Вы мне написали Данный пример очень наглядно демонстрирует подход "паттерны ради паттернов" - это не насмешка? Я 3й день спрашиваю, что именно в моем коде это демонстрирует, но вы мне отвечаете на другие вопросы, что я не задавал. И да, с какой стати AppDomain имеет отношение к синглтону? Вы в курсе, что с помощью аттрибута ThreadStatic можно иметь несколько экземляров статического класса в AppDomain. Это делает статический класс не синглтоном? – tym32167 Jan 12 '18 at 15:52
  • это не насмешка. Это указание на неуместность использования IoC в ответе на данный вопрос. Синглтон должен иметь не больше одного экземпляра на AppDomain. Да, это делает статический класс не синглтоном. "Одиночка" как бы намекает. Именно поэтому реализация с IoC так же не делает класс синглтоном, хотя бы потому что сам контейнер для этого должен являться синглтоном, что невозможно сделать с использованием только IoC (порочный круг). – Lunar Whisper Jan 15 '18 at 10:36
  • @LunarWhisper если это не насмешка, то ок. Мне это не было понятно из вашего первого комментария. По поводу синглтона - синглтон, как и любой другой паттерн, это не какой то конкретный код, это принцип, который следует определенной цели, назначению. Вы можете написать его статическим классом, или с IoC контейнером (или без него), или каким угодно другим методом. Естественно, можно создать второй контейнер в домене, но для этого явно надо писать код, который сломает существующее поведение. Как можно и добавить [ThreadStatic] в ваш ответ, и там тоже все сломается. – tym32167 Jan 15 '18 at 11:28
  • ваша позиция понятна. Думаю, что в словах каждого есть доля истины. :) – Lunar Whisper Jan 15 '18 at 11:37
  • @LunarWhisper я вам предлагаю абстрагироваться от вашего отношения к IoC, взглянуть на мой ответ и определить - следует ли конкретно этот написанный код цели синглтона, гарантируется ли в конкретном этом примере, без учета любого дополнительного кода, который может быть написан, что будет создан только 1 экземпляр класса? То, что эту реализацию можно сломать - это ясно как день, как и что, что сломать можно любую реализацию любого паттерна. Потому фразы типа "это не синглтон, так как можно этот код сломать" - не имеют отношения к паттернам вообще. – tym32167 Jan 15 '18 at 11:39
  • @LunarWhisper Ну, я понял, что просто наши 2 разные точки зрения столкнулись, а переубеждать наверное смысла нет друг друга. Предлагаю оставить эту дискуссию на подумать - вы над моими комментами, я над вашими :) – tym32167 Jan 15 '18 at 11:42