ajb*_*ven 8 c# inheritance table-per-hierarchy dapper
我正在使用Table Per Hierarchy数据库继承,其中所有派生类型的列都在一个表中.每个派生表都使用字符串Discriminator字段标识,该字段包含派生类的名称:
---------------------
| tanimal |
---------------------
| animalid |
| discriminator |
| furcolour |
| feathercolour |
---------------------
public abstract class Animal
{
public int AnimalId { get; set; }
public string Discriminator { get { return GetType().Name; } }
}
public class Bird : Animal
{
public string FeatherColour { get; set; }
}
public class Dog : Animal
{
public string FurColour { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
正如所料,当我通过Dapper的查询方法检索这个时,我会收到Instances of abstract classes cannot be created
.我希望这会返回一个Animal列表,它们的值是相应的派生类型.
var animals = Connection.Query<Animal>("SELECT * FROM tanimal")
Run Code Online (Sandbox Code Playgroud)
我试图为此添加支持是不成功的.在传入SqlMapper.cs :: GetTypeDeserializer()之前,如果传入的类型是抽象类,那么我将类型替换为以下方法中返回的类型:
static Type GetDerivedType(Type abstractType, IDataReader reader)
{
var discriminator = abstractType.GetProperty("Discriminator");
if (discriminator == null)
throw new InvalidOperationException("Cannot create instance of abstract class " + abstractType.FullName + ". To allow dapper to map to a derived type, add a Discriminator field that stores the name of the derived type");
return Type.GetType((string)reader["Discriminator"]);
}
Run Code Online (Sandbox Code Playgroud)
然而,在这一点看来,读者还没有被打开,所以它失败了Invalid attempt to read when no data is present
.
这是正确的方法吗?是否有任何努力在其他地方支持这一点?
您可以做到这一点,但它的效率会低于使用 Dapper 的默认行为和单独的表。
GetDeserializer
需要为每一行调用,这意味着它需要发生在内部while (reader.Read())
通过修改QueryImpl<T>
就可以达到你想要的结果。假设您得到的结果是:
var results = connection.Query<Animal>("SELECT * FROM tanimal");
Run Code Online (Sandbox Code Playgroud)
try {}
那么块的开头QueryImpl<T>
将是:
try
{
cmd = command.SetupCommand(cnn, info.ParamReader);
if (wasClosed) cnn.Open();
// We can't use SequentialAccess any more - this will have a performance hit.
reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
wasClosed = false;
// You'll need to make sure your typePrefix is correct to your type's namespace
var assembly = Assembly.GetExecutingAssembly();
var typePrefix = assembly.GetName().Name + ".";
while (reader.Read())
{
// This was already here
if (reader.FieldCount == 0) //https://code.google.com/p/dapper-dot-net/issues/detail?id=57
yield break;
// This has been moved from outside the while
int hash = GetColumnHash(reader);
// Now we're creating a new DeserializerState for every row we read
// This can be made more efficient by caching and re-using for matching types
var discriminator = reader["discriminator"].ToString();
var convertToType = assembly.GetType(typePrefix + discriminator);
var tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(convertToType, reader, 0, -1, false));
if (command.AddToCache) SetQueryCache(identity, info);
// The rest is the same as before except using our type in ChangeType
var func = tuple.Func;
object val = func(reader);
if (val == null || val is T)
{
yield return (T)val;
}
else
{
yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
}
}
// The rest of this method is the same
Run Code Online (Sandbox Code Playgroud)
这将使该方法仅适用于鉴别器字段,因此QueryImpl<T>
如果您需要此方法与其他查询正常工作,您可能需要创建自己的方法。另外,我不能保证这在每种情况下都有效,仅使用两行进行测试,每种类型之一 - 但这应该是一个很好的起点。
小智 5
我也想分享我的解决方案。输入:
C#
abstract class Stock {}
class Bond: Stock {}
class Equity : Stock {}
Run Code Online (Sandbox Code Playgroud)
SQL
CREATE TABLE [dbo].[Stocks] (
....some columns....
[Descriminator] VARCHAR (100) NOT NULL,
);
Run Code Online (Sandbox Code Playgroud)
在 SQL 中,我有一个描述符列,它确定每行“股票”或“债券”的 C# 类型。基本上,这是按层次结构表策略的标准实现。
我使用了 Dapper 的无参数查询语法
connection.Query(sql);
Run Code Online (Sandbox Code Playgroud)
获取dynamic
Dapper 视为 DapperRow 的对象。尽管 DapperRow 是一个私有类,但它实现了IDictionary<string, object>.
String - 属性名称、Object - 属性值。
函数将 IDictionary<string, object> 转换为类 (强类型):
public static T GetObject<T>(IDictionary<string, object> dict)
{
Type type = typeof(T);
var obj = Activator.CreateInstance(type);
foreach (var kv in dict)
{
type.GetProperty(kv.Key).SetValue(obj, kv.Value);
}
return (T)obj;
}
Run Code Online (Sandbox Code Playgroud)
鉴别器列和 C# 类之间的映射器:
public static Stock ConvertToStock(object value)
{
var dapperRowProperties = value as IDictionary<string, object>;
switch (dapperRowProperties["Descriminator"])
{
case "Bond":
return GetObject<Bond>(dapperRowProperties);
case "Stock":
return GetObject<Stock>(dapperRowProperties);
default:
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
转换器的使用:
public Stock GetStock(int id)
{
Stock stock;
var sql = "select * from Stocks where Id = @id";
using (var connection = ConnectionFactory.GetOpenConnection())
{
stock = connection.Query(sql, new { id }).Select(ConvertToStock).Single();
}
return stock;
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2899 次 |
最近记录: |