65.9K
CodeProject 正在变化。 阅读更多。
Home

LayerGen 3.5

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (78投票s)

2015年6月14日

CPOL

25分钟阅读

viewsIcon

340853

downloadIcon

8727

自动从 SQL Server、Microsoft Access、MySql 或 SQLite 数据库设计中生成 C# 或 VB.Net 的业务层和数据层。

下载源代码

下载可执行文件

引言

很久以前(大约七年前),我发布了一个名为“LayerGen MMX”的小程序。我早在 2002 年 .NET 框架首次发布时就开始了 LayerGen。在 LayerGen MMX 之后,我想要完全重写该程序。我做到了,七年后,我很自豪地推出 LayerGen 3.5。

如果您不熟悉 LayerGen,它是一个 ORM 工具,可以根据数据库模式自动生成数据层和业务层。它将生成 C# 或 VB.NET 代码。它目前支持 Microsoft SQL Server(2000 到 2014 版本)、Microsoft Access、SQLite 或 MySql。此外,它生成的代码应该与 .NET 2.0 到当前 .NET 版本兼容。此外,您还可以动态配置 LayerGen,使其与存储过程或纯 SQL 文本配合使用(注意:Microsoft Access 和 SQLite 不支持存储过程)。

使用 LayerGen 一段时间后,您很快就会意识到 LayerGen 最大的卖点和特点是它的易用性和直观性。与同类产品相比,LayerGen 使访问数据库变得轻而易举。

教程演练

简介和先决条件

我认为学习如何使用 LayerGen 的最佳方法是深入学习教程。LayerGen 支持 4 种不同的数据库系统:MySql、SQL Server、Microsoft Access 和 SQLite。在本教程中,我将使用 SQL Server,但是,您可以轻松地使用其他 3 种数据库服务器中的任何一种。从用户的角度来看,LayerGen 是特意创建的,因此无需实施任何代码更改。无论后端数据库如何,LayerGen 都会在后端创建兼容的代码,这不应干扰您的前端代码。

话虽如此,如果您计划使用 MySql 或 SQLite,您必须下载您的数据库服务器的 ADO.NET 连接器,或者您可以使用 LayerGen 3.5 中预编译的连接器。LayerGen 中预编译的连接器以 32 位模式编译以保持兼容性。如果您使用它们,您必须记住将您的项目设置为 x86 模式。如果您想使用 64 位,您必须从相应的网站下载连接器。MySql 连接器可以从此处下载。如果您想使用 SQLite,您可以从此处下载连接器。如果您正在使用 Microsoft SQL Server,您无需担心任何事情,因为 .NET 框架中已经内置了 ADO.Net 连接器。

您可能需要的另一件事是用于创建数据库、表、视图以及任何其他可想象的数据库开发人员管理工具。有几种工具可用,我将在此处链接推荐的工具。所有这些工具都是免费的。对于 SQL Server,我推荐 SQL Server Management Studio。您可以从此处下载。对于 MySql,我推荐 MySql Workbench。它是免费使用的,并且有 Windows 和 Linux 版本。您可以从此处下载。最后,对于 SQLite,我推荐 SQLite Expert Personal。可以从此处下载。请记住,您可以自由使用任何您想要的工具。这些只是建议。

设计数据库

我们将为 Foo 企业创建一个简单的销售数据库。首先,创建一个名为“FooEnterprises”的数据库。创建一个名为“Product”的表,其模式如下:

字段名 数据类型 注释
ProductId int 主键,标识(自动递增)
ProductName nvarchar(500)  
WholesaleCost money  
SaleCost money  

将以下行插入到新的 Product 表中

ProductName WholesaleCost SaleCost
苹果 0.25 0.39
橘子 1.25 1.50
巧克力棒 1.89 2.15

接下来,为客户创建一个表,并将其命名为“Customer”

字段名 数据类型 注释
CustomerId int 主键,标识(自动递增)
FirstName nvarchar(100)  
LastName nvarchar(100)  

将以下行插入到新的客户表中

FirstName LastName
迈克 史密斯
萨拉 贝克

最后,我们创建最后一个表,名为“CustomerProduct”

字段名 数据类型 注释
CustomerProductId int 主键,标识(自动递增)
CustomerId int 客户表的外键
ProductId int 产品表的外键

请务必在 CustomerProduct 中的 CustomerId 和 Customer 中的 CustomerId 之间设置外键关系。同时在 CustomerProduct 中的 ProductId 和 Product 中的 ProductId 之间设置外键关系。最后,将这些行插入到新的 CustomerProduct 表中

CustomerId ProductId
1 3
1 1
2 3
2 2
2 3

使用 LayerGen 3.5

我们终于可以使用 LayerGen 了。我们将使用 LayerGen 自动创建访问表和数据所需的所有代码。第一次启动 LayerGen 时,它看起来像这样

该屏幕非常直观,每个选项旁边都有帮助图标。您可以将鼠标悬停在任何帮助图标上,以获取带有更多信息的工具提示弹出窗口。

首先要做的就是从标记为“SQL Server:”的下拉菜单中选择您的数据库引擎。您将可以选择“SQL Server”、“MySql”或“SQLite”。根据您选择的选项,选项将发生变化。

填写适当的选项以连接到您的数据库。根据您选择的选项,连接字符串构建器将在您填写选项时自动更新。如果您想手动编辑连接字符串,可以选择“自定义连接字符串”。

连接字符串构建完成后,您可以单击“对象”旁边的按钮。这将显示数据库中所有表或视图的列表。LayerGen 只会为您在此窗口中选择的对象创建代码。由于我们的示例没有视图,您将只看到我们在窗口的“表”部分下创建的 3 个表。勾选所有 3 个复选框,然后单击“确定”。窗口将消失,您将在“对象”文本框中看到选定的对象。

接下来,我们需要指定一个输出文件夹。单击“输出”文本框旁边的按钮,以打开文件夹浏览器。在您的硬盘上选择一个空文件夹,LayerGen 可以在其中创建必要的文件。

接下来,在语言下拉列表中,选择一种语言,C# 或 VB.Net。在此示例中,我将使用 C#,但是,如果您愿意,也可以选择 VB.Net。C# 和 VB.Net 生成的代码功能相同。您还可以通过选中“包含注释”复选框来选择在生成的代码中包含一些注释。如果您不希望包含注释,请清除此复选框(无论此选项如何,XML 文档注释将始终生成)

最后,在语言选项部分,您可以设置各种影响生成代码的选项。默认情况下,LayerGen 将在“DataLayer.<数据库名称>”和“BusinessLayer.<数据库名称>”命名空间中创建代码。如果您希望提供自定义命名空间名称,只需选中该框并输入您自己的命名空间名称。

LayerGen 能够根据 SQL 语句或存储过程检索数据,并将该数据放入自定义类中。要启用此功能,请勾选标记为“启用动态数据检索”的复选框。在此示例中,请确保勾选此框,以便我稍后演示其用法。请注意,启用此功能会使生成的代码仅适用于 .NET 4.0 或更高版本。

下一个选项是自动右侧修剪传入字符串的选项。在 Sql Server 中,如果您创建了一个数据类型为 char(10) 或 nchar(10) 的字段,Sql Server 会在您的字符串末尾添加额外的空格,使其长度恰好为 10 个字符。通过启用此选项,LayerGen 将自动右侧修剪从数据库中读取的所有字符串。请注意,这只修剪从数据库中读取的数据。在执行插入或更新时,它仍将按原样存储数据。

最后一个选项是启用序列化的选项。LayerGen 可以将数据序列化和反序列化为 JSON、BSON 和 Xml 格式。通过勾选此框,为本教程启用此选项。序列化将需要引用 Newtonsoft 的 JSON.Net 库。它可以通过NuGetNewtonsoft 网站获取。

设置完所有选项后,单击“创建层”按钮,LayerGen 就会完成其工作。这是我的 LayerGen 设置的屏幕截图

LayerGen 完成后,您可以关闭它并浏览到您在硬盘上指定的输出文件夹。对于每个选定的表或视图,LayerGen 将创建两个文件,一个数据层文件和一个业务层文件。此外,它还会创建一个名为“Universal”的文件。此文件包含连接字符串以及一些共享内容,例如异常。还会生成一个“StoredProcedures.Sql”文件。此文件包含使 LayerGen 与存储过程一起工作所需的基本 CRUD 操作的必要存储过程。如果您不使用存储过程,则不需要此文件。请注意,如果您使用的是 SQLite,则不会创建此文件,因为 SQLite 目前不支持存储过程。

下一步是启动 Visual Studio。在此示例中,我使用的是 Visual Studio 2013。创建一个名为“Foo”的新控制台应用程序,并确保它面向 .NET 4.0 或更高版本。项目创建后,右键单击解决方案并添加一个新的类库项目。您可以将此项目命名为“FooLayers”。虽然不是必需的,但将数据层与主 UI 分开总是一个好主意。这样,如果需要,您可以在另一个应用程序中重用这些层。

删除 Visual Studio 自动创建的“Class1.cs”文件,然后右键单击“FooLayers”项目并单击“管理 NuGet 包”。搜索“Json.Net”并将其添加到项目中。或者,您可以右键单击“FooLayers”项目并单击“添加引用...”,然后手动浏览到 Json.Net DLL 文件。接下来,单击“添加现有项”。添加 LayerGen 为您创建的所有文件,除了 StoredProceduresScript.Sql 文件。最后,右键单击 Foo 项目上的引用并添加对 FooLayers 项目的引用。构建项目,您应该不会遇到任何错误!

使用 LayerGen 生成的代码

终于,是时候动手编写一些代码了。对于第一个示例,我想向您展示获取表中所有行并将其打印到控制台是多么容易。让我们打印出我们所有的产品。将此代码添加到 Foo 项目中 Program.cs 的 Main 方法中

    var products = new BusinessLayer.FooEnterprises.Products();
    products.GetAll();
    
    foreach (BusinessLayer.FooEnterprises.Product product in products)
    {
        Console.WriteLine("{0}", product.ProductName);
    }

运行程序,您应该会看到我们之前添加的所有产品列表打印到屏幕上。当 LayerGen 创建您的代码时,它会创建几个类。主类名基于表名。此外,它还通过在主类名末尾附加一个“s”来为主类创建一个集合类(称为复数)。此集合类中的一个方法是名为“GetAll”的方法,它从表中拉取所有行。

假设我们只想要包含字母“L”的产品。删除您刚刚创建的代码,并将其替换为以下代码

    var lProducts = new BusinessLayer.FooEnterprises.Products();
    lProducts.GetBySqlStatement(
        "SELECT * FROM " + BusinessLayer.FooEnterprises.Product.LgTableName + " WHERE ProductName LIKE '{0}'", "%L%");
    
    foreach (BusinessLayer.FooEnterprises.Product product in lProducts)
    {
        Console.WriteLine("{0}", product.ProductName);
    }

现在运行程序将打印“苹果”和“巧克力棒”,这是我们拥有的仅有的两个包含字母“L”的产品。除了 GetAll() 方法外,集合类还包含一个 GetBySqlStatement() 方法。此方法根据传入的 SQL 语句检索数据。您会注意到在字符串中,我们使用 {0} 而不是实际参数。我们这样做有两个原因。首先,为了避免 SQL 注入,其次,为了转义我们的字符串。SQL 注入是一种黑客攻击类型,允许黑客动态更改您的 SQL 并可能获取敏感数据甚至删除数据。始终使用 {0} 样式来传递参数!在上面的示例中需要注意的另一件事是使用“BusinessLayer.FooEnterprises.Product.LgTableName”。这是一个返回包含表名的字符串的常量。还有一个名为 LgPrimaryKey 的属性,它返回主键字段的名称。

除了 GetBySqlStatement() 方法外,还有一个姐妹方法叫 GetByStoredProcedure()。顾名思义,此方法执行存储过程。您可以选择传入一个参数字典。这是一个用法示例

    lProducts.GetByStoredProcedure("spGetProduct", new Dictionary<string, object> {{"@id", 1}});

当然,运行此代码会导致运行时错误,因为我们简单的数据库没有该存储过程。但是,如果您想创建一个名为“spGetProduct”的存储过程,它接受一个名为“id”的参数,您可以自己尝试一下。

处理单行

到目前为止,我们一直在使用复数集合类。假设我们想根据主键加载单行数据?擦除 Main() 方法下所有代码,并输入以下代码

    var sara = new BusinessLayer.FooEnterprises.Customer(2);
    
    Console.WriteLine("{0} {1}", sara.FirstName, sara.LastName);

这将加载 CustomerId 为 2 的客户,在我们的简单数据库中,这个人是 Sara。请注意,我们使用单数版本加载单行(例如,“Customer”而不是“Customers”)。每当您将主键值传递给构造函数时,对象会自动创建,并且与该主键匹配的记录会自动加载到对象中。

假设萨拉结婚了,并将她的姓氏改为琼斯?更新一行非常简单。删除 Console.WriteLine 行,并添加此代码

    sara.LastName = "Jones";
    sara.Save();

如果再次运行该程序,将不会有任何输出。但是,如果您打开 SQL Server Management Studio 并查看 Customer 表,您会看到 Sara 的姓氏确实改成了“Jones”。

插入新行同样简单。让我们插入另一个名为 Mindy Franklin 的客户。删除 Main() 方法中的所有代码,然后输入此代码

    var mindy = new BusinessLayer.FooEnterprises.Customer();
    
    mindy.FirstName = "Mindy";
    mindy.LastName = "Franklin";
    mindy.Save();
    
    Console.WriteLine("{0}'s id is {1}", mindy.FirstName, mindy.CustomerId);

再次运行此程序,您将看到 Mindy 的新 ID,它应该是 3。如果您再次打开 SQL Server Management Studio 并查看 Customer 表,您现在将看到 Mindy Franklin 在那里。请注意,我们这次传递了一个空构造函数。如果在构造函数中未传入主键值,则对象将创建为空。另请注意,由于我们的主键是一个标识(自动递增),因此我们无需指定 CustomerId。实际上,如果我们尝试隐式设置 CustomerId 的值,它会导致编译时错误。另请注意,一旦保存新行,主键值(如果是标识)将被捕获并存储在 CustomerId 属性中供我们使用。

另一件要演示的事情是空值。让我们添加一个名为 Sam 的新客户。让我们假装 Sam 不想分享他的姓氏。再次删除 Main() 方法中的所有行,并输入这些行

    var sam = new BusinessLayer.FooEnterprises.Customer();
    
    sam.FirstName = "Sam";
    sam.SetNull(BusinessLayer.FooEnterprises.Customer.Fields.LastName);
    sam.Save();
    
    if (sam.IsNull(BusinessLayer.FooEnterprises.Customer.Fields.LastName))
    {
        Console.WriteLine("{0} has no last name!", sam.FirstName);
    }
    else
    {
        Console.WriteLine("{0} has a last name! It's {1}", sam.FirstName, sam.LastName);
    }

运行此程序,您将看到输出显示 Sam 没有姓氏。如果您在 SQL Server Management Studio 中查看 Customer 表,您会发现 Sam 的 LastName 字段中确实有一个空值。您可以使用 SetNull() 方法将任何字段设置为 null 值,只要数据库中的字段允许 null 即可。您还可以使用 IsNull() 方法测试字段是否设置为 null。

本节要演示的最后一件事是删除行。让我们从 Customer 表中删除 Sam。删除 Main() 方法中的所有行,然后输入此代码

    var sam = new BusinessLayer.FooEnterprises.Customer(4); // Sam should be CustomerId 4
    sam.Delete();

在运行此程序之前,您应该在 Sql Server Management Studio 中仔细检查并确保 Sam 的 CustomerId 为 4。运行此程序将不会产生任何输出。但是,如果运行此程序,然后运行 Sql Server Management Studio 并查看 Customer 表,您会看到 Sam 已不在数据库中。再见 Sam!

外键

当 LayerGen 在表中遇到外键时,它会创建一个特殊的属性,以“F”开头,并以外键的 Id 字段名结尾。此属性可用于直接访问主键表,而无需实例化主键表的新实例。此外,您还将获得新的“GetBy”方法,允许您根据主键表的 Id 获取数据。示例总是比文字更有说服力。再次删除 Main() 方法中的所有行,并输入此代码

    var purchases = new BusinessLayer.FooEnterprises.CustomerProducts();
    purchases.GetByCustomerId(1);
    
    foreach (BusinessLayer.FooEnterprises.CustomerProduct purchase in purchases)
    {
        Console.WriteLine("{0} {1} bought a(n) {2}", purchase.FCustomerId.FirstName,
            purchase.FCustomerId.LastName, purchase.FProductId.ProductName);
    }

如果您还记得我们创建的名为 CustomerProduct 的表,它不包含任何数据。它只包含连接客户和产品的 ID。如果您运行此程序,它将显示 Mike Smith 的所有购买记录(一块巧克力棒和一个苹果)。请注意,CustomerProducts() 集合包含一个名为 GetByCustomerId() 的方法,它会拉取 CustomerProducts 表中所有客户 ID 等于 1(Mike Smith 的客户 ID)的数据。还有一个类似的方法名为 GetByProductId(),您可以使用它来查看所有购买特定产品的客户。另请注意,我们不必这样做

    foreach (BusinessLayer.FooEnterprises.CustomerProduct purchase in purchases)
    {
        var customer = new BusinessLayer.FooEnterprises.Customer(purchase.CustomerId);
        var product = new BusinessLayer.FooEnterprises.Product(purchase.ProductId);
        
        Console.WriteLine("{0} {1} bought a(n) {2}", customer.FirstName, customer.LastName, product.ProductName);
    }

尽管这会产生相同的结果,但我们不必这样做,因为我们有特殊的“F”属性。值得一提的是,数据直到第一次使用我们的特殊“F”属性时才从数据库中检索。这可以避免不必要地加载您可能永远不会使用的数据,从而减少服务器的负担。

存储过程

如果您使用 Microsoft SQL Server 或 MySql 作为数据库后端,LayerGen 完全支持使用存储过程进行 CRUD 操作,而不是默认的 Sql Text 操作。要告诉 LayerGen 使用存储过程而不是 Sql 文本,您只需在构造函数中传入值“true”。这是一个例子

    var mike = new BusinessLayer.FooEnterprises.Customer(1, true);

现在,您对 mike 对象执行的任何 CRUD 操作(插入、删除、更新或检索)都将使用存储过程而不是 Sql 文本完成。如果您想尝试此操作,必须首先打开 LayerGen 为您创建的 StoredProcedureScripts.Sql 文件,然后必须在 Sql Server Management Studio 或 MySql Workbench 中执行该 Sql 文件。此脚本创建支持基本 CRUD 操作所需的必要过程。当然,您可以自由创建自己的存储过程,并使用 GetByStoredProcedure() 方法调用这些过程,如上所述。另请注意,复数集合类也具有相同的重载以启用存储过程。

    var customers = new BusinessLayer.FooEnterprises.Customers(true);
    customers.GetAll();

部分加载

有时可能需要加载一行,但只加载部分字段而不是所有字段。当您在行中存储图像或二进制数据时,尤其如此。因为图像或二进制数据可能很大,所以您可能只想加载除这些大容量字段之外的所有字段。LayerGen 允许您执行此操作。再次删除 Main() 方法中的所有行,并输入这些行

    var mike = new BusinessLayer.FooEnterprises.Customer(1,
        new List<BusinessLayer.FooEnterprises.Customer.Fields>
        {
            BusinessLayer.FooEnterprises.Customer.Fields.FirstName
        });

此程序将加载 Mike 的记录,但仅加载他的名字。任何未加载的字段都将设置为 null。由于未加载的字段设置为 null,调用 Save() 方法将抛出类型为“BusinessLayer.FooEnterprises.ReadOnlyException”的异常。Save() 方法上有一个重载,允许您强制保存记录。调用 Save(true) 将强制保存该行。但是,这样做会用 null 值覆盖未加载的字段。请务必谨慎使用。您已收到警告!

并发选项

如果您在多个用户可能同时编辑相同行的应用程序中使用 LayerGen,您会希望启用 LayerGen 的并发检查。

要理解并发检查,请想象这个场景。假设 Bill 正在更新客户信息。当 Bill 输入新信息时,想象一下 Bill 的同事 Joe 也在更新同一个客户的信息。Joe 更新客户信息,然后将信息保存到数据库。现在,45 秒后,Bill 完成了对同一个客户的信息更新,并将他的更新保存到 Joe 刚刚更新的同一个客户。Joe 的信息现在将被 Bill 的更改覆盖。糟糕!

幸运的是,LayerGen 内置了并发保护,但默认情况下是关闭的。默认关闭的原因是并发检查在保存和加载行时会占用一些额外的时间和内存。并发检查只应在您实际编写一个允许多人同时更改相同行的应用程序时才启用。

启用并发检查非常容易。可怜的 Sara 离婚了,是时候把她的名字改回 Baker 了。再次删除 Main() 方法中的行,然后输入此程序,但暂时不要运行它

    var sara = new BusinessLayer.FooEnterprises.Customer(2,
        BusinessLayer.FooEnterprises.Customer.ConcurrencyOptions.Strict);
        
    sara.LastName = "Baker";
    sara.Save();

运行此程序之前,请在“sara.Save();”行设置一个断点。运行程序时,请确保在调试模式下运行,以便 Visual Studio 在保存该行之前在断点处停止。如果您在 Visual Studio 中检查“sara”对象,您会注意到当 Sara 的记录加载时,她的姓氏被设置为 Jones。现在切换到 Sql Server Management Studio 并编辑 Sara 的记录,将其姓氏或名字更改为其他内容。无论是什么都无所谓。现在切换回 Visual Studio 并按 恢复程序。请注意抛出了类型为 BusinessLayer.FooEnterprises.OutOfSyncException 的异常。之所以抛出此异常,是因为该记录中的数据与最初加载时不同。在您的应用程序中,您总是希望捕获该异常,以便您可以提醒用户或其他操作。复数集合类也具有此重载构造函数以启用并发检查。

序列化/反序列化

LayerGen 中的数据可以序列化为各种格式。假设我们想将 Sara 的行序列化为 JSON。再次删除 Main() 方法中的行,并输入此程序

    var sara = new BusinessLayer.FooEnterprises.Customer(2);
    string saraJson = sara.ToString(BusinessLayer.FooEnterprises.SerializationFormats.Json);
    
    Console.WriteLine(saraJson);

运行此程序,您将看到 Sara 的记录以 JSON 格式打印到屏幕上。如果需要,您可以将程序更改为以下内容

    var sara = new BusinessLayer.FooEnterprises.Customer(2);
    string saraXml = sara.ToString(BusinessLayer.FooEnterprises.SerializationFormats.Xml);
    
    Console.WriteLine(saraXml);

运行此程序将以 Xml 格式而不是 JSON 格式将 Sara 的记录输出到屏幕上。

您可能会注意到一个额外的字段,名为“SerializationIsUpdate”。此字段由 LayerGen 内部用于反序列化,可以忽略。但如果您计划将数据反序列化回去,请务必包含它。

除了序列化单行之外,LayerGen 还可以序列化多行。假设我们想将客户表中所有客户序列化为 JSON

    var customers = new BusinessLayer.FooEnterprises.Customers();
    customers.GetAll();

    string customersJson = customers.ToString(BusinessLayer.FooEnterprises.SerializationFormats.Json);

    Console.WriteLine(customersJson);

运行此程序,您将看到我们所有的客户以 JSON 格式打印到屏幕上。如果需要,您可以更改代码以输出 Xml 格式而不是 JSON,就像我们对上面的程序所做的那样。

从 JSON、Xml 或 BSON 反序列化也同样简单。试试这个程序

    var customers = new BusinessLayer.FooEnterprises.Customers();
    customers.GetAll();

    string customersJson = customers.ToString(BusinessLayer.FooEnterprises.SerializationFormats.Json);

    BusinessLayer.FooEnterprises.Customers reconstructedCustomers =
        BusinessLayer.FooEnterprises.Customers.FromJson(customersJson);

    foreach (BusinessLayer.FooEnterprises.Customer customer in reconstructedCustomers)
    {
        Console.WriteLine("{0} {1}", customer.FirstName, customer.LastName);
    }

此程序将检索所有客户,将其转换为 JSON,然后从 JSON 字符串中重建原始对象。最后,它将遍历每个客户并将其姓名打印到屏幕上。同样,您不仅可以从 JSON 反序列化,还可以从 BSON 或 Xml 反序列化。请随意尝试!

动态数据检索

LayerGen 最酷的功能之一是能够动态检索数据并将其存储到自定义类中。这通过一个示例将变得更加清晰。假设我们想在销售程序中计算利润率。这是我们想执行的 SQL

    SELECT  SUM([SaleCost]) AS TotalSales ,
            SUM([WholesaleCost]) AS TotalWholesaleCost ,
            1 - SUM([WholesaleCost]) / SUM([SaleCost]) AS ProfitMargin
    FROM    [CustomerProduct]
            INNER JOIN [Product] ON [Product].[ProductId] = [CustomerProduct].[ProductId];

问题是,我们如何获得该 SQL 语句的结果?有几种方法可以解决这个问题。选项 #1 是我们可以创建一个视图并将该 SQL 语句放入视图中,然后再次针对该视图运行 LayerGen。视图很好,但如果我们在一个地方只使用此语句,似乎会增加很多额外的开销。选项 #2 是我们可以只从 CustomerProducts 加载所有行并调用 GetAll() 方法,然后在应用程序中而不是在 SQL Server 端计算它。此选项可能适用于少量行,但假设您有数百万行。那将是大量数据传输,最重要的是,想象一下如果 5 个人同时执行此操作。不行!选项 #3 是做一些 hacky 的事情,比如将 TotalSales 转换为 VarChar 并说“As FirstName”,然后只在 Customer 表上调用 GetBySqlStatement(),并将结果存储在 FirstName 和 LastName 中。出于多种原因,这是一个非常糟糕的主意。永远不要这样做,否则我保证您的同事会想用锤子敲您的头。

选项 #4 是使用 LayerGen 的动态数据检索。动态数据检索需要 .NET 4.0 及以上版本。我将向您展示此功能如何工作的示例。首先,我们需要添加一个名为 Sales 的新类。因此,右键单击“Foo”并添加一个新类。该类应如下所示

    public class Sales
    {
        public decimal TotalSales { get; set; }
        public decimal TotalWholesaleCost { get; set; }
        public decimal ProfitMargin { get; set; }
    }

接下来,回到 Main() 方法,删除该方法中的所有行,然后输入此代码

    string sql = "SELECT  SUM([SaleCost]) AS TotalSales ,";
    sql = sql + "         SUM([WholesaleCost]) AS TotalWholesaleCost ,";
    sql = sql + "         1 - SUM([WholesaleCost]) / SUM([SaleCost]) AS ProfitMargin";
    sql = sql + "         FROM    [CustomerProduct]";
    sql = sql + "         INNER JOIN [Product] ON [Product].[ProductId] = [CustomerProduct].[ProductId];";
    
    List<Sales> sales = BusinessLayer.FooEnterprises.LoadData.FromSqlStatement<Sales>(sql);
    
    if (sales.Count == 0)
    {
        Console.WriteLine("No Data!");
    }
    else
    {
        Console.WriteLine("Total Sales   ==> {0}", sales[0].TotalSales.ToString("C"));
        Console.WriteLine("Total Costs   ==> {0}", sales[0].TotalWholesaleCost.ToString("C"));
        Console.WriteLine("Profit Margin ==> {0}", sales[0].ProfitMargin.ToString("p2"));
    }

简而言之,LayerGen 所做的是它尝试将 Sales 类中的属性与 SQL 语句返回的字段名称进行匹配。然后它将返回的所有行放入一个列表中。由于我们的 SQL 查询是聚合的,因此只返回一行。LoadData 还有一个“FromStoredProcedure”方法,其工作方式相同,只是数据来自存储过程而不是 Sql 文本。除了 LoadData,还有 ExecuteNonQuery,用于执行不返回行的存储过程或 Sql 语句(例如插入或更新)。

命令行用法

LayerGen 可以从命令行使用。这对于将 LayerGen 集成到您的自动化构建脚本中特别有用。以下是一些示例

Microsoft SQL Server:

     LayerGen35 "include-comments=True" "pluralization-template={ObjectName}s" "enable-dynamic-data-retrieval=True" "automatically-right-trim-data=False" "allow-serialization=True" "language=CSharp" "output=C:\LayerGen\Output" "sql-plugin=SqlServer" "sql-server-server-name=mysqlserver" "sql-server-port=1433" "sql-server-username=sa" "sql-server-default-schema=dbo" "sql-server-password=password" "sql-server-trusted-connection=False" "sql-server-database-name="MyDatabase" "data-namespace-name=DataLayer" "business-namespace-name=BusinessLayer"

MySql:

     LayerGen35 "include-comments=True" "pluralization-template={ObjectName}s" "enable-dynamic-data-retrieval=True" "automatically-right-trim-data=False" "allow-serialization=True" "language=VbNet" "output=C:\LayerGen\Output" "sql-plugin=MySql" "mysql-server-server-name=mysql" "mysql-server-port=3306" "mysql-server-username=alan" "mysql-server-password=dragonslayer" "mysql-server-database-name="MyDatabase" "data-namespace-name=DataLayer" "business-namespace-name=BusinessLayer" 

Sqlite:

     LayerGen35 "include-comments=True" "pluralization-template={ObjectName}s" "enable-dynamic-data-retrieval=True" "automatically-right-trim-data=False" "allow-serialization=True" "language=CSharp" "output=C:\LayerGen\Output" "sql-plugin=Sqlite" "sqlite-filename=C:\My Databases\Test.sqlite" "data-namespace-name=DataLayer.LayerGenTestDb" "business-namespace-name=BusinessLayer.LayerGenTestDb" 

MS Access:

     LayerGen35 "include-comments=True" "pluralization-template={ObjectName}s" "enable-dynamic-data-retrieval=True" "automatically-right-trim-data=False" "allow-serialization=True" "language=CSharp" "output=C:\LayerGen\Output" "sql-plugin=MsAccess" "msaccess-filename=C:\My Databases\Test.accdb" "data-namespace-name=DataLayer.LayerGenTestDb" "business-namespace-name=BusinessLayer.LayerGenTestDb" 

GUI 中可用的每个选项都应该可以从命令行使用!

摘要

我想我已经介绍了 LayerGen 提供的所有功能。LayerGen 的教程到此结束。我感谢所有的评论和错误报告。

LayerGen 已知错误和限制

SQLite 和 Microsoft Access 不支持存储过程。

在 SQLite 中,如果您创建了一个视图并且该视图使用聚合函数,那么您必须在 SELECT 语句之前使用“TYPES”关键字。这是 SQLite 的 ADO.Net 连接器中的一个错误,您可以在此处找到有关此错误的详细信息。

所有表都必须有主键。如果没有主键,LayerGen 将默默地跳过该表。如果您针对某个表运行 LayerGen 却看不到任何输出,那么这很可能是原因。

LayerGen 不支持多个主键(即复合主键)。

LayerGen 不支持 Microsoft Access 中的多值字段。

LayerGen 即将推出

支持 Oracle 数据库(我得先学习 Oracle!)

支持 PostgreSQL

历史

2016 年 5 月 18 日 - LayerGen 3.5.11.601 发布!修复并添加了许多好东西

  • 所有对象构造函数现在都支持传入连接字符串。如果您不传入连接字符串,LayerGen 将像往常一样运行,从 Universal 文件中获取它。
  • LayerGen 现在可以从命令行调用。这将使其易于在自动化构建中实现
  • LayerGen 创建的代码现在可以在 .NET 2.0 中正常运行,没有任何问题(除非您启用了动态数据检索,这需要 .NET 4.0 或更高版本)
  • 修复了许多 MS Access 错误
  • 海量的其他错误已被消除和修复!

2016 年 2 月 23 日 - LayerGen 3.5.1.559 发布!此版本修复了 VB.Net 代码生成中 GetBySqlStatement() 方法中的错误。之前它未能正确替换参数。

2016 年 2 月 17 日 - LayerGen 3.5.1.557 发布!此版本修复了几个错误,并增加了对 Microsoft Access 数据库的支持。以下错误已修复

  • 增加了对 Microsoft Access 数据库的支持(请注意,这仍处于实验阶段,您可能会遇到错误)。
  • 上次使用的配置文件现在将正确选择上次使用的数据库引擎。
  • 上次使用的配置文件现在将正确保存数据库引擎上次使用的端口
  • 修复了复数化模板的工具提示文本
  • MySql 和 SQLite 模块现在从 NuGet 拉取,而不是拥有自己的自定义项目
  • 大量的代码重构!

2016 年 1 月 15 日 - LayerGen 3.5.1.552 发布!此版本为 LayerGen 3.5 增加了配置文件支持。您现在可以通过填写信息并单击“保存配置文件”按钮来创建配置文件。您还可以通过单击“删除配置文件”按钮来删除配置文件。

2015 年 11 月 12 日 - LayerGen 3.5.1.544 发布!此版本修复了与 GUID 数据类型相关的错误。如果 GUID 用作主键,则使用 LayerGen 生成的代码将不正确地生成代码。

2015 年 11 月 5 日 - LayerGen 3.5.1.534 发布!此版本仅修复了 MySQL 代码生成中的一个错误,该错误错误地为 **tinyint(1)** 数据类型生成了代码。

2015 年 9 月 28 日 - LayerGen 3.5.1.482 发布!LayerGen 现在有一个可选的密码字段,用于为 SQLite 数据库提供密码。还修复了集合序列化代码中的一个错误,该错误阻止了它正确恢复 IsUpdate 和 IsDirty 标志,导致反序列化对象总是插入新行而不是更新行。

2015 年 9 月 16 日 - LayerGen 3.5.1.375 发布!LayerGen 现在支持 Json、Bson 和 Xml 格式的序列化和反序列化。VB.Net 代码生成现在添加了“Option Strict On”和“Option Explicit On”,在大多数情况下还添加了“Option Infer Off”,以使 VB.Net 代码更具类型安全性。各种 bug 修复。

2015 年 6 月 20 日 - LayerGen 3.5.13 发布!修复了为 Sql Server 指定自定义端口无法正常工作的 bug。

2015 年 6 月 17 日 - LayerGen 3.5.12 发布!修复了许多 bug,尤其是在 VB.Net 代码生成方面。修复了主键为文本类型时 Delete() 方法无法正常工作的 bug。

2015 年 6 月 14 日 - LayerGen 3.5.11 发布!

LayerGen 3.5 - CodeProject - 代码之家
© . All rights reserved.