【问题标题】:linq2sql C#: How to query from a table with changing schema namelinq2sql C#:如何从具有更改模式名称的表中查询
【发布时间】:2020-02-19 18:35:20
【问题描述】:

我有一个网络服务,它尝试连接到桌面会计应用程序的数据库。

它有同名但不同模式名称的表,例如:

[DatabaseName].[202001].[CustomerCredit]
[DatabaseName].[202002].[CustomerCredit]
.
.
.
[DatabaseName].[202014].[CustomerCredit]
[DatabaseName].[202015].[CustomerCredit]
[DatabaseName].[202016].[CustomerCredit]
...
..
[DatabaseName].[2020xx].[CustomerCredit]

架构名称的格式为[Year+IncrementalNumber],例如[202014][202015][202016]等。

每当我想在数据库中查询客户信用信息时,如果 202016 是我数据库中的最新架构,我应该从具有最大数字的架构中获取信息,例如 [DatabaseName].[202016].[CustomerCredit]

注意: 会计应用程序数据库中新模式的创建没有规则,完全由会计应用程序的用户决定,安装在不同位置的每个应用程序实例可能有不同的模式数。

所以当我开发我的网络服务时,我不知道在开发之前连接到哪个架构。在运行时,我可以找到正确的模式以从其表中查询,但我不知道如何设法在查询中获取具有正确模式名称的表信息。 我通常创建一个 linq-to-sql dbml 类并使用它的定义从 db 读取信息,但我不知道如何以这种方式管理架构更改?

DBML 设计器像这样管理 Scehma 名称:

[global::System.Data.Linq.Mapping.TableAttribute(Name="[202001].CustomerCredit")]

但是,由于我的应用程序可以在运行时检索架构名称,我不知道如何在我的特殊情况下修复表声明。 在 ADO.NET 中很容易处理,但我不知道它在 Linq2SQL 中的等价物:

select count(*)  from [" + Variables.FinancialYearSchemaName + "].CustomerCredit  where SFC_Status = 100;

【问题讨论】:

  • 这不是 LINQ2SQL 问题,而是数据库设计问题。对于初学者,如果您使用table partitioning,则不需要这些表。分区在所有 SQL Server 版本中都可用。它曾经是企业独有的功能,但自 SQL Server 2016 SP1 起,它可用于所有版本,甚至是 Express
  • 在旧版本(即不受支持的版本)中,您可以使用 Partitionend views 将所有这些表合并为一个。如果表有约束,例如在 date 列上明确使用哪个表,服务器将只查询该单个表。
  • 简而言之,Linq2Sql 不必知道数据是如何分区的。 LINQ2SQL 是 ORM,而不是 SQL 替代品,它所要做的就是将对象映射到关系结构
  • @PanagiotisKanavos 所以你建议制作分区视图而不是在 linq2sql 中寻找解决方案?我的偏好不是在数据库层中寻找解决方案,因为目前数据库的设计不在我的范围内,而且我没有足够的授权来做这件事。除了以前版本的 Web 服务使用 ADO.NET 并且可以正常工作,但我决定将其迁移到 Entity Framework 和 Linq2SQL 但这是我的挑战之一
  • 这是一个与对象完全没有关系的数据库问题。 was using ADO.NET and works without problem 相反,它有完全相同的问题。它只是隐藏在视线之外,实际上传递给了其他人。数据库设计无缘无故地溢出到应用程序中。 DBA 无法在不破坏所有应用程序的情况下管理数据库。将旧数据移动到历史表等会破坏所有查询

标签: c# entity-framework linq-to-sql ado.net dbml


【解决方案1】:

最终,不:大多数 ORM 不希望架构更改在运行时发生变化,因此大多数 - 包括 EF 和 LINQ-to-SQL 不支持这种情况。一个可能的选项是有不同的连接字符串,每个都有不同的用户帐户,每个都有不同的默认模式在数据库中配置 - 并初始化你的数据库上下文与所需帐户匹配的连接字符串或连接。然后,如果 EF 向 RDBMS 询问 [CustomerCredit],它将首先查看该帐户的架构 ([202014].[CustomerCredit])。您可能应该避免在这种情况下使用[202014].[CustomerCredit],以防止混淆。然而,这是一个非常老套和丑陋的解决方案。但是......它应该可以工作。

或者,您将不得不对数据访问进行更多控制,本质上是编写自己的 SQL(可能用令牌替换模式,这有其自身的问题)。

【讨论】:

    【解决方案2】:

    该架构本质上是CustomerCredit 表的手动分区。最好的解决方案是使分区对所有用户透明。代码不应该知道数据是如何分区的。

    数据库解决方案

    数据库解决方案的好处是它们对用户透明或几乎透明,并且需要最少的维护

    表分区

    干净的解决方案是使用table partitioning,使不同的分区对所有用户透明。表分区曾经是企业独有的功能,但它在 SQL Server 2016 SP1 之后的所有版本中都可用,甚至是 Express。这意味着它在仍受主流支持的所有版本中都是免费的。

    该表根据一个函数(例如基于日期的函数)进行分区并存储在不同的文件中。只要有可能,查询优化器就可以检查分区边界和查询条件,并且只使用包含相关数据的文件。例如,在日期分区表中,包含日期过滤器的查询只能搜索相关分区。

    分区视图

    另一个选项,至少从 2000 年开始可用,是使用 partitionend views,本质上是一个结合所有表分区的 UNION ALL 视图,例如:

    SELECT <select_list1>  
    FROM [202001].[CustomerCredit]
    UNION ALL  
    SELECT <select_list2>  
    FROM [202002].[CustomerCredit]
    UNION ALL  
    ...  
    SELECT <select_listn>  
    FROM Tn;
    

    EF 可以将实体映射到视图而不是表。如果满足可更新视图的标准,则分区视图本身将是可更新的,并且将对正确的表进行任何修改。

    查询优化器可以利用表上的 CHECK 约束一次只搜索一个表,类似于分区表的工作方式。

    代码解决方案

    这需要原始 SQL 查询,以及每次进行更改时识别正确表/架构的方法。每次表分区更改时都需要对应用程序进行修改,无论是代码修改还是配置文件中的更改。

    在所有情况下,一个查询一次只能读取一张表

    保留 ADO.NET

    一种可能性是继续使用 ADO.NET,替换查询模板中的表/模式名称。如果需要,代码必须映射到对象,就像它已经做的那样。

    EF 原始 SQL

    另一个是使用 EF 的原始 SQL 功能,例如 EF Core 的 FromSqlRaw 从特定表中查询,与 ADO.NET 一样。好处是 EF 会将查询结果映射到对象。在 EF Core 中,原始查询可以与 LINQ 运算符结合使用:

    var query=$"select * from [DatabaseName].[{schemaName}].[CustomerCredit]"
    var credits = context.CustomerCredits
        .FromSqlRaw(query)
        .Where(...)
        .ToList();
    

    小巧玲珑

    另一种选择是使用 Dapper 或其他具有 ad-hoc 查询的微 ORM,类似于 ADO.NET,并将结果映射到对象:

    var query=$"select * from [DatabaseName].[{schemaName}].[CustomerCredit] where customerID=@ID";
    var credits=connection.Query<CustomerCredit>(query,new {ID=someID});
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-07
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      • 2010-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多