如果数据库使用代码协定支持接口的实现,如何确保接口的实现具有连接字符串 [英] How to ensure that implementations of an interface have a connection string if they are backed by a database using code contracts
问题描述
想象一下,您有一个像这样的界面:
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
需要连接字符串,因此我们可以在DbBackedPersonManager
的AddPerson
方法实现中添加一个前提条件:
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 DbBackedPersonManager
and 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屋!