【问题标题】:Cross Context LINQ Joins in Azure?Azure 中的跨上下文 LINQ 联接?
【发布时间】:2016-02-22 14:43:31
【问题描述】:

我们目前正在将生产平台迁移到 Azure,因此我需要迁移我们所有的支持工具。以前,我们严重依赖数据适配器和存储过程,其中许多存储过程执行跨数据库连接。

随着我们迁移到 Azure,这些跨数据库连接都无法运行。我尝试将我们的数据适配器移动到实体框架,但我似乎无法让它们工作。相反,我收到一条错误消息,指出不允许跨上下文连接。其中许多查询依赖于来自多个数据库的数据,因此我只是想找出实现此操作的最佳方法。

我查看了其他几个要求类似解决方案的问题,但似乎没有一个非常适用于我的解决方案。

例如,下面是 SQL 中更简单的查询之一:

USE CustomerDB1234
SELECT DISTINCT u.[UserID]
  ,u.[UserLogin]
  ,u.[UserPhoneNumber]
  ,u.[UserPasswordHash]  
  , ISNULL(gl.[gl_login_name],'* no global login ID') AS [gl_login_name]
  ,gl.[gl_password_hash]
  ,gl.[gl_GUID]
  ,gl.[gl_Email_Validated]
  ,u.[usr_unit_set_id]
  ,oob.[oob_org_id]
FROM [dbo].[User] u WITH (NOLOCK)
LEFT JOIN [dbo].[OrganizationObjectBridge] oob WITH (NOLOCK) ON oob.[oob_object_type_id] = 9 AND oob.[oob_object_id]= u.UserID  
LEFT JOIN [MainServer].[MainDb].[dbo].[GlobalLoginCustomerBridge] glcb WITH (NOLOCK) ON glcb.[glcbr_user_id] = u.UserID
    AND glcb.[glcbr_customer_id] = dbo.efnGetCustomerID()
LEFT JOIN [MainServer].[MainDb].[dbo].[GlobalLogin] gl WITH (NOLOCK) ON gl.[gl_id] = glcb.[glcbr_gl_id]
WHERE ([UserID] = @userID OR @userID IS NULL)
    AND ([UserDisabled] = @isDisabled OR @isDisabled IS NULL)
ORDER BY [gl_login_name]

在 Linq 中,它看起来类似于:

List<User2> userList = new List<User2>();
using (var e = new eContext())
using (var context = new CustomerContext(CustomerID))
{
    var databaseConnections = e.DatabaseConnectionStrings;
    var customer = e.Customers.Select(n => new
    {
        ID = n.CustomerID,
        Name = n.CustomerName,
        Email = n.CustomerEmail,
        Website = n.CustomerWWW,
        Logo = n.CustomerLogo,
        DatabaseConnectionName = databaseConnections.FirstOrDefault(d => d.DatabaseConnectionID == n.DatabaseConnectionID).DatabaseConnectionName,
        DatabaseConnectionString = databaseConnections.FirstOrDefault(d => d.DatabaseConnectionID == n.DatabaseConnectionID).DatabaseConnectionString1,
        AccountNumber = n.CustomerAcctNumber
    }).FirstOrDefault(n => n.ID == CustomerID);

    userList = context.Users
        .Join(e.GlobalLoginCustomerBridges,
            u => u.UserID,
            glcb => glcb.glcbr_user_id,
            (u, glcb) => new { u, glcb })
            .Where(n => n.glcb.glcbr_customer_id == CustomerID)
        .Join(e.GlobalLogins,
            glcb => glcb.glcb.glcbr_gl_id,
            gl => gl.gl_id,
            (glcb, gl) => new { glcb, gl })
        .Join(context.OrganizationObjectBridges,
            glcb => glcb.glcb.u.UserID,
            oob => oob.oob_object_id,
            (glcb,oob) => new {glcb, oob})
            .Where(n=>n.oob.oob_object_type_id == 9)
       .Select(n => new
       {
           ID = n.glcb.glcb.u.UserID,
           GlobalLogin = n.glcb.gl.gl_login_name,
           FirstName = n.glcb.glcb.u.UserFirstName,
           MiddleName = n.glcb.glcb.u.UserMiddleName,
           LastName = n.glcb.glcb.u.UserLastName,
           GUID = n.glcb.gl.gl_GUID,
           UserID = n.glcb.gl.gl_id,
           HasSHA256Hash = n.glcb.gl.gl_password_hash_sha256 == null,
           Customer = customer,
           Organization = context.Organizations
               .Select(o => new
               {
                   ID = o.org_id,
                   Name = o.org_name,
                   ParentID = o.org_parent_id,
                   ExternalID = o.org_external_id,
                   Default = o.org_default,
                   Logo = o.org_logo,
                   URL = o.org_url,
                   PeerGroupID = o.org_peer_grp_id,
                   ExternalInfo = o.org_external_info
               }).Cast<Organization2>().FirstOrDefault(o=>o.ID == n.oob.oob_org_id)
    }).Cast<User2>().ToList();

}

【问题讨论】:

标签: c# sql-server entity-framework linq azure


【解决方案1】:

两种方法:

  1. 在内存中执行应用程序中的连接。根据查询,这或多或少是有效的。此外,所需的代码更改范围从微小到可怕。
  2. 将数据库合并在一起。拥有许多没有物理原因的数据库是一种反模式。数据库不是应用程序模块化的逻辑单元。通过模式或表名前缀进行文档模块化。数据库是物理部署单元。

第三种方法:在 VM 中运行 SQL Server。

【讨论】:

  • 至于选项2,这有点问题。我没有发言权——所以当谈到我们的架构时,我们有一个完整的开发团队专注于这方面的事情,我只是来为我们的支持团队编写工具。但是,我们的架构基本上由 1 个包含共享数据的主数据库和数百个为每个客户保存数据的单租户数据库组成。
  • 我假设您需要同时查询 1 个租户和共享数据库?
  • 通常是的,在某些情况下我可能需要查询 2 个租户,但这种情况要少得多。在我们较旧的代码库中,当事物托管在本地时,我们可以使用链接服务器,但 Azure 中并不完全存在此功能。我在上面的问题中发布了一个更简单的查询。
  • 你的处境很糟糕。在我看来,SQL Azure 是核选项。摆脱了很多维护,但也扼杀了很多功能和灵活性。自己决定……
  • 我什至不知道从哪里开始这些更相互关联的联接。
【解决方案2】:

通过使用另一个答案发布的信息,我想出了一个似乎可行的解决方案。诚然,它看起来不像跨数据库连接那样高效或简洁,但它可以完成工作。

using (var context = new CustomerContext(CustomerID))
using (var e = new eContext())
{
    var globalUserList = e.GlobalLoginCustomerBridges
        .Join(e.GlobalLogins,
            glcb => glcb.glcbr_gl_id,
            gl => gl.gl_id,
            (glcb, gl) => new { glcb, gl })
        .Where(n => n.glcb.glcbr_customer_id == CustomerID)
        .Select(n => new User2
        {
            ID = (int)n.glcb.glcbr_user_id,
            GlobalLogin = n.gl.gl_login_name,
            GUID = n.gl.gl_GUID
        }).ToList();

    var customer = e.Customers
        .Join(e.DatabaseConnectionStrings,
        c => c.DatabaseConnectionID,
        d => d.DatabaseConnectionID,
        (c, d) => new { c, d })
        .Select(n => new Customer2
        {
            ID = n.c.CustomerID,
            Name = n.c.CustomerName,
            DatabaseConnectionName = n.d.DatabaseConnectionName,
            DatabaseConnectionString = n.d.DatabaseConnectionString1,
            GUID = n.c.cust_guid,
        }).ToList().FirstOrDefault(n => n.ID == CustomerID);

    var orgs = context.Organizations
        .Select(o => new Organization2
        {
            ID = o.org_id,
            Name = o.org_name,
        }).ToList();

    var users = context.Users
        .Select(n => new User2
        {
            ID = n.UserID,
            FirstName = n.UserFirstName,
        }).ToList();

    var userList = users
        .Join(globalUserList,
            u => u.ID,
            gl => gl.ID,
            (u, gl) => new { u, gl })
        .Join(context.OrganizationObjectBridges,
            u => u.u.ID,
            oob => oob.oob_object_id,
            (u, oob) => new { u, oob })
            .Where(o => o.oob.oob_object_type_id == 9)
        .Select(n => new User2
        {
            ID = n.u.u.ID,
            GlobalLogin = n.u.gl.GlobalLogin,
            FirstName = n.u.u.FirstName,
            GUID = n.u.gl.GUID,
            Customer = customer,
            Organization = orgs.FirstOrDefault(o => o.ID == n.oob.oob_org_id)
        }).Where(n => !isDisabled != null && n.Disabled == isDisabled).ToList();

    return userList;
}

【讨论】:

  • 是的,这行得通。这并不有趣,有时您会陷入效率陷阱,您的时间复杂度为二次方,但由于查询优化,数据库以前以线性时间完成此操作。请务必在部署前进行测试:)
  • orgs.FirstOrDefault... 看起来就是这种情况。连接通常更快,因为它在内部使用哈希表。
猜你喜欢
  • 2011-07-22
  • 2010-10-28
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-22
  • 1970-01-01
相关资源
最近更新 更多