c# - 在 ServiceStack AutoQuery 中多次加入同一个表

标签 c# servicestack ormlite-servicestack

我正在尝试使用 ServiceStack 的 Auto Query针对表 A 的功能,该表 A 多次引用另一个表 B,但无法使其工作。

问题的根源似乎是 AutoQuery 生成联接时没有给联接表添加别名,从而导致列不明确。它对 SQL Server 和 Sqlite 都失败并出现类似的错误。我一直没能找到解决这个问题的方法。

这基本上就是我所做的:

public class Purchase
{
    public int Id { get; set; }
    public string Description { get; set; }
    [References(typeof(Person))]
    public int SellerId { get; set; }
    [References(typeof(Person))]
    public int BuyerId { get; set; }

    [Reference]
    public Person Buyer { get; set; }

    [Reference]
    public Person Seller { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

[Route("/autoquery")]
public class AutoQueryRequest : QueryBase<Purchase, CustomPurchase>, IJoin<Purchase, Seller>, IJoin<Purchase, Buyer>
{ }

完整代码可以在这里找到: https://gist.github.com/AVee/0cb0dc1912698fcc43df

最佳答案

OrmLite(以及扩展的 AutoQuery)不支持表 JOIN 的自定义别名,因此您无法通过 AutoQuery 查询单个表。

尽管正常用例:

[Route("/purchase/query")]
public class QueryPurchase : QueryBase<Purchase>
{
    public int Id { get; set; }
}

client.Get(new QueryPurchase { Id = 1 }).PrintDump();

这将加入并填充 multiple self references :

{
        Offset: 0,
        Total: 1,
        Results:
        [
                {
                        Id: 1,
                        Description: Sonic Screwdriver,
                        SellerId: 2,
                        BuyerId: 1,
                        Buyer:
                        {
                                Id: 1,
                                Name: Rose Tyler
                        },
                        Seller:
                        {
                                Id: 2,
                                Name: Martha Jones
                        }
                }
        ]
}

OrmLite 中还有一个新的 CustomJoin API,允许您指定自己的 Aliases on JOINS .

使用自定义 AutoQuery 实现

由于您无法查询多个自引用表,因此一种方法是使用自定义 AutoQuery 实现,您可以在自定义 AutoQuery 实现中查询子表 ID。

为此,我们将创建一个额外的“支持”AutoQuery DTO QueryPurchase,其中包含 AutoQuery 可以处理的属性,而 CustomPurchase AutoQuery 仍然是您可以使用的面向公众的 API想要公开此服务,例如:

[Alias("Purchase")]
public class CustomPurchase : QueryBase<Purchase>
{
    public int? Id { get; set; }
    public string Description { get; set; }
    public string SellerName { get; set; }
    public string BuyerName { get; set; }
}

public class QueryPurchase : QueryBase<Purchase>
{
    public int? Id { get; set; }
    public string Description { get; set; }
    public List<int> BuyerIds { get; set; }
    public List<int> SellerIds { get; set; }
}

在您的自定义 AutoQuery 实现中,您可以获取适当的买家和卖家 ID,并将它们添加到支持 QueryPurchase 查询中,例如:

public QueryResponse<Purchase> Any(CustomPurchase request)
{
    var qPurchase = request.ConvertTo<QueryPurchase>();

    if (request.BuyerName != null)
    {
        qPurchase.BuyerIds = Db.Column<int>(Db.From<Person>()
            .Where(x => x.Name == request.BuyerName)
            .Select(x => x.Id));
    }

    if (request.SellerName != null)
    {
        qPurchase.SellerIds = Db.Column<int>(Db.From<Person>()
            .Where(x => x.Name == request.SellerName)
            .Select(x => x.Id));
    }

    var q = AutoQuery.CreateQuery(qPurchase, Request.GetRequestParams());

    return AutoQuery.Execute(qPurchase, q);
}

AutoQuery 可以理解 BuyerIds 等集合属性,并在 BuyerId 上执行适当的查询。

现在调用此服务时:

client.Get(new CustomPurchase { BuyerName = "Rose Tyler" }).PrintDump();

它将打印所需的内容:

{
        Offset: 0,
        Total: 1,
        Results:
        [
                {
                        Id: 1,
                        Description: Sonic Screwdriver,
                        SellerId: 2,
                        BuyerId: 1,
                        Buyer:
                        {
                                Id: 1,
                                Name: Rose Tyler
                        },
                        Seller:
                        {
                                Id: 2,
                                Name: Martha Jones
                        }
                }
        ]
}

附加自定义条件

本着类似的精神,解决此问题的另一种方法是向 AutoQuery 本身添加自定义条件,为此我们不再需要 BuyerIdsSellerIds 属性,例如:

public class QueryPurchase : QueryBase<Purchase>
{
    public int? Id { get; set; }
    public string Description { get; set; }
}

QueryPurchase DTO 保存 AutoQuery 可以处理的 Purchase 表属性,减去需要的 SellerNameBuyerName需要特殊处理,例如:

public QueryResponse<Purchase> Any(CustomPurchase request)
{
    //Copy only the properties that AutoQuery can handle
    var qPurchase = request.ConvertTo<QueryPurchase>();
    var q = AutoQuery.CreateQuery(qPurchase, Request.GetRequestParams());

    //Add Custom SQL Conditions for each Custom Query
    if (request.BuyerName != null)
        q.UnsafeWhere("BuyerId IN (SELECT Id FROM Person WHERE Name = {0})", 
            request.BuyerName);

    if (request.SellerName != null)
        q.UnsafeWhere("SellerId IN (SELECT Id FROM Person WHERE Name = {0})", 
            request.SellerName);

    return AutoQuery.Execute(qPurchase, q);
}

这与上面的自定义 AutoQuery 实现类似,只不过它被添加到单个 AutoQuery 查询中。

同样调用此服务:

csharp client.Get(new CustomPurchase { BuyerName = "Rose Tyler"}).PrintDump();

还打印所需的内容:

{
        Offset: 0,
        Total: 1,
        Results:
        [
                {
                        Id: 1,
                        Description: Sonic Screwdriver,
                        SellerId: 2,
                        BuyerId: 1,
                        Buyer:
                        {
                                Id: 1,
                                Name: Rose Tyler
                        },
                        Seller:
                        {
                                Id: 2,
                                Name: Martha Jones
                        }
                }
        ]
}

为了支持这一点,OrmLite 的 SqlExpression 有一个新的 UnsafeWhere API,以便能够添加未经验证的原始 SQL,如上面添加的子 SELECT。参数仍然会被转义,这将防止任何 SQL 注入(inject)。此更改可从现在的 v4.0.37+ 开始 available on MyGet .

关于c# - 在 ServiceStack AutoQuery 中多次加入同一个表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28189930/

相关文章:

c# - 使用 NHibernate 实现全局化

c# - 在c#中将十六进制字符串转换为小端字符串

c# - 为什么我们在升级到 Core 2.1 和 Framework 4.7.2 后在 ServiceStack 中收到 TypeIntializer Exception?

c# - ServiceStack OrmLite - 处理默认列和计算列

ormlite-servicestack - servicestack ormlite sqlite DateTime 在插入时获取时区调整

c# - 进行 HTTP 隧道时如何保持连接打开

C# WPF datagrid 鼠标悬停显示图像

c# - 依赖注入(inject)——如何解决基于值而不是类型的依赖?

servicestack - ServiceStack.Interfaces.dll 和 ServiceStack.ServiceInterface.dll 之间的区别

ormlite-servicestack - 我如何在 ServiceStack Ormlite 中计数(DISTINCT)?