c# – 在小巧玲珑中查询抽象模型

前端之家收集整理的这篇文章主要介绍了c# – 在小巧玲珑中查询抽象模型前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用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; }
}

正如所料,当通过Dapper的查询方法检索它时,我无法创建抽象类的实例.我希望这会返回一个Animal列表,它们的值是相应的派生类型.

var animals = Connection.Query<Animal>("SELECT * FROM tanimal")

我试图增加对此的支持是不成功的.在传入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"]);
}

但是,此时看起来还没有打开读取器,因此当没有数据存在时无效尝试读取失败.

这是正确的方法吗?有没有任何努力在其他地方支持这个?

解决方法

你可以使这个工作,但它不如使用Dapper的默认行为与单独的表.

需要为每一行调用GetDeserializer,这意味着它需要在内部发生(reader.Read())

通过修改QueryImpl< T>你可以达到你想要的效果.假设你得到的结果是:

var results = connection.Query<Animal>("SELECT * FROM tanimal");

然后是QueryImpl< T>的try {}块的开头.将会:

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,-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

这将使该方法仅适用于鉴别器字段,因此您可能想要创建自己的QueryImpl< T>如果你需要这个与其他查询正常工作.此外,我不能保证这将在每种情况下都有效,只测试两行,每种类型一个 – 但这应该是一个很好的起点.

猜你在找的C#相关文章