我有很多表可以取。现在,对于每个表,我都有一个控制器类来获取请求,应用查询参数和分页,最后返回结果。它们几乎都是一样的,只是表名不同,这导致了很多文件和很多重复。
因此,我试图创建一个端点并为所有端点提供服务。目标是从路由参数中检测表。
首先我创建了这个属性:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class ResourceAttribute : Attribute
{
public ResourceAttribute(ResourceType resourceType)
{
ResourceType = resourceType;
}
public ResourceType ResourceType { get; }
}
public enum ResourceType
{
Public = 1,
User,
Admin
}
Public
表示任何人都可以获取数据,例如:产品。 User
表示该资源可供每个登录用户使用,并且他们不应该看到其他用户的数据,例如:评论、订单。 Admin
表示此资源仅在我们的管理面板中可用,并且不公开,例如:AppSettings。
接下来,我应用属性:
[Resource(ResourceType.Public)]
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// ...
}
然后我创建了一个单例服务来存储所有资源信息:
public class ResourceService
{
private readonly Dictionary<string, RoutableResource> _resourceMap;
public ResourceService()
{
_resourceMap = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(x => x.GetTypes())
.Where(x => x.IsDefined(typeof(ResourceAttribute), false))
.ToDictionary(
GetResourceRouteName,
x => new RoutableResource
{
Name = GetResourceRouteName(x),
Type = x,
ResourceType = GetResourceRouteType(x)
});
// ...
}
public bool ResourceExists(string resourceName) => _resourceMap.ContainsKey(resourceName);
public RoutableResource? GetResource(string resourceName) => _resourceMap.GetValueOrDefault(resourceName);
}
public class RoutableResource
{
public string Name { get; set; }
public Type Type { get; set; }
public ResourceType ResourceType { get; set; }
}
这是控制器方法:
[HttpGet("{resourceName}/{id?}")]
public async Task<IActionResult> GetResource(string resourceName, string? id)
{
if (!_resourceService.ResourceExists(resourceName))
{
return NotFound();
}
RoutableResource resource = _resourceService.GetResource(resourceName)!;
// Check authorization
// Get IQueryable of the selected resource 👈 [I'm stuck here]
// Get all query params and apply them to query with the help of reflection and expressions
// Get the target ViewModel class from ResourceVM map and apply the projection
// Finally return the result
}
有一个
DbContext.Set<T>()
但它需要编译时的类型。在这个答案的帮助下,我得到了IQueryable
,但它没有用。
如果您确定它们都具有相同的属性,则创建一个接口并在所有 DBO 类型中实现它。然后,使用可以接受所有 DBO 类型的接口类型。
我也不明白为什么你不能这样做?
var tableToQuery;
if(somecondition)
tableToQuery = dbContext.A;
else
tableToQuery = dbContext.B;
tableToQuery.query();