如果数据库使用代码协定支持接口的实现,如何确保接口的实现具有连接字符串 [英] How to ensure that implementations of an interface have a connection string if they are backed by a database using code contracts

查看:75
本文介绍了如果数据库使用代码协定支持接口的实现,如何确保接口的实现具有连接字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,您有一个像这样的界面:

Imagine you've an interface like this:

public interface IPersonManager
{
     public void AddPerson(string name);
}

...以及一个我们称为DefaultPersonManager的实现.假设我们要确保IPersonManager的任何实现都不能将空或空字符串作为AddPerson(string name)的参数.为此,我们将实现合同类,如下所示:

...and an implementation which we'll going to call DefaultPersonManager. Let's say we want to be sure that any implementation of IPersonManager won't be able to give a null or empty string as argument of AddPerson(string name). For that matter, we're going to implement a contract class as follows:

[ContractClassFor(typeof(IPersonManager))]
public abstract class IPersonManagerContract : IPersonManager
{
   public void AddPerson(string name)
   {
       Contract.Requires(!string.IsNullOrEmpty(name), "Person's name cannot be a null or empty string");
   }
}

...,我们将使用ContractClassAttribute属性装饰IPersonManager界面:

...and we'll decorate our IPersonManager interface with the ContractClassAttribute attribute:

[ContractClass(typeof(IPersonManagerContractClass))]
public interface IPersonManager
{
     public void AddPerson(string name);
}

我们谈到了DefaultPersonManager.看起来像这样的课程:

We talked about a DefaultPersonManager. It would look like this class:

public class DefaultPersonManager
{
    private readonly List<string> _personNames = new List<string>();

    public void AddPerson(string name)
    {   
        // "name" argument will be verified by contract class!
        _personNames.Add(name);
    }
}

好的!

现在,我们需要实现一个新的IPersonManager实现,该实现与DefaultPersonManager的不同之处在于AddPerson应该将人员姓名持久保存到SQL数据库(即 SQL Server ),这只是一个示例...).我们将此实现称为DbBackedPersonManager.

Now we need to implement a new IPersonManager implementation which differs from the DefaultPersonManager in that AddPerson should persist person names to a SQL database (i.e. SQL Server, it's just an example...). We'll call this implementation DbBackedPersonManager.

由于DbBackedPersonManager需要连接字符串,因此我们可以在DbBackedPersonManagerAddPerson方法实现中添加一个前提条件:

Since DbBackedPersonManager requires a connection string, we could add a pre-condition in the AddPerson method implementation of DbBackedPersonManager:

public void AddPerson(string name)
{
     Contract.Requires(ConfigurationManager.ConnectionStrings["SomeConnectionStringId"] != null, "A connection string is required in your application/web configuration file");
}

错误:代码合同编译器会说 AddPerson实现了一个接口成员,因此我们不能添加Requires (阅读此问答集<一个href ="https://stackoverflow.com/questions/1385485/add-contract-to-interface-implementation">我发现Jon Skeet回答了这个问题,而且很久以前与这个话题有关. ).

Wrong: code contracts compiler will say that AddPerson implements an interface member thus we can't add a Requires (Read this Q&A I found that was answered by Jon Skeet and it's someway related to this topic a long time ago.).

推荐答案

可能的一种方法是像这样创建一个不相关的接口 (或您喜欢的任何标识符...) :

Possibly an approach would be creating an unrelated interface called IWithSqlDbBackend (or any identifier of your preference...) like this:

public interface IWithSqlDbBackend
{
     string ConnectionString { get; }
     string ConnectionStringId { get; set; }
}

稍后,我们需要创建一个合同类,如下所示:

Later, we'll need to create a contract class like this:

[ContractClassFor(typeof(IWithSqlDbBackend))]
public abstract class IWithSqlDbBackendContract : IWithSqlDbBackend
{
    public string ConnectionString
    {
        get 
        {  
            Contract.Requires(!string.IsNullOrEmpty(ConnectionStringId), "Connection string id cannot be null or empty");
            Contract.Requires(ConfigurationManager.ConnectionStrings[ConnectionStringId] != null, "Connection string must be configured");
            Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()), "A connection string cannot be null");

            return null;
        }
    }

    public string ConnectionStringId
    {
        get
        {
            Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()), "A connection string identifier cannot be null or empty");

            return null;
        }
    }
}

...我们还需要用所谓的ContractClassAttribute装饰IWithSqlDbBackend接口:

...also we'll need to decorate IWithSqlDbBackend interface with the so-called ContractClassAttribute:

[ContractClass(typeof(IWithSqlDbBackendContract))]
public interface IWithSqlDbBackend
{
    ...
}

...并在DbBackedPersonManager中实现接口.我将在此处添加实现签名:

...and implement the interface in DbBackedPersonManager. I'll add here the implementation signature:

public class DbBackedPersonManager : IPersonManager, IWithSqlDbBackend

最后,如果我们创建DbBackedPersonManager的实例,然后尝试调用AddPerson方法实现,但是先前未在应用程序/Web配置文件(即web.config或app.config ...)中配置连接字符串. ),我们的前提条件将确保我们的应用程序,服务或库不满足与存储在数据库后端中的人员一起工作的合同!

Finally, if we create an instance of DbBackedPersonManagerand we try to call AddPerson method implementation but no connection string was previously configured in the application/web configuration file (i.e. web.config or app.config...), our pre-conditions will ensure that our application, service or library isn't satisfying the contract to work with persons stored in a database backend!

这只是一个示例,说明许多其他域将能够确保由于代码合同合同类限制而无法使用常规多态性和代码合同进行验证的一系列条件.

这篇关于如果数据库使用代码协定支持接口的实现,如何确保接口的实现具有连接字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆