C# 如何在泛型 class 中访问泛型类型的成员?
C# How to access member of generic type in a generic class?
public class DS2DbContext : DbContext
{
public DbSet<DocumentFileData> DocumentFileData { get; set; }
public DbSet<WatermarkFileData> WatermarkFileData { get; set; }
public DS2DbContext(DbContextOptions<DS2DbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class FileDataController<T> : ODataController where T : class
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS1061: 'T' does not contain a definition for 'ID'
// and no accessible extension method 'ID' accepting a
// first argument of type 'T' could be found
return SingleResult.Create(result);
}
}
如何正确(如果可能,优雅地)访问类型为 T 的变量成员并获取它们的值?
更新
当我使用这样的界面时:
public class FileDataController<T> : ODataController where T : IFileData
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS0452: The type 'T' must be a reference type in
// order to use it as parameter 'TEntity' in the generic
// type or method 'DbSet<TEntity>'
return SingleResult.Create(result);
}
}
调用 _context.Set() 时出现错误。
我想到的是
var result = _context.Set<T>()
.Where(x => (Guid) x.GetType().GetProperty("ID").GetValue(x) == ID);
但这看起来非常复杂。
背后的故事
是我必须根据数据的语义(需求文档或水印)将完全相同结构的数据存储在两个不同的数据库中 table。因为我在做 Blazor,首先编码,我需要有两个不同的 classes 才能正确创建两个 tables 并通过两个不同的控制器处理它们。然而,这些控制器共享完全相同的代码和完全相同的底层数据结构。这是一个仅实现 ID 的简化示例:
public interface IFileData
{
public Guid ID { get; set; }
}
using System.ComponentModel.DataAnnotations;
public class FileData : IFileData
{
[Key]
public Guid ID { get; set; } = Guid.Empty;
}
public class DocumentFileData : FileData { }
public class WatermarkFileData : FileData { }
因此,有一个用于 DocumentFileData 的控制器和一个用于 WatermarkFileData 的控制器,可以为每种类型的数据访问适当的数据库 table。但是,所有基础操作都是相同的。所以我希望能够通过一个通用的 class 来解决这个问题,这样我就不必在每次文件数据处理发生变化时对两个不同的 class 进行相同的更改。
正如@GuruSiton 正确评论的那样,您应该在其中引入一个带有 ID
的接口:
interface IClassWithId
{
Guid ID { get; } // Notice that you only require get - the implementer can choose how the ID get init'd - private / c'tor / etc
}
public class FileDataController<T> : ODataController where T : IClassWithId
{ ... }
这假设您可以制作 - 或要求 - 所有 类 用作 T 来实现 IClassWithId
。如果您坚持使用现有的 类 定义自己的 ID 并且无法更改它们,那么您必须求助于反射,正如@NaeemAhmed 所暗示的那样。
因为你有一个公共接口公开 ID
属性 提供相应的泛型约束 - (你需要指定泛型类型约束 - 接口和 class
):
public class FileDataController<T> : ODataController where T : class, IFileData
public class DS2DbContext : DbContext
{
public DbSet<DocumentFileData> DocumentFileData { get; set; }
public DbSet<WatermarkFileData> WatermarkFileData { get; set; }
public DS2DbContext(DbContextOptions<DS2DbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
public class FileDataController<T> : ODataController where T : class
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS1061: 'T' does not contain a definition for 'ID'
// and no accessible extension method 'ID' accepting a
// first argument of type 'T' could be found
return SingleResult.Create(result);
}
}
如何正确(如果可能,优雅地)访问类型为 T 的变量成员并获取它们的值?
更新
当我使用这样的界面时:
public class FileDataController<T> : ODataController where T : IFileData
{
private readonly DS2DbContext _context;
private readonly ILogger<FileDataController<T>> _logger;
public FileDataController(DS2DbContext dbContext,
ILogger<FileDataController<T>> logger)
{
_logger = logger;
_context = dbContext;
}
[EnableQuery]
public SingleResult<T> Get([FromODataUri] Guid ID)
{
var result = _context.Set<T>().Where(i => i.ID == ID);
// Error CS0452: The type 'T' must be a reference type in
// order to use it as parameter 'TEntity' in the generic
// type or method 'DbSet<TEntity>'
return SingleResult.Create(result);
}
}
调用 _context.Set() 时出现错误。
我想到的是
var result = _context.Set<T>()
.Where(x => (Guid) x.GetType().GetProperty("ID").GetValue(x) == ID);
但这看起来非常复杂。
背后的故事
是我必须根据数据的语义(需求文档或水印)将完全相同结构的数据存储在两个不同的数据库中 table。因为我在做 Blazor,首先编码,我需要有两个不同的 classes 才能正确创建两个 tables 并通过两个不同的控制器处理它们。然而,这些控制器共享完全相同的代码和完全相同的底层数据结构。这是一个仅实现 ID 的简化示例:
public interface IFileData
{
public Guid ID { get; set; }
}
using System.ComponentModel.DataAnnotations;
public class FileData : IFileData
{
[Key]
public Guid ID { get; set; } = Guid.Empty;
}
public class DocumentFileData : FileData { }
public class WatermarkFileData : FileData { }
因此,有一个用于 DocumentFileData 的控制器和一个用于 WatermarkFileData 的控制器,可以为每种类型的数据访问适当的数据库 table。但是,所有基础操作都是相同的。所以我希望能够通过一个通用的 class 来解决这个问题,这样我就不必在每次文件数据处理发生变化时对两个不同的 class 进行相同的更改。
正如@GuruSiton 正确评论的那样,您应该在其中引入一个带有 ID
的接口:
interface IClassWithId
{
Guid ID { get; } // Notice that you only require get - the implementer can choose how the ID get init'd - private / c'tor / etc
}
public class FileDataController<T> : ODataController where T : IClassWithId
{ ... }
这假设您可以制作 - 或要求 - 所有 类 用作 T 来实现 IClassWithId
。如果您坚持使用现有的 类 定义自己的 ID 并且无法更改它们,那么您必须求助于反射,正如@NaeemAhmed 所暗示的那样。
因为你有一个公共接口公开 ID
属性 提供相应的泛型约束 - (你需要指定泛型类型约束 - 接口和 class
):
public class FileDataController<T> : ODataController where T : class, IFileData