带有精巧的

时间:2016-12-17 15:17:44

标签: c# sql asp.net-mvc dapper

我正在尝试使用dapper构建通用存储库。但是,我在实现CRUD操作时遇到了一些困难。

这是存储库中的一些代码:

 public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
    internal IDbConnection Connection
    {
        get
        {
            return new SqlConnection(ConfigurationManager.ConnectionStrings["SoundyDB"].ConnectionString);
        }
    }

    public GenericRepository(string tableName)
    {
        _tableName = tableName;
    }

    public void Delete(TEntity entity)
    {
        using (IDbConnection cn = Connection)
        {

            cn.Open();
            cn.Execute("DELETE FROM " + _tableName + " WHERE Id=@ID", new { ID = entity.Id });
        }
    }
}

如您所见,我的delete-method将TEntity作为参数,它是类型类的参数。

我从我的UserRepository调用我的Delete方法,如下所示:

public class UserRepository : GenericRepository<User>, IUserRepository
{
    private readonly IConnectionFactory _connectionFactory;

    public UserRepository(IConnectionFactory connectionFactory) : base("User")
    {
        _connectionFactory = connectionFactory;
    }

    public async Task<User> Delete(User model)
    {
        var result = await Delete(model);
        return result;
    }
}

问题是我无法在我的通用存储库中的删除操作中编写entity.Id。我收到一个错误。那么如何轻松实现这样的CRUD操作呢?

这是错误消息:

TEntity does not contain a definition of "Id" and no extension method "Id" accepting a argument of type "TEntity" could be found

4 个答案:

答案 0 :(得分:8)

定义一个这样的界面。

public interface ITypeWithId {
    int Id {get;}
}

确保您的User类型实现该接口。

现在将它作为通用约束应用于您的类。

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class, ITypeWithId

如果您的类型存储在存储库中但DO不具有Id属性,则使您的删除类型约束特定于方法而不是类。这将允许您仍然使用相同的存储库类型,即使类型可能键入其他内容,如字符串或复合(多)键。

public void Delete<T>(T entity) where T : class, ITypeWithId
{
    using (IDbConnection cn = Connection)
    {

        cn.Open();
        cn.Execute("DELETE FROM " + _tableName + " WHERE Id=@ID", new { ID = entity.Id });
    }
}

答案 1 :(得分:3)

请不要这样做!您的通用存储库增加了比值更多的混淆。它是脆弱的代码(_tableName的字符串文字,id参数上的无效转换错误),并引入了一个巨大的安全漏洞(通过_tableName进行sql注入)。如果您选择了Dapper,那是因为您希望控制您的sql,因此生成您发送给Dapper的sql是没有意义的。

答案 2 :(得分:1)

您必须定义如下所示的界面

public interface IIdentityEntity
{
  public int Id { get; set;}
}

所有想要使用该类的实体都必须实现IIdentityEntity。

并且第一行应更改为以下

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class,IIdentityEntity

问题是你只是将TEntity描述为类,并且类在其描述中没有Id,所以你必须通知编译器Generic类型实现了一个在其中包含Id字段的接口

答案 3 :(得分:0)

如果有帮助,我刚刚发布了一个库 Harbin.DataAccess,该库使用“原始” Dapper,{{3}来实现通用存储库(通用存储库模式) }和Dapper.FastCRUD

  • 插入/更新/删除由Dapper FastCRUD自动生成(类应使用键/自动增量列的属性修饰)
  • 支持FastCRUD批量更新,批量删除和异步方法。
  • 可以使用自定义查询和自定义命令扩展存储库(允许/促进CQRS分隔)
  • 可以手动定义查询(原始sql)或使用Dapper FastCRUD语法
  • 可以使用DapperQueryBuilder构建动态查询(条件的动态数量)
  • 有只读连接包装程序和只读存储库,因此可以轻松使用只读副本(或多个数据库)
  • 对ADO.NET事务的支持
  • 支持模拟查询和命令

示例插入/更新/删除(通用存储库-使用Dapper FastCRUD):

var conn = new ReadWriteDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));

// Get a IReadWriteRepository<TEntity> which offers some helpers to Query and Write our table:
var repo = conn.GetReadWriteRepository<ContactType>();

var contactType = repo.QueryAll().First();

// Updating a record
contactType.ModifiedDate = DateTime.Now;
repo.Update(contactType);

// Adding a new record
var newContactType = new ContactType() { Name = "NewType", ModifiedDate = DateTime.Now };
repo.Insert(newContactType);
// FastCRUD will automatically update the auto-generated columns back (identity or guid)

// Deleting a record
repo.Delete(newContactType);
[Table("ContactType", Schema = "Person")]
public class ContactType
{
    [Key] // if column is part of primary key
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] // if column is auto-increment
    public int ContactTypeId { get; set; }

    public DateTime ModifiedDate { get; set; }

    public string Name { get; set; }
}

示例动态查询

var conn = new ReadDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));


// Get a IReadRepository<TEntity> which offers some helpers to Query our table:
var repo = conn.GetReadRepository<Person>();

// Custom Query (pure Dapper)
var people = repo.Query("SELECT * FROM Person.Person WHERE PersonType = @personType ", new { personType = "EM" } );

// DapperQueryBuilder allows to dynamically append conditions using string interpolation (but injection-safe)

string type = "EM"; string search = "%Sales%";

var dynamicQuery = repo.QueryBuilder(); // if not specified query is initialized with "SELECT * FROM tablename"
dynamicQuery.Where($"PersonType = {type}");
dynamicQuery.Where($"ModifiedDate >= {DateTime.Now.AddDays(-1)} ");
dynamicQuery.Where($"Name LIKE {search}");

// Result is SELECT * FROM [Person].[Person] WHERE PersonType = @p0 AND ModifiedDate >= @p1 AND Name LIKE @p2
var people = dynamicQuery.Query();

使用继承扩展存储库(添加自定义查询和命令)

public class PersonRepository : ReadWriteDbRepository<Person>
{
  public PersonRepository(IReadWriteDbConnection db) : base(db)
  {
  }
  public virtual IEnumerable<Person> QueryRecentEmployees()
  {
    return this.Query("SELECT TOP 10 * FROM [Person].[Person] WHERE [PersonType]='EM' ORDER BY [ModifiedDate] DESC");
  }
  public virtual void UpdateCustomers()
  {
    this.Execute("UPDATE [Person].[Person] SET [FirstName]='Rick' WHERE [PersonType]='EM' ");
  }
}

public void Sample()
{
  // Registers that GetReadWriteRepository<Person>() should return a derived type PersonRepository
  ReadWriteDbConnection.RegisterRepositoryType<Person, PersonRepository>();

  var conn = new ReadWriteDbConnection(new System.Data.SqlClient.SqlConnection(connectionString));  
  
  // we know exactly what subtype to expect, so we can just cast.
  var repo = (PersonRepository) conn.GetReadWriteRepository<Person>();
  
  repo.UpdateCustomers();
  var recentEmployees = repo.QueryRecentEmployees();
}

完整文档DapperQueryBuilder