• Как передать произвольный метод в класс для асинхронного выполнения, отслеживания статуса и получения результата?

    Набросал очень простой пример, естественно, менеджер должен быть сложнее и потокобезопасней
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication7
    {
        public abstract class Job
        {
            public Guid Id { get; private set; }
            public bool IsCompleted { get; protected set; }
    
            protected Job()
            {
                Id = Guid.NewGuid();
                IsCompleted = false;
            }
        }
    
        public class Job<TResult> : Job
        {
            public TResult Result { get; private set; }
    
            public Job(Func<TResult> workFunction)
            {
                MakeJob(workFunction);
            }
    
            private async void MakeJob(Func<TResult> func)
            {
                var result = await Task.Run(func);
                Result = result;
                IsCompleted = true;
            }
        }
    
        public class JobManager
        {
            private readonly Dictionary<Guid, Job> _jobs = new Dictionary<Guid, Job>();
    
            public Guid StartJob<TResult>(Func<TResult> func)
            {
                Job job = new Job<TResult>(func);
                _jobs.Add(job.Id, job);
                return job.Id;
            }
    
            public bool TrtGetResult<TResult>(Guid jobId, out TResult result)
            {
                if (!_jobs.ContainsKey(jobId))
                {
                    throw new ArgumentException("Работы с данным идентификатором нет в списке.", "jobId");
                }
    
                var job = _jobs[jobId];
    
                if (!job.IsCompleted)
                {
                    result = default(TResult);
                    return false;
                }
    
                var parametrizedJob = job as Job<TResult>;
                
                if (parametrizedJob == null)
                {
                    throw new ArgumentException("Ты, что, забыл какой тип возвращаемого значения тебе нужен?");
                }
    
                result = parametrizedJob.Result;
                _jobs.Remove(jobId);
                return true;
            }
        }
    
        public static class JobExamples
        {
            public static int Sum(int a, int b)
            {
                return a + b;
            }
    
            public static string Trim(string str)
            {
                return str.Trim();
            }
        }
    
        public static class FunctionalHelper
        {
            public static Func<TResult> ApplyPartial<T1, T2, T3, TResult>
            (Func<T1, T2, T3, TResult> function, T1 arg1, T2 arg2, T3 arg3)
            {
                return () => function(arg1, arg2, arg3);
            }
    
            public static Func<TResult> ApplyPartial<T1, T2, TResult>
                (Func<T1, T2, TResult> function, T1 arg1, T2 arg2)
            {
                return () => function(arg1, arg2);
            }
    
            public static Func<TResult> ApplyPartial<T1, TResult>
                (Func<T1, TResult> function, T1 arg1)
            {
                return () => function(arg1);
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var manager = new JobManager();
                var jobId1 = manager.StartJob(FunctionalHelper.ApplyPartial(JobExamples.Sum, 2, 6));
                var jobId2 = manager.StartJob(FunctionalHelper.ApplyPartial(JobExamples.Trim, "   a a aaaa  "));
    
                int job1Result;
    
                Thread.Sleep(100);
    
                if (manager.TrtGetResult(jobId1, out job1Result))
                {
                    Console.WriteLine(job1Result);
                }
    
                Console.ReadKey();
    
            }
        }
    }
  • Как передать произвольный метод в класс для асинхронного выполнения, отслеживания статуса и получения результата?

    Я правильно понял, что "десятки разных методов" никак не связаны друг с другом и относятся к разным обращениям пользователя? При этом хочется сделать один шедулер, в который складывались все эти долгие методы и при запросе пользователя о статусе его задания - сервер обращался бы в одну точку доступа, где лежат все разные работы за статусом?
  • Как наиболее эффективно выкачать несколько миллионов html страниц, при этом не потратив вечность?

    @blackx есть. И есть, конечно, всякие другие более тяжелые реализации, можно порт квартца использовать.
  • Как в WP заблокировать в цикле кнопки на форме?

    Ты имеешь в виду, что тебе нужно именно обращение по индексу? Ну дык тут всё просто - меняем IEnumerable на IList:

    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace NicklaykTestApp
    {
        /// <summary>
        /// Форма
        /// </summary>
        public partial class MainWindow : Window
        {
            /// <summary>
            /// Коллекция кнопок, которые должны быть изменены в зависимости от значения поля _masterField.
            /// </summary>
            private readonly List<Button> _slavedButtons;
    
            /// <summary>
            /// Поле, от значения которого зависит изменение доступности кнопок на форме.
            /// </summary>
            private readonly bool _masterField;
    
    
            public MainWindow()
            {
                InitializeComponent();
    
                _slavedButtons = new List<Button>
                              {
                                  this.button1,
                                  this.button2,
                                  this.button3
                              };
    
                _masterField = false; // Здесь кастомная логика вычисления поля
            }
    
            private void WindowLoaded(object sender, RoutedEventArgs e)
            {
    
                for (int i = 0; i < _slavedButtons.Count ; i++)
                {
                    _slavedButtons[i].Content = string.Format("Пример использования индекса {0}", i);
                    _slavedButtons[i].IsEnabled = _masterField;
                }
            }
    
        }
    }
  • Как в WP заблокировать в цикле кнопки на форме?

    Давай я попробую привести исчерпывающий пример, а ты скажешь, чем именно он отличается от задачи, которую необходимо решить.

    MainWindow.xaml
    <Window x:Class="NicklaykTestApp.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Loaded="WindowLoaded">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <StackPanel>
                <Button Name="button1" Content="Кнопка 1" />
                <Button Name="button2" Content="Кнопка 2" />
            </StackPanel>
    
            <Button Grid.Row="1" Name="button3" Content="Кнопка 3"></Button>
        </Grid>
    </Window>


    MainWindow.xaml.cs
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace NicklaykTestApp
    {
        /// <summary>
        /// Форма
        /// </summary>
        public partial class MainWindow : Window
        {
            /// <summary>
            /// Коллекция кнопок, которые должны быть изменены в зависимости от значения поля _masterField.
            /// </summary>
            private readonly IEnumerable<Button> _slavedButtons;
    
            /// <summary>
            /// Поле, от значения которого зависит изменение доступности кнопок на форме.
            /// </summary>
            private readonly bool _masterField;
    
    
            public MainWindow()
            {
                InitializeComponent();
    
                var buttons = new List<Button>
                              {
                                  this.button1,
                                  this.button2,
                                  this.button3
                              };
    
                _slavedButtons = buttons;
    
                _masterField = false; // Здесь кастомная логика вычисления поля
            }
    
            private void WindowLoaded(object sender, RoutedEventArgs e)
            {
                foreach (var button in _slavedButtons)
                {
                    button.IsEnabled = _masterField;
                }
            }
    
        }
    }


    Тестовое приложение: zalil.ru/34900493

    Всё просто - при назначении в xaml для кнопки атрибута Name к ней можно обращаться, как к обычному члену класса окна.

    Я всё еще хочу сказать о том, что сам сценарий, что переменная в behindcode, которая влияет на доступность кнопки - плохо. Но если очень хочется, то можно :/
  • Как в WP заблокировать в цикле кнопки на форме?

    Ок.
    Если кнопки определены заранее и их ограниченное количество, то почему не назначить каждой из них имя (в xaml - Name="btn1" и т.д.), а в конструкторе или инициализаторе формы сделать следующее:
    private List<Button> _buttons;
    public Initialize()
    {
        base.Initialize();
        buttons = new List<Button>() { this.btn1, this.btn2, ... , this.btnN };
        // здесь ваш цикл
    }


    Похоже, я всё ещё не понимаю загвоздки проблемы :/ Поле, которое будет влиять на видимость кнопок находится во View?
  • Автоопределение Generic-типов внутри Generic (Get<TEntity>() where TEntity: IEntity<TId>)?

    Vadimyan
    @Vadimyan Автор вопроса
    Понимаю, что комментирую это уже скорее для самодокументации, но вспомнил способ без необходимости вручную объявлять интерфейс для каждой сущности. Можно поступить так же, как я делал с автофабриками

    Вот примеры кода:
    /// <summary>
    /// Базовый класс для всех доменных объектов.
    /// </summary>
    [DataContract]
    public abstract class Entity<TId> : IEntity<TId> 
        where TId : struct
    {
        /// <summary>
        /// Идентификатор.
        /// </summary>
        [DataMember]
        [Key]
        public virtual TId Id { get; set; }
    
        [Repository]
        public interface Repository : IRepository<Entity<TId>, TId>
        {
        }
    }
    
    public interface IRepositoryProvider
    {
        IRepository<TEntity, TId> GetRepository<TEntity, TId>() where TEntity : Entity<TId> where TId : struct;
    }
    
    IRepositoryProvider rp = ...;
    var rep = rp.GetRepository<Department.Repository>();


    Любителям эстетики может не понравиться конструкция Department.Repository, но с отсутствием необходимости самостоятельно прописывать интерфейсы репозиториев получается вкусно.
  • Автоопределение Generic-типов внутри Generic (Get<TEntity>() where TEntity: IEntity<TId>)?

    Vadimyan
    @Vadimyan Автор вопроса
    Нет, сценарий использования подразумевает, что пользователь будет ждать именно TEntity. То есть при запросе репозитория депортаментов юзер сразу хочет видеть департаменты после Get или GetAll.
    Но идея сделать наоборот мне понравилась. Я немного распишу сценарий чтобы обосновать своё решение. Библиотека предназначена для ускорения разработки при работе с EF Code first в случае использования IoC в проекте. Пользователь:
    1. наследует доменные объекты от Entity<TId>.
    2. Вызывает в контейнере на этапе инициализации
    container.RegisterRepositories(/* тут скорее всего assembly с entity-классами */)


    При этом в пункте 2 происходит следующее - для каждого класса в assembly, относледованного от Entity<TId> генерируется код класса-наследника от RepositoryBase<T, TId> и этот класс регистрируется в контейнере под интерфейсом IRepository<T, TId>
    То есть пользователь завёл просто ентити-классы, а у него уже есть такой чудесный набор методов для каждого:
    public interface IRepository<T, in TID> : IRepository
        where TID : struct 
    {
        T Get(TID id);
        IList<T> Get(Action<RepositoryQuery<T>> query, out int totalCount);
        IList<TMap> Get<TMap>(Action<RepositoryQuery<TMap>> queryAction, out int totalCount) where TMap : class;
        IList<TMap> Get<TMap>(Action<RepositoryQuery<T>> queryAction, Action<RepositoryQuery<TMap>> mapAction, out int totalCount) where TMap : class;
        IList<T> Get(Action<RepositoryQuery<T>> query);
        IList<TMap> Get<TMap>(Action<RepositoryQuery<TMap>> queryAction) where TMap : class;
        IList<TMap> Get<TMap>(Action<RepositoryQuery<T>> queryAction, Action<RepositoryQuery<TMap>> mapAction) where TMap : class;
        IList<T> GetAll();
        T Save(T entity);
        void Delete(T entity);
    }

    Всё готово и не нужно реализовывать однообразный доступ к данным, просто берешь и

    var repository = _repProvider.GetRepository<Department>();
    repository.Get(q => q.Filter(cd => !cd.IsDeleted).Include("Employees", "Organization", "SomeAnotherLinkedPropertyInModel"));


    ----------------------------------------------------------------
    Теперь решение.
    Если пользователь хочет простой доступ из коробки, то он делает
    container.RegisterRepositories(/* тут скорее всего assembly с entity-классами */)

    И при необходимости получить репозиторий делает так:
    var repository = _repProvider.GetRepository<Department, Int32>();

    Не идеально, но безопасно. Проверка, что ключ у Department именно Int32 есть, всё ок.
    Второй способ работы с библиотекой состоит в том, что пользователь сам пишет интерфейсы репозиториев, помечая их специальным аттрибутом:
    [Repository]
    public interface IUserRepository : IRepository<User, Int32> { }
    
    [Repository]
    public interface IDepartmentRepository : IRepository<Department, Int32> { }


    И при инициализации на шаге 2 для каждого класса, реализующего Entity ищется соответствие среди интерфейсов, помеченных RepositoryAttribute, и автосгенерированный класс регистрируется и как IRepository и под своим собственным интерфейсом (IDepartmentRepository).

    При этом интерфейс провайдера репозиториев будет таким:
    public interface IRepositoryProvider
    {
        TRespositoryType GetRepository<TRespositoryType>() where TRespositoryType : IRepository;
        IRepository<TEntity, TId> GetRepository<TEntity, TId>() where TEntity : Entity<TId> where TId : struct;
    }


    Это не идеальное решение, но в первой итерации такую реализацию можно протестировать на реальных пользователях :)

    Спасибо за помощь, свежего взгляда на проблему мне не хватает при работе в одиночку.
  • Автоопределение Generic-типов внутри Generic (Get<TEntity>() where TEntity: IEntity<TId>)?

    Vadimyan
    @Vadimyan Автор вопроса
    Спасибо за ответ.
    dynamic действительно является одним из решений проблемы, но поскольку RepositoryProvider подразумевается как API для внешних пользователей библиотеки, то такое решение может испугать пользователя класса.
    Собственно, речь идёт как раз о косметическом улучшении для пользователя - нет необходимости указывать параметр в generic, который и так можно извлечь.
    Я поигрался с твоим вариантом, попытавшись как-нибудь скастать результат к нужному типу, но, видимо, само устройство interface IRepository мешает получить необходимый мне результат.
    Корень зла как раз в лишнем параметре IRepository, который принимает T : Entity, но при этом вынужден получать TId извне, хотя может извлечь его из Entity.