我有几个不同的自定义备份程序,我想将它们合并为一个。 这样做,我正在研究我的OO设计,通过抽象我的服务器和数据库信息来帮助保持驱动程序简单易用。
我正在使用两个不同的主机(Discount ASP.net和Rackspace)。他们都以略微不同的方式进行备份。
我尝试过几种不同的方法,但这是我认为最有意义的路线。
我有我的基类:
public abstract class DBServer
{
public List<Database> Databases { get; set; }
public abstract bool MakeBackupCall(Database d);
}
然后是两个派生类:
public class DASPServer : DBServer
{
public string APIKey { get; set; }
public override bool MakeBackupCall(Database d)
{
// do some stuff
return true;
}
}
public class RackspaceServer : DBServer
{
public override bool MakeBackupCall(Database d)
{
// do some different stuff
return true;
}
}
问题来自我的相关对象Database
。
由于每个主机的备份过程不同,因此我需要不同的数据库信息。例如,对于Discount ASP.net,我需要一个版本(2000,2005,2008)用于数据库,因此我知道要调用哪些Web服务。对于Rackspace,我需要外部服务器名称,数据库用户名和密码来创建连接字符串。因此,我尝试使用以下层次结构创建数据库对象。
public abstract class Database
{
public string Name { get; set; }
}
public class DASPDatabase : Database
{
public string Version { get; set; }
}
public class RackspaceDatabase : Database
{
public string ServerName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
protected string ConnectionString
{
get { return string.Format("Data Source={0};Network Library=;Connection Timeout=30;Packet Size=4096;Integrated Security=no;Encrypt=no;Initial Catalog={1};User ID={2};Password={3}", ServerName, Name, UserName, Password); }
}
}
我想要做的是确保我的DASPServer实例始终获取DASPDatabases的实例,并且与Rackspace相同。或者,如果我朝错误的方向前进,我想要头脑清醒。
非常感谢提前
答案 0 :(得分:3)
使您的DBServer类具有通用性,然后指定实现类的实际数据库类型。
public abstract class DBServer<TDatabase> where TDatabase : Database
{
public List<TDatabase> Databases { get; set; }
public abstract bool MakeBackupCall( TDatabase d );
}
public class DASPServer : DBServer<DASPDatabase>
{
public string APIKey { get; set; }
public override bool MakeBackupCall( DASPDatabase d )
{
// do some stuff
return true;
}
}
public class RackspaceServer : DBServer<RackspaceDatabase>
{
public override bool MakeBackupCall( RackspaceDatabase d )
{
// do some different stuff
return true;
}
}
答案 1 :(得分:1)
您可以隐藏MakeBackupCall
的基本实现:
public new bool MakeBackupCall(RackspaceDatabase d)
{
// Do something
}
这样,如果在RackspaceServer
变量上调用该方法,编译器将强制该参数的类型为BackspaceDatabase
。但是当然如果在DBServer变量上调用它将无法工作,因为将调用基本实现...
可接受的折衷方案是在采用Database
参数的受保护虚拟方法中进行实际实现:
public abstract class DBServer
{
public List<Database> Databases { get; set; }
// Non-virtual; the actual implementation is not done in that method anyway
public bool MakeBackupCall(Database d)
{
return MakeBackupCallCore(d);
}
// Actual implementation goes there
protected abstract MakeBackupCallCore(Database d);
}
public class RackspaceServer : DBServer
{
// Hide the base method
public new bool MakeBackupCall(BackspaceDatabase d)
{
return MakeBackupCallCore(d);
}
// Do the actual implementation here, and ensure it is really a BackspaceDatabase
protected virtual bool MakeBackupCallCore(Database d)
{
BackspaceDatabase bd = d as BackspaceDatabase;
if (bd == null)
throw new ArgumentException("d must be a BackspaceDatabase");
// do some different stuff with bd
return true;
}
}
这样,您可以在显式使用BackspaceServer
时在编译时强制执行该类型,并在使用DbServer
时仍然使用派生实现,而不知道其实际类型。
在ADO.NET类中使用了类似的方法:例如,SqlConnection.CreateCommand()
返回SqlCommand
,即使基类DbConnection.CreateCommand()
返回DbCommand
。基本方法是隐藏的,实际的实现是受保护的抽象(CreateDbCommand
)
答案 2 :(得分:1)
我倾向于完全抽象出数据访问层 - 将它放在一个接口(基本上是Dependency Inversion)之后,然后开发适合你正在处理的后端的数据访问实现。您最终得到的结果是,您可以简单地将新的具体数据访问实现放入bin目录(和适当的配置),然后您就可以了 - 您不必重新编译和重新部署整个系统。
根据您正在处理的依赖项的具体情况,您甚至可以将两个实现组合到同一个程序集中。在这种情况下,它更像是Strategy Pattern。
在这两种情况下,您都可以通过配置选择具体的数据访问实现。
答案 3 :(得分:0)
只需在运行时检查类型,如果传递的数据库类型错误,则抛出ArgumentException
。
答案 4 :(得分:0)
在编译时,我不知道有任何方法确保MakeBackupCall()
使用正确的Database
子类。但是,在运行时,您可以检查传递的参数is
是否为正确的子类型。