从加入的 table 中选择 OrmLite 新对象进行插入

Selecting OrmLite new object from joined table for insertion

我有 3 个实体:

    [CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
    public class WatchedUrlRecord
    {
        [AutoIncrement]
        public long Id { get; set; }
        public string Url { get; set; }
        public string Provider { get; set; }
        public string DomainKey { get; set; }
        public WatchedUrlScanStatus WatchedUrlScanStatus { get; set; }
        public bool NoFollow { get; set; }
        public HttpStatusCode HttpStatusCode { get; set; }
        public DateTime? LastScanTime { get; set; }
        public WatchedUrlScanResult LastScanData { get; set; }
        public string Anchors { get; set; }
        public int? OutboundLinks { get; set; }

        [ForeignKey(typeof(TargetDomainRecord), OnDelete = "CASCADE")]
        public long TargetDomainRecordId { get; set; }

        [ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
        public long UserAuthCustomId { get; set; }
    }

    [CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
    public class WatchedUrlQueue
    {
        [PrimaryKey]
        public long WatchedUrlRecordId { get; set; }
        [Index]
        public string Url { get; set; }
        [Index]
        public string DomainKey { get; set; }
        [Index]
        public long TargetDomainRecordId { get; set; }
        public string TargetDomainKey { get; set; }
        [Index]
        public DateTime CreateDate { get; set; } = DateTime.UtcNow;
        public int Tries { get; set; }
        [Index]
        public DateTime? DeferUntil { get; set; }
        [Index]
        public long UserAuthCustomId { get; set; }
        [Index]
        public bool FirstScan { get; set; }

    }

    [CompositeIndex(nameof(Url), nameof(UserAuthCustomId), Unique = true)]
    public class TargetDomainRecord
    {
        [AutoIncrement] 
        public long Id { get; set; }
        public string Url { get; set; }
        public string DomainKey { get; set; }
        public DateTime CreateDate { get; set; } = DateTime.Now;
        public DateTime? DeleteDate { get; set; }
        public bool IsDeleted { get; set; }
        public bool Active { get; set; } = true;
        public DomainType DomainType { get; set; }

        [ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
        public long UserAuthCustomId { get; set; }
    }

我试图根据 WatchedUrlRecords 的 ID 插入队列对象,所以我想出了这个查询:

                var q = db.From<WatchedUrlRecord>()
                    .Where(x => Sql.In(x.Id, ids))
                    .Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
                    .Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new WatchedUrlQueue()
                    {
                        UserAuthCustomId = w.UserAuthCustomId,
                        DomainKey = w.DomainKey,
                        CreateDate = DateTime.UtcNow,
                        DeferUntil = null,
                        FirstScan = firstScan,
                        TargetDomainKey = t.DomainKey,
                        Tries = 0,
                        TargetDomainRecordId = w.TargetDomainRecordId,
                        Url = w.Url,
                        WatchedUrlRecordId = w.Id
                    });

                var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd => dbCmd.OnConflictIgnore());

这不起作用并给出错误:

variable 'w' of type 'Project.ServiceModel.WatchedUrl.Entities.WatchedUrlRecord' referenced from scope '', but it is not defined

如果我尝试像 new {} 这样的匿名对象而不是 new WatchedUrlQueue 那么 InsertIntoSelect() 会抛出错误:

'watched_url_record"."user_auth_custom_id' is not a property of 'WatchedUrlQueue'

我查看了文档并且可以看到 SelectMulti() 方法,但我认为这不合适,因为它需要我创建一个元组列表以合并到新对象中。传递的列表可能非常大,所以我只想将正确的 SQL 语句发送到 PostgreSQL,这将沿着以下行:

insert into watched_url_queue (watched_url_record_id, url, domain_key, target_domain_record_id, target_domain_key, create_date, tries, defer_until, user_auth_custom_id)
select wur.id watched_url_record_id,
       wur.url url,
       wur.domain_key,
       wur.target_domain_record_id,
       tdr.domain_key,
       '{DateTime.UtcNow:MM/dd/yyyy H:mm:ss zzz}' create_date,
       0 tries,
       null defer_until,
       wur.user_auth_custom_id

from watched_url_record wur
join target_domain_record tdr on wur.target_domain_record_id = tdr.id
where wur.id in (323,3213123,312312,356456)
on conflict do nothing ;

我目前在我的应用程序中有很多类似类型的查询,这导致了维护它们的额外工作,能够让它们在不降低性能的情况下流畅地使用 api 真是太好了。这可能吗?

自定义 select 表达式不能是类型化投影(即 x => new MyType { ... }),即您需要使用匿名类型表达式(即 new { ... })来捕获您的查询的自定义 SELECT 投影表达式。

您还需要将 JOIN 表达式直接放在 FROM 之后(如 SQL 中所做的那样),这告诉 OrmLite 它需要完全限定后续的列表达式,例如 Id,否则会产生歧义.

我已经解决了 this commit 中自定义 select 表达式的字段解析问题,您的查询现在应该按预期工作了:

var q = db.From<WatchedUrlRecord>()
    .Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
    .Where(x => Sql.In(x.Id, ids))
    .Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new {
        UserAuthCustomId = w.UserAuthCustomId,
        DomainKey = w.DomainKey,
        CreateDate = DateTime.UtcNow,
        DeferUntil = (DateTime?) null,
        FirstScan = firstScan,
        TargetDomainKey = t.DomainKey,
        Tries = 0,
        TargetDomainRecordId = w.TargetDomainRecordId,
        Url = w.Url,
        WatchedUrlRecordId = w.Id
    });

var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd=>dbCmd.OnConflictIgnore());

此更改适用于 v5.10.5,即现在 available on MyGet