c# 泛型类型和存储库模式

Posted

技术标签:

【中文标题】c# 泛型类型和存储库模式【英文标题】:c# generic types and repository pattern 【发布时间】:2015-10-06 13:27:09 【问题描述】:

我正在使用存储库模式开发 C# MVC 应用程序。我使用 Unity 作为我的 IoC,以便在 MVC 应用程序中实现我的存储库。

我创建了以下泛型类型,目的是最大限度地减少整个应用程序中的代码复制。这是一个示例:

public abstract class GenericRepository<T, C> : IDisposable,IGenericRepository<T> where T : class where C : DbContext, new()


public async void AddObjectAsync<T>(T entity) where T : class
    
        try
        
            // Validate that that there is not an existing object in the database.
            var x = _context.Entry(entity);
            if (!Exists(entity))
            
                _context.Set<T>().Add(entity); // Add the entry if it does not exist.
                _context.SaveChanges();
            
            else
            
                UpdateObjectAsync(entity); // Update the entry if it did exist.
            
        
        catch
        
            throw;
        

这在我使用 TPC 或 TPH 策略时效果很好,但它不适用于 TPT 策略。一个例子:

public abstract class Component

    [Key]
    public Guid ComponentId  get; set; 

    public string Manufacturer  get; set; 

public class SpecialComponent : Component

    public string ExtraSpecialProperty  get; set; 

我在 ComponentRepository 中的实现:

public class ComponentRepository : GenericRepository<Component, HappyDbContext>, IComponentRepository

public async void AddComponentAsync<T>(T component) where T : class
    
        try
        

            if(component != null)
            
                AddObjectAsync(component);
            
            else
            
            throw new ArgumentNullException();
            

        
        catch (ArgumentNullException ex)
        
            _logger.WriteErrorLogAsync(ex);
            throw ex;
        
    

这个代码块的第一行是我遇到的问题。为了初始化 GenericRepository 类型,我需要为 T 插入一个类。在这种情况下,DbContext C 将始终相同,因此无需担心。

如何实现适用于继承类型的完全通用模式?如果是少量物品,那么我就不那么担心了,但是在这种情况下并非如此。我认为肯定有更好的方法来处理这个问题,所以我向大家征求意见。

【问题讨论】:

GenericRepository 类的T 参数和AddObjectAsync 的T 参数是什么关系? 在这种情况下您可以使用组合而不是继承吗?如果 ComponentRepository 注入了一个能够构造 GenericRepository 实例的专用工厂,那么AddComponentAsync 可以调用该工厂以根据提供给该方法的类型构造一个强泛型存储库。 对不起,我误读了您的评论。他们是一样的。如果我们传递实体“组件”,则 AddObjectAsync 将是 类型,并期望(组件实体)的方法参数。 @StriplingWarrior 这是一个非常有趣的建议,今晚我将对其进行调查,并让您知道它是否适用于我的模型。我正在想象某种基于服务的方法,这很可能是答案。如果我不是一个完整的新手,我可能会更快地告诉你!感谢您迄今为止的意见。 如果两个T 类型相同,则该方法不需要在该级别是通用的。 public async void AddObjectAsync(T entity) .. 可以正常工作。 【参考方案1】:

看起来你可能有“过度通用”的情况。

如果将DbContext 硬编码在两个地方,那么将其设为泛型似乎是在浪费时间。即GenericRepository&lt;Component, HappyDbContext&gt;where C : DbContext, new()。强类型化字段 _context 并使用来自强类型存储库的 Unity 注入 HappyDbContext 可能更有意义。

将通用实体类型下移到方法级别而不是类也可能会简化事情:

public interface IGenericRepository

    void AddObjectAsync<T>(T entity) where T : class;


public class GenericRepository : IGenericRepository

    private readonly DbContext _context;

    public GenericRepository(DbContext context)
    
        _context = context;
    

    ....

    public async void AddObjectAsync<T>(T entity) where T : class
    
        // Validate that that there is not an existing object in the database.
        var x = _context.Entry(entity);
        if (!Exists(entity))
        
            _context.Set<T>().Add(entity); // Add the entry if it does not exist.
            _context.SaveChanges();
        
        else
        
            UpdateObjectAsync(entity); // Update the entry if it did exist.
        
    


public interface IComponentRepository

    void AddComponentAsync(Component component);
    // or void AddComponentAsync<T>(T component);
    // depends if you'll be reusing the ComponentRepository 
    // for types that inherit Component but still have individual tables... 
    // It was a little difficult to tell from your example.


public class ComponentRepository : GenericRepository, IComponentRepository

    public ComponentRepository(DbContext context) : base(context)  

    ...

    public async void AddComponentAsync(Component component)
    
        try
        
            if (component == null) throw new ArgumentNullException();
            AddObjectAsync(component);
        
        catch (ArgumentNullException ex)
        
            _logger.WriteErrorLogAsync(ex);
            throw ex;
        
    

希望这会有所帮助

【讨论】:

以上是关于c# 泛型类型和存储库模式的主要内容,如果未能解决你的问题,请参考以下文章

C# 泛型的使用

为啥 C# (4.0) 不允许泛型类类型中的协变和逆变?

C# 2.0 新特性(上)

C#关于反射创建泛型类

C#实体类中如何定义泛型集合类型的属性?

受保护的泛型类 - 是不是支持?