Linq 加入以针对 Dynamics CRM 2015 执行特定查询

Linq join to perform specific query against Dynamics CRM 2015

我正在寻找有关特定 Linq 连接查询的帮助。我的 Linq 知识相当基础,我一整天都在努力编写正确的连接代码。

我实际上正在尝试构建一个 Linq 查询,该查询将使用 CrmSvcUtil.exe 实用程序生成的 ServiceContext 与 Dynamics CRM Online 2015 SDK 一起使用。

显然 CRM Linq 提供程序存在局限性(ref1, ref2, ref3 等)。使用我熟悉的 Linq 查询类型时,我经常会遇到以下错误。似乎答案是使用更自然的 Linq 连接。

Invalid 'where' condition. An entity member is invoking an invalid property or method.

与其向您展示我 100 多次失败的尝试,我认为最好使用 SQL 示例来展示我正在努力实现的目标。下面的示例脚本。本质上,我有一个实体,我想为其 return 记录列表。这与另一个实体有两个 N:N 关系。我想 return 主要实体的所有实例,其中它与一个 N:N 关系中的给定 ID 相关联,并且不与另一个 N:N 关系中的相同 ID 相关联。

我最费劲的部分是执行包含内部联接和左外部联接的 Linq 查询。即使您没有使用 CRM Linq Provider 的直接经验,它仍然可以帮助我了解这在 Linq 中通常是如何完成的。非常感谢大家的帮助。

SQL 我想用 Linq 构建的查询:

DECLARE @id INT = 1

-- Should only return entities with IDs 1 and 2
SELECT a.* FROM [dbo].[MainEntity] a
INNER JOIN [dbo].[AltOne] b ON a.EntityID = b.EntityID AND b.AltOneID = @id
LEFT JOIN [dbo].[AltTwo] c ON a.EntityID = c.EntityID AND c.AltOneID = @id
WHERE c.AltOneID IS NULL

数据库设置脚本:

CREATE TABLE [dbo].[MainEntity](
    [EntityID] [int] NOT NULL,
    [EntityName] [varchar](50) NOT NULL,
    CONSTRAINT [PK_MainEntity] PRIMARY KEY CLUSTERED 
    ( 
        [EntityID] ASC 
    )
)
GO

CREATE TABLE [dbo].[AltOne](
    [EntityID] [int] NOT NULL,
    [AltOneID] [int] NOT NULL,
    CONSTRAINT [PK_AltOne] PRIMARY KEY CLUSTERED 
    (
        [EntityID] ASC,
        [AltOneID] ASC
    )
)
GO

ALTER TABLE [dbo].[AltOne]  WITH CHECK ADD  CONSTRAINT [FK_AltOne_MainEntity] FOREIGN KEY([EntityID])
REFERENCES [dbo].[MainEntity] ([EntityID])
GO

ALTER TABLE [dbo].[AltOne] CHECK CONSTRAINT [FK_AltOne_MainEntity]
GO

CREATE TABLE [dbo].[AltTwo](
    [EntityID] [int] NOT NULL,
    [AltOneID] [int] NOT NULL,
    CONSTRAINT [PK_AltTwo] PRIMARY KEY CLUSTERED 
    (
        [EntityID] ASC,
        [AltOneID] ASC
    )
)
GO

ALTER TABLE [dbo].[AltTwo]  WITH CHECK ADD  CONSTRAINT [FK_AltTwo_MainEntity] FOREIGN KEY([EntityID])
REFERENCES [dbo].[MainEntity] ([EntityID])
GO

ALTER TABLE [dbo].[AltTwo] CHECK CONSTRAINT [FK_AltTwo_MainEntity]
GO

INSERT INTO [dbo].[MainEntity] ([EntityID], [EntityName]) VALUES (1, 'Test 1')
INSERT INTO [dbo].[MainEntity] ([EntityID], [EntityName]) VALUES (2, 'Test 2')
INSERT INTO [dbo].[MainEntity] ([EntityID], [EntityName]) VALUES (3, 'Test 3')
GO

INSERT INTO [dbo].[AltOne] ([EntityID], [AltOneID]) VALUES (1, 1)
INSERT INTO [dbo].[AltOne] ([EntityID], [AltOneID]) VALUES (1, 2)
INSERT INTO [dbo].[AltOne] ([EntityID], [AltOneID]) VALUES (2, 1)
INSERT INTO [dbo].[AltOne] ([EntityID], [AltOneID]) VALUES (2, 2)
INSERT INTO [dbo].[AltOne] ([EntityID], [AltOneID]) VALUES (3, 1)
GO

INSERT INTO [dbo].[AltTwo] ([EntityID], [AltOneID]) VALUES (3, 1)
INSERT INTO [dbo].[AltTwo] ([EntityID], [AltOneID]) VALUES (1, 2)
INSERT INTO [dbo].[AltTwo] ([EntityID], [AltOneID]) VALUES (2, 2)
GO

编辑 1:

根据要求添加示例 类。重申一下,我需要 return MainEntity 个对象的集合 而不是直接使用 它的 ICollection 属性,而是使用连接(这似乎是一个CRM Linq 提供商的限制)。该列表必须是通过 CollectionOne 与特定 RelatedEntity 相关的对象,但 与相同对象相关RelatedEntity 通过 CollectionTwo。我希望这是清楚的。

public class MainEntity
{
    public int EntityID { get; set; }
    public string EntityName { get; set; }

    public ICollection<RelationshipOne> CollectionOne { get; set; }
    public ICollection<RelationshipTwo> CollectionTwo { get; set; }
}

public class RelationshipOne
{
    public int EntityID { get; set; }
    public int AltOneID { get; set; }

    public ICollection<MainEntity> MainEntities { get; set; }
    public ICollection<RelatedEntity> RelatedEntities { get; set; }
}

public class RelationshipTwo
{
    public int EntityID { get; set; }
    public int AltOneID { get; set; }

    public ICollection<MainEntity> MainEntities { get; set; }
    public ICollection<RelatedEntity> RelatedEntities { get; set; }
}

public class RelatedEntity
{
    public int RelatedEntityID { get; set; }
    public string RelatedEntityName { get; set; }

    public ICollection<RelationshipOne> RelationshipOnes { get; set; }
    public ICollection<RelationshipTwo> RelationshipTwos { get; set; }
}

public class DummyContext
{
    public System.Data.Entity.DbSet<MainEntity> MainEntitySet { get; set; }
    public System.Data.Entity.DbSet<RelationshipOne> RelationshipOneSet { get; set; }
    public System.Data.Entity.DbSet<RelationshipTwo> RelationshipTwoSet { get; set; }
    public System.Data.Entity.DbSet<RelatedEntity> RelatedEntitySet { get; set; }
}

问题出在你的需求上:

I want to return all instances of the main entity where it is associated with a given ID in one N:N relationship, and NOT associated with the same ID in the other N:N relationship.

您无法在 Dynamics CRM 中使用 Linq 查询来完成此操作。 Dynamics CRM 的 Linq 查询转换为 QueryExpression 查询。使用 QueryExpression 不可能 select 与其他记录关联的记录。

同样重要的是要提及:LINQ for CRM 不支持左外连接,但 QueryExpression 查询支持左外连接。

您唯一的选择是 select(希望如此)多记录几条,然后过滤不需要的记录。

一句警告:当我不得不面对类似的要求时,以下对我有用,但它可能会对系统性能造成影响,具体取决于数据、其他自定义等。彻底测试是必须.

您可以 "cheat" 使用插件完成此操作。

  1. 为您需要跟踪的每个关系向 MainEntity 添加一个整数字段
  2. 构建一个插件来计算相关记录并更新 MainEntity 中新添加的字段(这应该在 Retrieve/RetrieveMultiple 消息上注册为同步 Post-Operation)。出于可重用性目的,通过不安全的配置为其提供字段和关系名称。

您现在可以查询 MainEntity 并知道您需要的一切,不再需要更显式的连接(如果您 need/want 它也可以将列表作为视图)。