如何构造委托函数以便在每个用例中 运行 不同的代码行
How to construct delegate function in order to run different lines of code in each use case
我是委托 Func<> 和 Action<> 的新手。我已尝试阅读多个堆栈溢出 post 和文档,但无济于事。
这个问题同样旨在了解我应该如何将问题概念化,或者我是否应该以完全不同的方式思考。无论如何,我们走了...
我有一个 BaseRepository class 连接到 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;
}
然而,将其写入每个方法会很繁琐,所以我想知道最佳实践是什么,并考虑制作如下内容:警告伪代码传入;)
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 可以采用不同类型的语句并分别 return 不同的结果...
请让我知道这是否是模棱两可的方式。如果是的话,我很抱歉,如果我问了一些更强调的问题,请告诉我,这样我就可以重新措辞。
此致!提前致谢
整个基础存储库都可以在这里看到以供参考:
编辑
这是更新后的基础存储库,希望对其他人有所帮助!
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;
}
}
这是一个相当简单的翻译。您想要一个不带参数的方法(如 injectedCode.Run()
所示,是可等待的,因此必须 return 一个像 Task<T>
这样的可等待对象,并且具有 TResult
类型的结果。因此,您的委托参数应为 Func<Task<TResult>>
.
类型
protected async Task<TResult> ExecuteCmd<TResult>(Func<Task<TResult>> query)
{
// ...
var result = await query();
// ....
}
然后您可以通过将 GetAll
中的查询转换为兼容的 lambda 来调用它。 ToListAsync<T>
returns a Task<List<T>>
可以用作 Task<IEnumerable<T>>
(因为异步方法是如何被编译器翻译的)。 T
是 DefaultCollection
中的内容,即 TEntity
中的内容,ExecuteCmd
编辑的任务对象 return 是 GetAll
中等待的内容。 =30=]
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
被 lambda 捕获,这就是委托不接受参数的原因。
您可以像这样直接传递任务对象:
public Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
return ExecuteCmd(
() => DefaultCollection
.Find(Filter.Empty)
.Skip(skip)
.Limit(limit)
.ToListAsync()
);
}
这看起来更简洁,因为您不需要 async
和 await
关键字。缺点是您会丢失异常中的堆栈信息,并且更难追踪异常发生的位置。我认为大多数专家会反对这样做,但最终决定权在您。
我是委托 Func<> 和 Action<> 的新手。我已尝试阅读多个堆栈溢出 post 和文档,但无济于事。
这个问题同样旨在了解我应该如何将问题概念化,或者我是否应该以完全不同的方式思考。无论如何,我们走了...
我有一个 BaseRepository class 连接到 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;
}
然而,将其写入每个方法会很繁琐,所以我想知道最佳实践是什么,并考虑制作如下内容:警告伪代码传入;)
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 可以采用不同类型的语句并分别 return 不同的结果...
请让我知道这是否是模棱两可的方式。如果是的话,我很抱歉,如果我问了一些更强调的问题,请告诉我,这样我就可以重新措辞。
此致!提前致谢
整个基础存储库都可以在这里看到以供参考:
编辑 这是更新后的基础存储库,希望对其他人有所帮助!
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;
}
}
这是一个相当简单的翻译。您想要一个不带参数的方法(如 injectedCode.Run()
所示,是可等待的,因此必须 return 一个像 Task<T>
这样的可等待对象,并且具有 TResult
类型的结果。因此,您的委托参数应为 Func<Task<TResult>>
.
protected async Task<TResult> ExecuteCmd<TResult>(Func<Task<TResult>> query)
{
// ...
var result = await query();
// ....
}
然后您可以通过将 GetAll
中的查询转换为兼容的 lambda 来调用它。 ToListAsync<T>
returns a Task<List<T>>
可以用作 Task<IEnumerable<T>>
(因为异步方法是如何被编译器翻译的)。 T
是 DefaultCollection
中的内容,即 TEntity
中的内容,ExecuteCmd
编辑的任务对象 return 是 GetAll
中等待的内容。 =30=]
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
被 lambda 捕获,这就是委托不接受参数的原因。
您可以像这样直接传递任务对象:
public Task<IEnumerable<TEntity>> GetAll(int skip = 0, int limit = 100)
{
return ExecuteCmd(
() => DefaultCollection
.Find(Filter.Empty)
.Skip(skip)
.Limit(limit)
.ToListAsync()
);
}
这看起来更简洁,因为您不需要 async
和 await
关键字。缺点是您会丢失异常中的堆栈信息,并且更难追踪异常发生的位置。我认为大多数专家会反对这样做,但最终决定权在您。