【问题标题】:Generic repository with generic constructor of type T具有 T 类型的通用构造函数的通用存储库
【发布时间】:2015-01-30 11:16:38
【问题描述】:

我在 c# 中有以下类:

public class Customer{
  public long Id { get; set;}
  public String Firstname { get; set;}
  public String Lastname { get; set;}

  public Customer(long id, String firstname, String lastname){...}
}

public class Book{
  public long Id { get; set;}
  public String Title { get; set;}
  public String Author{ get; set;}
  public int NumberOfCopies{ get; set;}

  public Book(long id, String title, String author, int numberofcopies){...}
}

我的仓库是通用的,界面如下:

public interface IGenericRepository<T> where T : class, new(){

  Add(T entity);
  Update(T entity);
  Delete(T entity);
  GetAll();

}

因此,要为客户创建存储库,我将创建 IGenericRepository 和 Book IGenericRepository。要从 GUI 访问这些方法,我需要一个类似的方法: AddCustomer(long id, String firstname, String lastname) 和一本书一样,因为 GUI 不知道实体本身。我正在考虑创建一个包含这些方法的 CustomerService 和 BookService。 但是我必须为我添加的每个实体创建一个新服务。

我的问题是,我怎样才能使这个服务通用并且仍然保留实体构造函数的参数?

明确地说,我想要一个通用的服务类,它可以添加书籍和客户,但使用相同的方法。比如:

Add(<constructor parameters of T>)

这个方法将从一个拥有所有服务的控制器类中调用。 GUI 然后可以通过控制器访问这些服务方法。

这可能吗?如果是,如何?如果没有,是否有更好的解决方案来实现这一目标?

提前致谢!

【问题讨论】:

  • 请澄清“通用服务类,它可以添加书籍和客户,但使用相同的方法”是否意味着可以包含两种类型的单个类或在代码中语法看起来相同的兄弟通用类型。

标签: c# generics repository


【解决方案1】:

一种选择是根据需要接受参数,例如。 g.:

public void Add<T>(params object param) { ... }

T 是您要创建的类型。通过反射检查哪些构造函数可用,选择适合参数列表的构造函数。然后实例化对象。瞧。

【讨论】:

  • 感谢您的回答。这确实可行,但在我看来它很容易出错。我希望通过更好的解决方案获得更多答案。如果没有,我会和你一起去:) 再次感谢你。
  • 还有另一种选择:您是否调查过System.Reflection.Emit 命名空间为您提供的选项?仍然像发布的答案一样容易出错,但在调用错误的情况下可能更具体(更好的编译器错误反馈)并且执行速度更快。
【解决方案2】:

我通常看到的完成方式是您具有以下内容:

1。 一个定义通用添加、更新、删除等的接口:

public interface IGenericRepository<T> where T : class
{
    Add(T entity);
    Update(T entity);
    Delete(T entity);
    GetAll();
}

2。 一个通用存储库,它接受您将要持久保存到数据存储的不同类型。

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    private connectionInformation;

    public GenericRepository<T>(object connectionInformation)
    {
        // do something with the connection info, dbContext, etc...
    }

    public T Add(T entity)
    {
        // implementation...
    }

    public T Update(T entity)
    {
        // implementation...
    }

    public T Delete(T entity)
    {
        // implementation...
    }

    public List<T> GetAll()
    {
        // implementation...
    }
}

3。 为不同的具体类型设置通用存储库实例的工作单元。

public class UnitOfWork
{
    private object connectionInformation;

    public UnitOfWork(object connectionInformation)
    {
        // set up your connection information
        this.connectionInformation = connectionInformation;

        this.CustomerRepository = new GenericRepository<Customer>(connectionInformation);

        this.BookRepository = new GenericRepository<Book>(connectionInformation);
    }

    public GenericRepository<Book> BookRepository { get; private set; }

    public GenericRepository<Customer> CustomerRepository { get; private set; }
}

4。 可以实例化您可以与之交互的“工作单元”的服务/服务层。该服务负责处理每种类型的不同属性,但数据保存和检索将通过 unitOfWork 处理。在您的服务中,您可以使用这样的方法:

public void DeleteFirstBook()
{
    var unitOfWork = new UnitOfWork(connnectionInformation);

    var books = unitOfWork.BookRepository.GetAll();

    if(books.Any())
    {
        unitOfWork.BookRepository.Delete(books.First());
    }
}

您也可以为客户做同样的事情。拥有服务层可以帮助您的代码避免过于紧密的耦合。 “数据层”应该只负责与数据库交互(创建、读取、更新、删除)。正如您所说,您的 UI 层也不应该知道如何创建新对象或与数据库交互。因此服务变得很重要,因为它知道如何设置数据、将数据放在哪里以及如何将数据返回给 UI。

希望对你有帮助!

【讨论】:

  • 我不确定你在哪里解决 Add 的原始问题,不需要对象而是构造函数参数(我想最好是类型安全的)。
  • 重点是要说明,作为最佳实践,您不应该将对象构造函数传递到存储库中。让存储库处理对象创建/操作通常是一个坏主意。虽然它在技术上并没有“回答”这个问题,但它提供了一种解决问题的替代方法。如果 UI 需要将数据获取到数据库,它应该有一个中间层(服务)来处理对象创建以将其转换为数据层期望的形式。
  • 感谢您的回答!我要这样做。我同意你的看法,这是最优雅的选择。
  • @Afrit(或 Josh Taylor)请编辑问题以匹配接受的答案。
【解决方案3】:

我会将IRepository&lt;t&gt; 方法Add(T entity) 更改为Add(object entity)。这样您就可以对任何实体使用完全相同的代码。

【讨论】:

  • 当 IRepository 被实例化时,T 是一个泛型并成为您想要存储库的对象的表示。因此,如果您有一个 IRepository 和一个 IRepository,则通用 T 可以毫无问题地处理这两个实体。使用泛型而不是“对象”可以在每个存储库中强制执行类型安全。
【解决方案4】:

相当标准的方法是将“创建者”委托传递给您的存储库,我也不确定它是否符合您的目标:

public interface IGenericRepository<T> where T : class{

  Add(Func<T> creator);
  ...
}

Repository<T> : IGenericRepository<T> where T : class
{
     public  Add(Func<T> creator) 
     {
        T newOne = creator();
        ....
     }
}

// usage
bookRepository.Add(() => new Book(42, "some title", ...));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 2018-03-03
    相关资源
    最近更新 更多