【问题标题】:How to automatically create concrete classes based on field value如何根据字段值自动创建具体类
【发布时间】:2014-05-10 21:25:21
【问题描述】:

考虑这个例子:

数据库(类似于 Stackoverflow)有一个表,同一个表中的问题和答案可通过PostType 字段区分。我想取回最新帖子的列表,无论是问题还是答案。

由于问题和答案具有共同的属性,因此两个类都继承自抽象类 ContentBase。他们只添加了一些与每种类型相关的额外字段。

如何根据条件字段获取转换为 IList<ContentBase> 的内容项目列表,而单个项目属于具体类型 QuestionAnswer

public abstract class ContentBase
{
    public int Id { get; set; }

    public string Title { get; set; }

    public User Author { get; set; }

    public abstract ContentType Type { get; }

    ...
}

public class Question : ContentBase
{
    public override ContentType Type
    {
        get { return ContentType.Question; }
    }

    ...
}

public class Answer: ContentBase
{
    public override ContentType Type
    {
        get { return ContentType.Answer; }
    }

    ...
}

然后我的存储库会有一个调用:

IList<ContentBase> result = db.GetLatest();

我正在使用 PetaPoco/NPoco,但我想如果使用 Dapper 也会出现同样的问题。

问题

如何指示 DAL 根据特定字段的值(在本例中为 ContentType)实例化正确的具体类型?

说明

我应该这样做:

db.Fetch<ContentBase, User>(...)

但我不能,因为ContentBase 是一个抽象类。如果我将此行更改为:

db.Fetch<dynamic, User>(...)

它仍然不起作用,因为我从 NPoco 收到了一个 InvalidOperationException,说 无法自动加入用户

我想这样做的唯一方法是创建一个继承自PocoData 的新类并提供我自己的GetFactory 委托实现。 我认为。我认为没有任何其他可扩展点可以实例化具体的 POCO,我不确定我是否应该这样做以及如何,因为我' m 处理抽象祖先类。

【问题讨论】:

  • 如果你使用var result = db.Fetch&lt;Question&gt;("Select * from ContentBase ..",你会得到问题...但是如果你想要一个包含问题和答案的列表,那么将它们分开不是 DAL 的工作。
  • @EduardoMolteni:没错。 DAL 不应该知道不同的内容类型,因此应该只返回适当的实例。您的意思是我应该进行 N 个 DB 调用,每种类型调用一个并将它们组合起来?
  • 不不不。使用 LINQ 等进行 1 个 DB 调用并分离或转换
  • @EduardoMolteni:这正是我想要做的。我同时从数据库中得到问题和答案,但我的结果应该包括正确的类型(它们都继承自 ContentBase)。我只是不知道该怎么做?我该怎么做?你能用一些代码写一个答案,以便更清楚你想告诉我什么吗?让我更新一下我的问题...
  • 对不起,我错过了abstract 部分,现在我明白你的意思了。

标签: c# petapoco mapper table-per-hierarchy npoco


【解决方案1】:

不确定我是否理解正确。

  1. 您能否通过一个基类来解决这个问题,该基类采用 Enum 的整数值,将 Q&A 加载到一个集合中,然后在内存中将其在 Q'a 和 A 之间拆分?这似乎要简单得多。

  2. 也许会单独获得问答:

    var s = @"select q.Id, q.Title, q.ContentType as Type , q.Author as Name from QA q where q.ContentType = @0;";
    var q = db.Fetch<Question,User>(s,1);
    var a = db.Fetch<Answer, User>(s,2);
    
  3. 你能用multiple result sets解决这个问题吗?

    var sql  = @"select * from QA where contenttype = 1; select * from qa where
    contenttype  =1;";
    var com = db.FetchMultiple<Question,Answer>(sql);
    com.Dump();
    

注意:我的数据库表使用nvarchar 代替User,并在您的基础中为User 使用string

ContentType创建了一个枚举

void Main()
{
    var sql  = @"select * from QA where contenttype = 1; select * from QA where contenttype  = 2;";
    var com = db.FetchMultiple<Question, Answer>(sql);

    com.Dump();
}

此处使用的其他方法和类型

public enum ContentType
{
    Question,
    Answer
}

public abstract class ContentBase
{
    public int Id { get; set; }

    public string Title { get; set; }

    public string Author { get; set; }

    public abstract ContentType Type { get; }
}

public class Question : ContentBase
{
    public override ContentType Type
    {
        get { return ContentType.Question; }
    }
}

public class Answer: ContentBase
{
    public override ContentType Type
    {
        get { return ContentType.Answer; }
    }
}

【讨论】:

  • 基本上不需要分切。我只需要基于数据库记录的适当类的混合。 #1. 是的,我目前有一个从ContentBase 继承的单独的 DAO 类型,它代表任何内容,我稍后将使用 Linq 更改项目类型。这样我就可以得到正确混合顺序的结果,而无需单独获得单独的类型并将它们混合到更高层。 #2 & #3 除了我们使用 N 个调用(N 个内容类型)或一个多重调用之外,它们的工作方式类似。它们也与 #1 非常相似,只是我们省略了 AnyContent 类。
  • 在所有 3 个中,#1 似乎是最合适的,因为我们不必在上层混合内容项。因此我现在实现了它。
猜你喜欢
  • 1970-01-01
  • 2021-08-23
  • 2017-07-31
  • 1970-01-01
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多