Как построить функцию делегата для запуска разных строк кода в каждом случае использования - PullRequest
0 голосов
/ 11 января 2019

Я новичок в делегатах, Func <> и Action <>. Я попытался прочитать несколько постов переполнения стека и документации, но безрезультатно.

Вопрос так же нацелен на то, чтобы понять, как я должен осмыслить проблему, или если я должен думать совершенно иначе. Во всяком случае здесь мы идем ...

У меня есть класс BaseRepository, подключенный к mongoDB. Ниже мы видим пример одного из методов, связанных с этим BaseClass

public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
    return await DefaultCollection
        .Find(Filter.Empty)
        .Skip(skip)
        .Limit(limit)
        .ToListAsync();
}

Что я хочу сделать, так это реализовать некоторое отслеживание, которое отслеживает время связи в истекшем миллисекунде между API и БД, чтобы я мог лучше расставить приоритеты в оптимизации. Таким образом, простой метод должен выглядеть следующим образом:

public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
    var sw = new Stopwatch();

    sw.Start();
    var result = await DefaultCollection
        .Find(Filter.Empty)
        .Skip(skip)
        .Limit(limit)
        .ToListAsync();
    sw.Stop();

    Logger.PushContext("Elapsed Milles to DB", sw.ElapsedMilliseconds);
    return result;

}

Это было бы, однако, уместно записать в каждый метод, так что я задавался вопросом, какой может быть лучшая практика, и думал о том, чтобы сделать что-то вроде следующего: ПРЕДУПРЕЖДАЮЩИЙ ДОСТУП К КОДУ PSEUDO;)

enter image description here

public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
    {
        return await DefaultCollection
            .Find(Filter.Empty)
            .Skip(skip)
            .Limit(limit)
            .ToListAsync();
    }

    /// <summary>
    /// PSEUDO CODE. ONLY PSEUDO CODE
    /// </summary>
    /// <returns></returns>
    protected async Task<TResult> ExecuteCmd(* Inject code into this method* injectedCode)
    {

        var sw = new Stopwatch();

        sw.Start();

        var result = injectedCode.Run();

        sw.Stop();

        Logger.PushContext("Elapsed milli to db", sw.ElapsedMilliseconds);

        return result;
    }

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

Может быть, что-то вроде этого:

public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
    return await ExecuteCmd(c =>
    {
        DefaultCollection
            .Find(Filter.Empty)
            .Skip(skip)
            .Limit(limit)
            .ToListAsync();
    });
}

где этот код запускается внутри ExecuteCmd как «injectedCode.run».

Я представлял, что ExecuteCmd может принимать операторы разного типа и, соответственно, возвращать разные результаты ...

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

С наилучшими пожеланиями! Заранее спасибо

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

Редактировать Для справок в будущем это обновленный базовый репозиторий, надеюсь, он может помочь другим!

public abstract class MongoReadmodelRepository<TEntity> : IMongoReadmodelRepository<TEntity> where TEntity : IEntity
{
    protected readonly ILogger Logger;
    protected readonly IMongoDatabase DefaultDatabase;
    protected readonly string CollectionName = $"rm-{typeof(TEntity).Name.ToLower()}";
    protected IMongoCollection<TEntity> DefaultCollection =>
        DefaultDatabase.GetCollection<TEntity>(CollectionName);


    protected UpdateDefinitionBuilder<TEntity> Update => Builders<TEntity>.Update;
    protected SortDefinitionBuilder<TEntity> Sort => Builders<TEntity>.Sort;
    protected FilterDefinitionBuilder<TEntity> Filter => Builders<TEntity>.Filter;
    protected ProjectionDefinitionBuilder<TEntity> Projection => Builders<TEntity>.Projection;

    public MongoReadmodelRepository(IMongoClient client, IOptions<ProjectionsPersistenceConfiguration> config, ILogger logger)
    {
        Logger = logger;
        DefaultDatabase = client.GetDatabase(config.Value.DefaultProjectionsDatabaseName);

        if (!CollectionExists(DefaultDatabase, CollectionName))
            DefaultDatabase.CreateCollection(CollectionName);
    }


    public async Task<bool> Delete(Guid id)
    {
        Logger.Information("Trying to delete {Entity} with {Id}", typeof(TEntity).Name, id);
        return (await DefaultCollection.DeleteOneAsync(Filter.Eq(x => x.Id, id)))
            .IsAcknowledged;
    }

    public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
    {
        return await ExecuteCmd(
            () =>
                DefaultCollection
                    .Find(Filter.Empty)
                    .Skip(skip)
                    .Limit(limit)
                    .ToListAsync()
        );
    }

    public async Task<TEntity> GetByIndex(int index, int collectionSize)
    {
        return await DefaultCollection.Find(Filter.Empty)
            .Skip(index)
            .Limit(1)
            .FirstOrDefaultAsync();

    }

    public async Task<IEnumerable<TEntity>> GetPaged(int page, int pageSize)
    {
        return await GetAll(page * pageSize, pageSize);
    }

    public async Task<TEntity> GetById(Guid id)
    {
        return await DefaultCollection.Find(b => b.Id == id).SingleOrDefaultAsync();
    }

    public async Task<Guid> Insert(TEntity entity)
    {
        await DefaultCollection.InsertOneAsync(entity, new InsertOneOptions());

        Logger.Information("Saved {@Entity}", entity);
        return entity.Id;
    }

    private bool CollectionExists(IMongoDatabase db, string collectionName)
    {
        var filter = new BsonDocument("name", collectionName);
        var collections = db.ListCollections(new ListCollectionsOptions { Filter = filter });
        return collections.Any();
    }

    protected async Task<TResult> ExecuteCmd<TResult>(Func<Task<TResult>> query)
    {
        var sw = new Stopwatch();

        //Start stopwatch
        sw.Start();

        var result = await query();

        sw.Stop();

        Console.WriteLine("Logging execution time between API and mongoDB: Execution time in millis = " + sw.ElapsedMilliseconds);

        return result;
    }
}

1 Ответ

0 голосов
/ 12 января 2019

Это довольно простой перевод. Вам нужен метод, который не принимает аргументов (как показано injectedCode.Run(), является ожидаемым и поэтому должен возвращать ожидаемый объект, такой как Task<T>, и имеет результат типа TResult. Таким образом, ваш параметр делегата должен иметь тип Func<Task<TResult>>.

protected async Task<TResult> ExecuteCmd<TResult>(Func<Task<TResult>> query)
{
    // ...
    var result = await query();
    // ....
}

Затем вы можете вызвать это, превратив запрос внутри GetAll в совместимую лямбду. ToListAsync<T> возвращает Task<List<T>>, который можно использовать как Task<IEnumerable<T>> (из-за того, как асинхронные методы транслируются компилятором). Это T - это то, что находится в DefaultCollection, то есть TEntity, а объект задачи, возвращаемый ExecuteCmd, - это то, что ожидается в GetAll.

public async Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
    return await ExecuteCmd(
        () => DefaultCollection
            .Find(Filter.Empty)
            .Skip(skip)
            .Limit(limit)
            .ToListAsync()
        );
}

skip и limit перехвачены лямбдой, поэтому делегат не принимает аргументов.

Вы можете напрямую передать объект задачи следующим образом:

public Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
    return ExecuteCmd(
        () => DefaultCollection
            .Find(Filter.Empty)
            .Skip(skip)
            .Limit(limit)
            .ToListAsync()
        );
}

Это может выглядеть чище, потому что вам не нужны ключевые слова async и await. Недостатком является то, что вы теряете информацию стека в исключениях, и может быть сложнее отследить, где происходит исключение. Я думаю, что большинство экспертов посоветовали бы против этого, но решение в конечном итоге остается за вами.

...