Ответы пользователя по тегу .NET
  • Микросервисы на .net - оптимальный протокол "общения"?

    Ogoun
    @Ogoun
    Programmer
    Раньше использовать брокеры сообщений (RabbitMQ, ActiveMQ, NATS, ZeroMQ), в итоге пришел к общению посредством ServiceDiscovery.
    Т.е. в сети есть сервис который хранит таблицу адресов других сервисов и периодически ее раздает. Плюс проходит по таблице и пингует не пропал ли какой сервис из доступа. Остальные сервисы общаются друг с другом напрямую, получая адрес нужного сервиса из таблицы. При этом, если запущено несколько одинаковых сервисов, то передача данных им идет через round-roobin балансировку (конечно это для stateless сервисов, если есть состояние придется усложнить алгоритм общения).
    Транспорт может быть любой, использую REST или протокол поверх TCP как более быстрый вариант.
    Ответ написан
    Комментировать
  • Как сделать solution explorer в С#?

    Ogoun
    @Ogoun
    Programmer
    Microsoft.Build
    Microsoft.Build.Engine.dll

    Для компиляции и работы с проектами советую использовать Microsoft.Build.Framework из nuget, он понимает синтаксис C# 7, у умеет работать с последними версиями .NET.

    Таким способом можно читать проекты из решения:
    public static void DbgReadSolution(string solution_file)
            {
                var wrapperContent = Microsoft.Build.BuildEngine.SolutionWrapperProject.
                    Generate(solution_file, toolsVersionOverride: null, projectBuildEventContext: null);
                byte[] rawWrapperContent = Encoding.Unicode.GetBytes(wrapperContent.ToCharArray());
                using (MemoryStream memStream = new MemoryStream(rawWrapperContent))
                using (XmlTextReader xmlReader = new XmlTextReader(memStream))
                {
                    var proj = ProjectCollection.GlobalProjectCollection.LoadProject(xmlReader);
                    int i = 0;
                    foreach (ProjectItem referencedProjectNode in GetOrderedProjectReferencesFromWrapper(proj))
                    {
                        Log.Info(String.Format("{0}{1}: {2}", Environment.NewLine, i++, referencedProjectNode.EvaluatedInclude));
                        string referencedProjectPath = Path.Combine(Path.GetDirectoryName(solution_file), referencedProjectNode.EvaluatedInclude);
                        var referencedProject = ProjectCollection.GlobalProjectCollection.LoadProject(referencedProjectPath);
                        Log.Info("\tReferenced Assemblies:");
                        foreach (ProjectItem referencedAssembly in GetReferencedAssemblies(referencedProject))
                        {
                            Log.Info("\t\t{0}", referencedAssembly.EvaluatedInclude);
                        }
                        Log.Info(String.Format("{0}\tSource Files:", Environment.NewLine));
                        foreach (ProjectItem includedSourceFile in GetSourceFiles(referencedProject))
                        {
                            Log.Info("\t\t{0}", includedSourceFile.EvaluatedInclude);
                        }
                    }
                    ProjectCollection.GlobalProjectCollection.UnloadAllProjects();
                }
            }
    Ответ написан
    Комментировать
  • Разумен ли self-hosting ASP.NET Web API приложения в службе Windows? Кто-нибудь использовал такое в проде?

    Ogoun
    @Ogoun
    Programmer
    Использую именно такой подход в проде, сервисы работаю как виндовс-службы, при старте сервис выбирает себе свободный порт и поднимает self-host webapi, по которому к нему может обращаться любой другой сервис. По скорости пока хватает, но планирую перейти на свой бинарный протокол сразу поверх TCP (по тестам до 20000 RPS получается добраться, на webapi до такого не дотянуть).
    Ответ написан
    7 комментариев
  • Как вынести логику работы в отдельные .dll? И как такое называется?

    Ogoun
    @Ogoun
    Programmer
    Специально для этих целей в стандартные библиотеки входи MEF
    Это намного проще чем писать свои host приложения на основе AppDomain.
    Ответ написан
    1 комментарий
  • Как читать и записывать xlsx стандартными средствами .NET?

    Ogoun
    @Ogoun
    Programmer
    Из всех библиотек которые перепробовал лучшим выбором, на мой взгляд, является NPOI, основные плюсы - не требует наличия офиса на машине, и умеет работать со старыми форматами (xls), в отличие от того же EPPlus.
    Для работы с Excel из nuget'а нужно установить сам NPOI и NPOI.ExcelExtend
    Ответ написан
    Комментировать
  • Есть ли такой реализованный список на C#?

    Ogoun
    @Ogoun
    Programmer
    Для решения подобной задачи я использовал свое решение, где делал односвязный список объектов, обертывающих мой объект и добавляющих поле с временем удаления. Класс со списком объектов содержит один таймер, из Threading. При добавлении нового объекта он вставляется с сортировкой по дате, т.е. первый объект в списке всегда с самым коротким временем жизни, при добавлении/удалении перерасчитывается таймер. В итоге ресурсов потребляется мало, но сложность вставки/удаления O(N)

    Вот упрощенный рабочий пример:
    internal class TemporaryObject
    {
        private static long _counter = 0;
        public long Key { get; private set; }
        public TemporaryObject()
        {
            Key = Interlocked.Increment(ref _counter);
        }
        /// <summary>
        /// Событие при завершении ожидания
        /// </summary>
        public Action Callback;
        /// <summary>
        /// Срок истечения ожидания
        /// </summary>
        public DateTime ExpirationDate;
        /// <summary>
        /// Следующий объект с ближайшей датой окончания ожидания
        /// </summary>
        public TemporaryObject Next;
    }
    
    public class TemporaryObjectPool
    {
        private readonly object _locker = new object();
        /// <summary>
        /// Таймер. Один на всех
        /// </summary>
        private Timer _timer;
        /// <summary>
        /// Объект с ближайшей датой окончания ожидания
        /// </summary>
        private TemporaryObject _current = null;
        /// <summary>
        /// Переустановка таймера
        /// </summary>
        private void ResetTimer()
        {
            if (null != _current)
            {
                var diff = (_current.ExpirationDate - DateTime.Now).TotalMilliseconds;
                if (diff < 0) diff = 0;
                _timer.Change((int)diff, Timeout.Infinite);
            }
            else
            {
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
            }
        }
    
        public TemporaryObjectPool()
        {
            _timer = new Timer(state =>
            {
                Action action = null;
                lock (_locker)
                {
                    if (null != _current)
                    {
                        // Получаем событие для исполнения
                        action = _current.Callback;
                        // Находим следующий ожидающий объект
                        _current = _current.Next;
                        // Перезадание таймера
                        ResetTimer();
                    }
                }
                // Вызов события ожидавшего даты
                if (null != action)
                {
                    ThreadPool.QueueUserWorkItem(s => action());
                }
            }, null, Timeout.Infinite, Timeout.Infinite);
        }
    
        /// <summary>
        /// Добавление ожидающего объекта
        /// </summary>
        /// <param name="insert"></param>
        internal long Push(TemporaryObject insert)
        {
            lock (_locker)
            {
                // Если пул пуст, то добавляемое событие становится корневым
                if (null == _current)
                {
                    _current = insert;
                }
                else
                {
                    // Если пул не пуст
                    var cursor = _current;
                    TemporaryObject prev = null;
                    // Поиск места для вставки, сложность вставки O(n) в худшем случае
                    do
                    {
                        if (DateTime.Compare(cursor.ExpirationDate, insert.ExpirationDate) > 0)
                        {
                            insert.Next = cursor;
                            if (null == prev)
                            {
                                _current = insert;
                            }
                            else
                            {
                                prev.Next = insert;
                            }
                            break;
                        }
                        prev = cursor;
                        cursor = cursor.Next;
                        if (cursor == null)
                        {
                            prev.Next = insert;
                        }
                    } while (cursor != null);
                }
                ResetTimer();
            }
            return insert.Key;
        }
    
        public void Remove(long key)
        {
            lock (_locker)
            {
                if (_current == null) return;
                bool removed = false;
                if (_current.Key == key)
                {
                    _current = _current.Next;
                    removed = true;
                }
                else
                {
                    var prev = _current;
                    var next = _current.Next;
                    while (next != null)
                    {
                        if (next.Key == key)
                        {
                            prev.Next = next.Next;
                            removed = true;
                            break;
                        }
                        prev = next;
                        next = next.Next;
                    }
                }
                if (removed)
                {
                    ResetTimer();
                }
            }
        }
    }


    И использование
    var pool = new TemporaryObjectPool();
    pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#1 removed"), ExpirationDate = DateTime.Now.AddSeconds(5) });
    pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#2 removed"), ExpirationDate = DateTime.Now.AddSeconds(10) });
    pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#3 removed"), ExpirationDate = DateTime.Now.AddSeconds(15) });
    Ответ написан
    Комментировать
  • Как использовать переменные (SqlParameter) в запросе c ALTER TABLE из .NET приложения?

    Ogoun
    @Ogoun Автор вопроса
    Programmer
    Красивого варианта не нашел, сделал следующим образом:
    1. Создаются новые поля, всем разрешается иметь значения NULL (1 запрос)
    2. Таблица обновляется, полям которые должны быть NOT NULL проставляются значения по умолчанию (1 запрос)
    Можно полноценно использовать переменные
    UPDATE [TABLE_NAME] SET [COLUMN_NAME]=@COLUMN_DEFAULT_VALUE

    3. Обновляются поля которые должны быть NUTT NULL (1 запрос на каждое поле)
    ALTER TABLE [TABLE_NAME] ALTER COLUMN [COLUMN_NAME] COLUMN_TYPE NOT NULL
    Ответ написан
    Комментировать
  • C# Как из другого потока обновить control на форме?

    Ogoun
    @Ogoun
    Programmer
    Если поток выполняется где-то в бизнес-слое и не знает про форму, то можно использовать контекст синхронизации. Вот так:

    При создании формы:
    SynchronizationContext uiContext = SynchronizationContext.Current;
    Thread thread = new Thread(Run);
    // Запустим поток и установим ему контекст синхронизации,
    // таким образом этот поток сможет обновлять UI
    thread.Start(uiContext);


    Код потока:
    private void Run(object state)
        {
            // вытащим контекст синхронизации из state'а
            SynchronizationContext uiContext = state as SynchronizationContext;
             // говорим что в UI потоке нужно выполнить метод UpdateUI 
             // и передать ему в качестве аргумента строку
             uiContext.Post(UpdateUI, "Hello world!");
        }


    И код который выполняет действие по изменению UI
    /// <summary>
    /// Этот метод исполняется в основном UI потоке
    /// </summary>
    private void UpdateUI(object state)
    {
        sampleListBox.Items.Add((string)state);
    }


    При этом никаких beginInvoke'ов в методе UpdateUI уже не потребуется, т.к. код однозначно исполняется в UI потоке.
    Ответ написан