连接和事务

Serenity 使用简单的 ADO.NET 数据访问对象,像 SqlConnection、DbCommand 等。

它提供了一些基本的助手(helpers)来创建连接、添加参数、执行查询等。

SqlConnections 类

[命名空间: Serenity.Data, 程序集: Serenity.Data]

该类包含创建连接的静态函数,并在数据库中以不可见的方式控制它。

SqlConnections.NewByKey 方法

public static IDbConnection NewByKey(string connectionKey)

该方法用于获取新的 IDbConnection 连接字符串,连接字符串定义在应用程序配置文件(app.config 或 web.config)。

using (var connection = SqlConnections.NewByKey("Default")) 
{
    // ...
}

尽量在 using 块中使用数据库连接。

它读取 web.config 中 key 为 "Default" 的连接字符串,并使用同样在连接字符串中指定的 ProviderName 信息创建一个新的连接字符串。例如,如果 ProviderName 是 "System.Data.SqlClient",则创建一个新的 SqlConnection 对象。

通常不需要显式打开连接,因为在需要它们的时候将自动打开连接(只要你使用 Serenity 扩展)。

SqlConnections.NewFor< TClass > 方法

如果你不想记忆连接字符串的键,而是想重用行(row)的信息(表单的 ConnectionKey 特性),你可能会更喜欢该变体。

查看 Row 类的顶部,你可能会发现由 Sergen 生成的 ConnectionKey 特性:

[ConnectionKey("Northwind")]
public sealed class CustomerRow : Row, IIdRow, INameRow
{
}

当要查询客户时,为了不使用硬编码 "Northwind",你可以重用来自 CustomerRow 的信息:

using (var connection = SqlConnections.NewFor<CustomerRow>()) 
{
    return connection.List<CustomerRow>();
}

此方法等效于 SqlConnections.NewByKey("Northwind")

我们没有在这里打开连接,因为 List 扩展方法会自动打开它。

用此方法的类不一定非得是 Row,任何类包含 ConnectionKey 特性都可以工作,即管大部分情况会是 row 类。

SqlConnections.New 方法

public static IDbConnection New(string connectionString, string providerName)

有时,你可能想创建配置文件中不存在的连接。

using (var connection = SqlConnections.New(
    "Data Source=(localdb)\v11.0; Initial Catalog=Northwind; 
     Integrated Security=true", "System.Data.SqlClient")) 
{
    // ...
}

在这里我们需要指定连接字符串和提供者的名称(如 "System.Data.SqlClient")。

你可能会问自己 “为什么使用这种方法而不是简单地使用 new SqlClient() ?”,请参阅下一关于这些优势的主题。

WrappedConnection

到目前为止,我们看到的所有方法都返回 IDbConnection 对象。你可能以为返回的是 SqlConnection、FirebirdConnection 等,但这是不完全正确的。

你所接收到的 IDbConnection 对象是 Serenity 特定的 WrappedConnection 对象,实际上包含了基本的 SqlConnection 或 FirebirdConnection 等。

这样做有助于 Serenity 提供一些功能,如自动打开连接、方言支持、默认事务、单元工作模式、可测试的重载连接等。

你可以不需要注意这些细节,而直接使用返回 IDbConnection 实例工作,它们会像基础连接那样工作,但你应优先使用 SqlConnections 方法来创建连接,否则可能会丢失一些列出的功能。

UnitOfWork 和 IUnitOfWork

UnitOfWork 是包含事务引用的简单对象。它有两个我们可以附加的额外事件:

比方说我们正在创建任务,当这些任务成功保存到数据库时,应该发送几封电子邮件。

如果我们很急并在事务提交之前发送电子邮件,可能会在事务失败的情况下为不存在的任务发送电子邮件。所以我们应该只在事务被提交成功时发送电子邮件,例如,在 OnCommit 事件发。

你可能会说先提交事务然后再发送电子邮件,但是,如果我们调用的创建任务服务只是更大操作中的一个步骤,所以我们不控制事务而在所有步骤成功后提交。

另一个场景是关于上传文件。这次我们要更新一些文件(File)实体,并且上传的新文件将替换旧文件。如果我们同样很急并且在事务执行完成之前(而最后事务执行失败了)删除旧的文件,我们最终得到实际不存在磁盘中的旧文件的文件实体。所以,我们其实应该在 OnCommit 事件中删除文件并替换新的文件,且在 OnRollback 事件中删除上传的文件。

void SomeBatchOperation() 
{
    using (var connection = SqlConnections.NewByKey("Default")) 
    using (var uow = new UnitOfWork(connection))
    {
        // here we are in a transaction context
        // create several tasks in transaction
        CreateATask(new TaskRow { ... });
        CreateATask(new TaskRow { ... });
        //...

        // commit the transaction
        // if any exception occurs here or at prior
        // lines transaction will rollback
        // and no e-mails will be sent
        uow.Commit();
    }
}

void CreateATask(IUnitOfWork uow, TaskRow task)
{
    // insert task using connection wrapped inside IUnitOfWork
    // this will automatically run in transaction context
    uow.Connection.Insert(task);

    uow.OnCommit += () => {
       // send e-mail for this task now, this method will only
       // be called if transaction commits successfully
    };

    uow.OnRollback += () => {
       // optional, do something else if it fails
    };       
}

results matching ""

    No results matching ""