对于具有私有设置器的原始类型Dapper
属性,只要它们的名称与您从数据库/查询中获得的名称匹配,就可以自动映射它们。
假设您的User
课程是聚合根
public class User : AggregateRoot
{
public int Id { get; private set; }
public string Name { get; private set; }
...
}
并且您有GetById
来自存储库的方法来重建用户
public class UserRepository : Repository<User>, IUserRepository
{
private UserRepository(IDbConnection dbConnection) : base(dbConnection) {}
...
public User GetById(int id)
{
const string sql = @"
SELECT *
FROM [User]
WHERE ID = @userId;
";
return base.DbConnection.QuerySingleOrDefault<User>(
sql: sql,
param: new { userId = id }
);
}
}
只要sql返回Id和Name列,它们就会自动映射到您的User类匹配属性,即使它们具有私有 setter。又好又干净!
人际关系问题
当您需要加载一对多的对象时,一切都会变得棘手。
假设现在User类有一个属于User的只读汽车列表,以及一些可用于添加/删除汽车的方法:
public class User : AggregateRoot
{
public int Id { get; private set; }
public string Name { get; private set; }
private readonly IList<Car> _cars = new List<Car>();
public IEnumerable<Car> Cars => _cars;
public void PurchaseCar(Car car)
{
_cars.Add(car);
AddEvent(new CarPurchasedByUser { ... });
}
public void SellCar(Car car)
{
_cars.Remove(car);
AddEvent(new CarSoldByUser { ... });
}
}
public class Car : Entity
{
public int Id { get; private set; }
public string Brand { get; private set; }
}
现在在构造User类时如何加载汽车列表?
有些人建议只运行多个查询,然后在构造用户后通过调用PurchaseCar
andSellCar
方法(或类中可用的任何方法)来添加/删除汽车来构造汽车列表:
public User GetById(int id)
{
const string sql = @"
SELECT *
FROM [User]
WHERE ID = @userId;
SELECT *
FROM [Car]
WHERE UserID = @userId;
";
using (var multi = base.DbConnection.QueryMultiple(sql, new { userId = id })
{
var user = multi.Read<User>()
.FirstOrDefault();
if (user != null)
{
var cars = multi.Read<Car>();
foreach (var car in cars)
{
user.PurchaseCar(car);
}
}
return user;
}
}
但是,如果您正在练习,您真的不能Domain-Driven Design
这样做,因为这些方法通常会触发其他事件,这些事件可能会被其他人订阅以触发其他命令。您只是试图初始化您的User对象。
用反射解决它
唯一对我有用的是使用System.Reflection
!
public User GetById(int id)
{
const string sql = @"
SELECT *
FROM [User]
WHERE ID = @userId;
SELECT *
FROM [Car]
WHERE UserID = @userId;
";
using (var multi = base.DbConnection.QueryMultiple(sql, new { userId = id })
{
var user = multi.Read<User>()
.FirstOrDefault();
if (user != null)
{
var cars = multi.Read<Car>();
// Load user car list using Reflection
var privateCarListField = user.GetType()
.GetField("_cars", BindingFlags.NonPublic | BindingFlags.Instance);
privateCarListField.SetValue(car, cars);
}
return user;
}
}