ASP.NET MVC泛型类静态属性设置及泛型方法静态属性可行性咨询
在ASP.NET MVC泛型类/方法中处理静态属性及实体公共字段的Insert实现
一、泛型类中设置静态属性的方法
其实泛型类的静态属性和普通类的静态属性逻辑类似,但要注意泛型类型参数不同时,静态属性是完全独立的——也就是说MyClass<T1>和MyClass<T2>的静态属性属于各自的“静态实例”,互相不会干扰。
举个直观的例子:
public class BaseRepository<T> where T : class { // 泛型类的静态属性,不同T对应不同的独立实例 public static string DefaultConnectionString { get; set; } // 其他仓储成员... }
你可以直接通过具体的泛型类型来赋值:
BaseRepository<User>.DefaultConnectionString = "UserDbConnection"; BaseRepository<Order>.DefaultConnectionString = "OrderDbConnection";
这样User仓储和Order仓储的静态连接字符串是分开存储的,不会出现覆盖问题。
二、能否在泛型方法中设置静态属性?
当然可以,但要分两种情况注意细节:
- 如果是泛型类的静态属性:直接在泛型方法里赋值就行,和普通方法操作静态属性的逻辑完全一致;
- 如果是非泛型类中泛型方法操作类的静态属性:静态属性是类级别的,和泛型参数无关,多次调用不同泛型参数的方法会覆盖静态属性的值。
举个示例:
public class RepositoryHelper { public static string GlobalConnectionString { get; set; } // 非泛型类里的泛型方法,操作类的静态属性 public static void ConfigureRepository<T>() where T : class { GlobalConnectionString = $"ConnectionFor{typeof(T).Name}"; } }
调用时:
RepositoryHelper.ConfigureRepository<User>(); // 此时GlobalConnectionString的值是"ConnectionForUser" RepositoryHelper.ConfigureRepository<Order>(); // 此时GlobalConnectionString会被覆盖为"ConnectionForOrder"
如果需要不同泛型参数对应不同的静态值,建议还是用泛型类的静态属性来实现。
三、针对含公共字段的实体实现通用Insert方法
你的场景是所有实体都有CreateDateTime、CreateUser这类共享字段,要实现通用的Insert方法,最优雅的方式是先通过接口或基类约束这些公共字段,再在泛型仓储里利用约束统一处理。
步骤1:定义公共约束接口
先创建一个接口来规范这些公共字段:
public interface IAuditableEntity { DateTime CreateDateTime { get; set; } string CreateUser { get; set; } // 如果有UpdateDateTime、UpdateUser这类字段也可以一并加入 }
然后让所有实体都实现这个接口:
public class User : IAuditableEntity { public int Id { get; set; } public string UserName { get; set; } // 实现接口的公共字段 public DateTime CreateDateTime { get; set; } public string CreateUser { get; set; } }
步骤2:在泛型仓储中实现通用Insert
接下来给泛型基仓储的类型参数加上IAuditableEntity约束,这样就能在Insert方法里直接访问这些公共字段了:
public class BaseRepository<T> where T : class, IAuditableEntity { protected readonly DbContext Context; protected readonly DbSet<T> DbSet; public BaseRepository(DbContext context) { Context = context; DbSet = context.Set<T>(); } public virtual void Insert(T entity) { // 统一设置公共字段的值 entity.CreateDateTime = DateTime.Now; // 这里可以根据实际场景获取当前登录用户,比如从身份认证信息中读取 entity.CreateUser = HttpContext.Current.User.Identity.Name; DbSet.Add(entity); Context.SaveChanges(); } }
这样不管是User、Order还是其他实体,只要实现了IAuditableEntity,调用Insert方法时都会自动设置公共字段,不用重复写逻辑。
备选方案:用反射处理(不推荐,仅作兼容)
如果因为历史原因没法让实体实现接口,也可以用反射来设置公共字段,不过性能会比接口约束差一些:
public virtual void Insert(T entity) { // 反射设置CreateDateTime var createDateTimeProp = typeof(T).GetProperty("CreateDateTime"); if (createDateTimeProp != null && createDateTimeProp.CanWrite) { createDateTimeProp.SetValue(entity, DateTime.Now); } // 反射设置CreateUser var createUserProp = typeof(T).GetProperty("CreateUser"); if (createUserProp != null && createUserProp.CanWrite) { createUserProp.SetValue(entity, HttpContext.Current.User.Identity.Name); } DbSet.Add(entity); Context.SaveChanges(); }
还是更推荐接口约束的方式,类型安全且性能更优。
内容的提问来源于stack exchange,提问作者Maldio Robert
相关产品推荐
相关产品推荐

