需要有关将ORM和SQL与遗留系统相结合的建议

[英]Need advice on combining ORM and SQL with legacy system


We are in the process of porting a legacy system to .NET, both to clean up architecture but also to take advantage of lots of new possibilities that just aren't easily done in the legacy system.

我们正在将遗留系统移植到.NET,既要清理架构,又要利用许多新的可能性,而这些新的可能性在遗留系统中并不容易。

Note: When reading my post before submitting it I notice that I may have described things a bit too fast in places, ie. glossed over details. If there is anything that is unclear, leave a comment (not an answer) and I'll augment as much as possible

注意:在提交之前阅读我的帖子时,我注意到我可能在某些地方描述的东西太快了,即。掩饰细节。如果有任何不清楚的地方,请留下评论(不是答案),我会尽可能地增加评论

The legacy system uses a database, and 100% custom written SQL all over the place. This has lead to wide tables (ie. many columns), since code that needs data only retrieves what is necessary for the job.

遗留系统使用数据库,并在整个地方使用100%自定义编写的SQL。这导致了宽表(即许多列),因为需要数据的代码只检索作业所需的内容。

As part of the port, we introduced an ORM layer which we can use, in addition to custom SQL. The ORM we chose is DevExpress XPO, and one of the features of this has also lead to some problems for us, namely that when we define a ORM class for, say, the Employee table, we have to add properties for all the columns, otherwise it won't retrieve them for us.

作为端口的一部分,除了自定义SQL之外,我们还引入了一个可以使用的ORM层。我们选择的ORM是DevExpress XPO,其中一个特性也给我们带来了一些问题,即当我们为Employee表定义一个ORM类时,我们必须为所有列添加属性,否则它不会为我们检索它们。

This also means that when we retrieve an Employee, we get all the columns, even if we only need a few.

这也意味着当我们检索一个Employee时,我们会得到所有列,即使我们只需要一些。

One nice thing about having the ORM is that we can put some property-related logic into the same classes, without having to duplicate it all over the place. For instance, the simple expression to combine first, middle and last name into a "display name" can be put down there, as an example.

使用ORM的一个好处是我们可以将一些与属性相关的逻辑放入相同的类中,而不必在整个地方复制它。例如,作为示例,可以将用于将名字,中间名和姓氏组合成“显示名称”的简单表达式放在那里。

However, if we write SQL code somewhere, either in a DAL-like construct or, well, wherever, we need to duplicate this expression. This feels wrong and looks like a recipe for bugs and maintenance nightmare.

但是,如果我们在某个地方编写SQL代码,无论是在类似DAL的构造中,还是在任何地方,我们都需要复制此表达式。这感觉不对,看起来像是臭虫和维护噩梦的秘诀。

However, since we have two choices:

但是,因为我们有两个选择:

  • ORM, fetches everything, can have logic written once
  • ORM,取一切,可以写一次逻辑

  • SQL, fetches what we need, need to duplicate logic
  • SQL,提取我们需要的东西,需要复制逻辑

Then we came up with an alternative. Since the ORM objects are code-generated from a dictionary, we decided to generate a set of dumb classes as well. These will have the same number of properties, but won't be tied to the ORM in the same manner. Additionally we added interfaces for all of the objects, also generated, and made both the ORM and the dum objects implement this interface.

然后我们提出了一个替代方案。由于ORM对象是从字典中生成的代码,因此我们决定生成一组哑类。它们具有相同数量的属性,但不会以相同方式绑定到ORM。此外,我们为所有对象添加了接口,也生成了接口,并使ORM​​和dum对象都实现了此接口。

This allowed us to move some of this logic out into extension methods tied to the interface. Since the dumb objects carry enough information for us to plug them into our SQL-classes and instead of getting a DataTable back, we can get a List back, with logic available, this looks to be working.

这使我们能够将一些逻辑移到与接口相关的扩展方法中。由于哑对象携带足够的信息以便我们将它们插入到我们的SQL类中而不是获取DataTable,我们可以返回一个List,并且逻辑可用,这看起来是有效的。

However, this has lead to another issue. If I want to write a piece of code that only displays or processes employees in the context that I need to know who they are (ie. their identifier in the system), as well as their name (first, middle and last), if I use this dumb object, I have no guarantee by the compiler that the code that calls me is really providing all this stuff.

然而,这导致了另一个问题。如果我想编写一段代码,只显示或处理上下文中的员工,我需要知道他们是谁(即他们在系统中的标识符),以及他们的名字(第一,中间和最后),如果我使用这个愚蠢的对象,编译器无法保证调用我的代码真的提供了所有这些东西。

One solution is for us to make the object know which properties have been assigned values, and an attempt to read an unassigned property crashes with an exception. This gives us an opportunity at runtime to catch contract breaches where code is not passing along enough information.

一种解决方案是让我们使对象知道哪些属性已赋值,并且尝试读取未分配的属性会因异常而崩溃。这使我们有机会在运行时捕获代码未传递足够信息的合同违规。

This also looks clunky to us.

这对我们来说也很笨拙。

So basically what I want advice on is if anyone else has been in, or are in, this situation and any tips or advice you can give.

所以基本上我想要的建议是,如果有其他人进入或处于这种情况,你可以给出任何提示或建议。

We can not, at the present time, break up the tables. The legacy application will still have to exist for a number of years due to the size of the port, and the .NET code is not a in-3-years-release type of project but will be phased in in releases along the way. As such, both the legacy system and the .NET code need to work with the same tables.

目前,我们不能打破这些局面。由于端口的大小,遗留应用程序仍然必须存在多年,并且.NET代码不是3年内发布类型的项目,但将在发布过程中分阶段实施。因此,遗留系统和.NET代码都需要使用相同的表。

We are also aware that this is not an ideal solution so please refrain from advice like "you shouldn't have done it like this". We are well aware of this :)

我们也知道这不是一个理想的解决方案,所以请不要提出“你不应该这样做”这样的建议。我们很清楚这个:)


One thing we've looked into is to create an XML file, or similar, with "contracts". So we could put into this XML file something like this:

我们研究的一件事是使用“合同”创建XML文件或类似文件。所以我们可以将这样的XML文件放入:

  • There is an Employee class with these 50 properties
  • 有一个包含这50个属性的Employee类

  • Additionally, we have these 7 variations, for various parts of the program
  • 此外,对于程序的各个部分,我们有这7种变体

  • Additionally, we have these 10 pieces of logic, that each require property X, Y and Z (X, Y and Z varies between those 10)
  • 另外,我们有这10个逻辑,每个都需要属性X,Y和Z(X,Y和Z在这10个之间变化)

This could allow us to code-generate those 8 classes (full class + 7 smaller variations), and have the generator detect that for variation #3, property X, Y and K is present, and I can then tie in either the code for the logic or the interfaces the logic needs into this class automagically. This would allow us to have a number of different types of employee classes, with varying degrees of property coverage, and have the generator automatically add all logic that would be supported by this class to it.

这可以允许我们代码生成那8个类(全类+7个较小的变体),并让生成器检测到变量#3,属性X,Y和K存在,然后我可以绑定任何代码逻辑或逻辑需要的接口自动地进入这个类。这将允许我们拥有许多不同类型的员工类,具有不同程度的属性覆盖,并让生成器自动添加此类支持的所有逻辑。

My code could then say that I need an employee of type IEmployeeWithAddressAndPhoneNumbers.

然后我的代码可以说我需要一个IEmployeeWithAddressAndPhoneNumbers类型的员工。

This too looks clunky.

这看起来也很笨重。

3 个解决方案

#1


3  

I would suggest that eventually a database refactoring (normalization) is probably in order. You could work on the refactoring and use views to provide the legacy application with an interface to the database consistent with what it expects. That is, for example, break the employe table down in to employee_info, employee_contact_info, employee_assignments, and then provide the legacy application with a view named employee that does a join across these three tables (or maybe a table-based function if the logic is more complex). This would potentially allow you to move ahead with a fully ORM-based solution which is what I would prefer and keep your legacy application happy. I would not proceed with a mixed solution of ORM/direct SQL, although you might be able to augment your ORM by having some entity classes which provide different views of the same data (say a join across a couple of tables for read-only display).

我建议最终数据库重构(规范化)可能是有序的。您可以使用重构和使用视图来为遗留应用程序提供与数据库接口一致的接口。也就是说,例如,将employees表分解为employee_info,employee_contact_info,employee_assignments,然后为遗留应用程序提供名为employee的视图,该视图在这三个表之间进行连接(如果逻辑是,则可能是基于表的函数)更复杂)。这可能会让您继续使用完全基于ORM的解决方案,这是我希望的并保持您的遗留应用程序的快乐。我不会继续使用ORM / direct SQL的混合解决方案,尽管您可以通过提供一些实体类来扩充ORM,这些实体类提供相同数据的不同视图(例如,跨几个表的连接以进行只读显示)。

#2


2  

"We can not, at the present time, break up the tables. The legacy application will still have to exist for a number of years due to the size of the port, and the .NET code is not a in-3-years-release type of project but will be phased in in releases along the way. As such, both the legacy system and the .NET code need to work with the same tables."

“目前我们不能打破这些表格。由于端口的大小,遗留应用程序仍然需要存在多年,并且.NET代码不是3年 - 发布类型的项目,但将在发布过程中分阶段进行。因此,遗留系统和.NET代码都需要使用相同的表。“

Two words: materialized views.

两个词:物化观点。

You have several ways of "normalizing in place".

你有几种“正常化”的方法。

  1. Materialized Views, a/k/a indexed views. This is a normalized clone of your source tables.

    物化视图,a / k / a索引视图。这是源表的规范化克隆。

  2. Explicit copying from old tables to new tables. "Ick" you say. However, consider that you'll be incrementally removing functionality from the old app. That means that you'll have some functionality in new, normalized tables, and the old tables can be gracefully ignored.

    从旧表到新表的显式复制。 “Ick”你说。但是,请考虑您将逐步从旧应用程序中删除功能。这意味着您将在新的规范化表中具有一些功能,并且可以优雅地忽略旧表。

  3. Explicit 2-way synch. This is hard, not not impossible. You normalize via copy from your legacy tables to correctly designed tables. You can -- as a temporary solution -- use Stored Procedures and Triggers to clone transactions into the legacy tables. You can then retire these kludges as your conversion proceeds.

    明确的双向同步。这很难,并非不是不可能。您可以通过复制从旧表到正确设计的表进行规范化。您可以 - 作为临时解决方案 - 使用存储过程和触发器将事务克隆到旧表中。然后,您可以在转换过程中淘汰这些kludges。

You'll be happiest to do this in two absolutely distinct schemas. Since the old database probably doesn't have a well-designed schema, your new database will have one or more named schema so that you can maintain some version control over the definitions.

你会很高兴在两个绝对不同的模式中做到这一点。由于旧数据库可能没有设计良好的模式,因此新数据库将具有一个或多个命名模式,以便您可以对定义维护某些版本控制。

#3


0  

Although I haven't used this particular ORM, views can be useful in some cases in providing lighter-weight objects for display and reporting in these types of databases. According to their documentation they do support such a concept: XPView Concepts

虽然我没有使用过这种特殊的ORM,但在某些情况下,视图在提供较轻的对象以便在这些类型的数据库中显示和报告时非常有用。根据他们的文档,他们确实支持这样一个概念:XPView Concepts


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2008/11/16/7125dc470e5c2bcb6f6b4905c1597023.html



 
粤ICP备14056181号  © 2014-2021 ITdaan.com