【问题标题】:OrmLite query to select some of the columns from each of 2 joined tablesOrmLite 查询从 2 个连接表中的每一个中选择一些列
【发布时间】:2016-05-25 16:03:39
【问题描述】:

this comment 开始,我如何执行 ServiceStack OrmLite 查询来连接两个或多个表并从每个表中返回一些列?

以 OrmLite Does_only_populate_Select_fields_wildcard 单元测试为例,我想做这样的事情:

public class DeptEmployee
{
    [PrimaryKey]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    [References(typeof(Department2))]
    public int DepartmentId { get; set; }

    [Reference]
    public Department2 Department { get; set; }
}

public class Department2
{
    [PrimaryKey]
    public int Id { get; set; }
    public string Name { get; set; }
}

var q = db.From<DeptEmployee>()
    .Join<Department2>()
    .Select<DeptEmployee, Department2>((de, d2) => new[] { de.FirstName, de.LastName, d2.Name });
var results = db.Select(q);

如我所料,上面没有返回包含 FirstName、LastName 和 Name 的匿名类型列表。它仍然返回DeptEmployee 对象的列表(但只填充了 FirstName 和 LastName)。

【问题讨论】:

    标签: ormlite-servicestack


    【解决方案1】:

    在 OrmLite 中需要注意的重要一点是查询的构造和执行方式与结果的映射方式无关。无论查询是原始自定义 SQL 还是 Typed SQL 表达式都没有关系,OrmLite 只查看返回的数据集以锻炼结果应如何映射。

    因此,当使用 Select&lt;T&gt;(SqlExpression&lt;T&gt;) API 时,OrmLite 将始终尝试将结果映射到 db.From&lt;DeptEmployee&gt;() 中的主 SqlExpression 类型,这不是您想要的,因为您选择的自定义列与形状不匹配来自DeptEmployee POCO。

    有几种不同的方法可以读取自定义架构,它们都适用于相同的查询(因为它独立于您选择映射结果的方式):

    var q = db.From<DeptEmployee>()
        .Join<Department2>()
        .Select<DeptEmployee, Department2>(
            (de, d2) => new { de.FirstName, de.LastName, d2.Name });
    

    我们的建议,尤其是。对于像 OrmLite 这样的类型化代码优先 ORM 是创建一个类型化自定义 POCO 并选择它,例如:

    class Custom
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Name { get; set; }
    }
    
    db.Select<Custom>(q).PrintDump();
    

    这会打印出漂亮的:

    [
        {
            FirstName: First 1,
            LastName: Last 1,
            Name: Dept 1
        },
    ]
    

    主要好处是您可以在List&lt;Custom&gt; 中获得对自定义结果的类型访问权限。

    如果您不想创建自定义类型,可以选择 OrmLite 的Dynamic Result APIs,例如:

    如果您很高兴知道不同字段的位置,您可以选择List&lt;object&gt;,它将按照选择的顺序返回所选字段,例如:

    db.Select<List<object>>(q).PrintDump();
    

    打印:

    [
        [
            First 1,
            Last 1,
            Dept 1
        ],
    ]
    

    否则,如果您还希望返回名称,则可以选择字符串对象字典,例如:

    db.Select<Dictionary<string,object>>(q).PrintDump();
    

    其打印结果类似于自定义 POCO,但名称和对应值保存在松散类型的对象字典中:

    [
        {
            FirstName: First 1,
            LastName: Last 1,
            Name: Dept 1
        },
    ]
    

    如果您只选择 2 列,例如:

    var q = db.From<DeptEmployee>()
        .Join<Department2>()
        .Select<DeptEmployee, Department2>(
            (de, d2) => new { de.LastName, d2.Name });
    

    您可以使用 OrmLite 的 convenient data access APIs,它可以让您在 Dictionary&lt;string,string&gt; 中选择 2 列,例如:

    db.Dictionary<string,string>(q).PrintDump();
    

    哪些打印:

    {
        Last 1: Dept 1,
        Last 2: Dept 2,
        Last 3: Dept 3
    }
    

    请注意,这与上面的字符串对象字典非常不同,因为它返回结果为单个 Dictionary&lt;string,string&gt;for all rows 而不是 List&lt;Dictionary&lt;string,object&gt;&gt;,其中每个行都有一个 Dictionary

    同样,如果您只选择 1 个字段,例如:

    var q = db.From<DeptEmployee>()
        .Join<Department2>()
        .Select(x => x.LastName);
    

    然后您可以在List&lt;string&gt; 中选择一列结果,例如:

    db.Column<string>(q).PrintDump();
    

    哪些打印:

    [
        Last 1,
        Last 2,
        Last 3
    ]
    

    如果您想要不同的结果,您可以在 HashSet&lt;string&gt; 中返回它们:

    db.ColumnDistinct<string>(q).PrintDump();
    

    回到最初的重点,查询的构造方式无关紧要(它只控制生成的SQL),OrmLite 只查看返回的结果集来映射结果,它会尝试映射到您指定要将结果映射到的目标 API,因此执行自定义 SQL:

    db.Column<string>("SELECT LastName FROM DeptEmployee").PrintDump();
    

    或者如果你执行了一个存储过程:

    db.Column<string>("EXEC GetLastNamesFromDeptEmployees").PrintDump();
    

    如果您使用类型化的 SQL 表达式,其映射方式完全相同,即 OrmLite 仅查看它映射到您希望返回结果的方式的结果集。

    【讨论】:

    • 感谢您提供如此详细的回答! .Select&lt;List&lt;object&gt;&gt;(q) 确实返回了各个字段,就像我想要的那样,但是处理结果的代码当然会变得丑陋!您是否认为可以添加对.Select&lt;dynamic&gt;(q) 的支持,这将返回包含所选字段的匿名类型列表?在此示例中,它将与您的 Custom 类相同。这在随后使用 LINQ(在内存中)进一步处理结果的情况下非常有用,因为它可以保持所有字段的类型安全,而无需为每个此类查询创建类的开销。
    • @EM 我通常反对在任何地方使用动态。但我有added support for dynamic in this commit,它可以让你使用db.Select&lt;dynamic&gt;(q) 获取动态对象列表。您需要clear your MyGet cache 才能下载最新版本。
    • 谢谢,这很有帮助,但我错了它返回匿名类型。当然,“动态”与匿名类型完全不同。哎呀!如果我们能以某种方式从选择中保留匿名类型以得到结果,那就更好了,即像 var q = db.SelectExpression&lt;DeptEmployee, Department2&gt;(db.From&lt;DeptEmployee&gt;().Join&lt;Department2&gt;(), (de, d2) =&gt; new { de.FirstName, de.LastName, d2.Name }) 这样的东西,其中 q 是 SqlExpression&lt;T&gt; 而 T 是像 Custom 这样的匿名类型。这将允许对其进行进一步过滤,例如.Where(t =&gt; t.FirstName...)
    • 我想我正在努力解决的根本问题是整个表达式的最终返回类型必须提前定义,在初始 db.From&lt;&gt; 类型参数中,而不是从列表中推断出来在所有过滤、分组等之后我最终选择的字段。
    • 匿名类型只是一个对象,无论如何你不可能做到的,因为泛型类型在 SqlExpression 之外是不可访问的。但是有几个选项可用于访问数据,例如,如果您想要类型化访问,请使用自定义类型。
    猜你喜欢
    • 2012-10-15
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    • 2018-10-10
    • 2011-07-09
    • 1970-01-01
    相关资源
    最近更新 更多