我正在尝试使用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
答案 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):
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。