怎样才能容易更换DB
//怎样才能容易更换DB
//写一个可以对用户插入和获取的方法
namespace switchDB
{
class User
{
public int ID { get; set; }
public string Name { get; set; }
}
class SqlServerUser
{
public void Insert(User user)
{ }
public void GetUser(int id)
{ }
}
class Program
{
static void Main(string[] args)
{
User u = new User();
SqlServerUser ssu = new SqlServerUser();
ssu.Insert(u);
ssu.GetUser(1);
}
}
}
这里的问题是SqlServerUser被硬编码了,只能使用SqlServer,要是多态的话就好了,使用抽象工厂修改:
namespace switchDB
{
class User
{
public int ID { get; set; }
public string Name { get; set; }
}
interface IUser
{
void Insert(User user);
void GetUser(int id);
}
class SqlServerUser : IUser
{
public void Insert(User user)
{ }
public void GetUser(int id)
{ }
}
class AccessUser : IUser
{
public void Insert(User user)
{ }
public void GetUser(int id)
{ }
}
//定义IFactory接口
interface IFactory
{
IUser CreateUser();
}
//抽象工厂
class SqlServerFactory : IFactory
{
public IUser CreateUser()
{
return new SqlServerUser();
}
}
class AccessFactory : IFactory
{
public IUser CreateUser()
{
return new AccessUser();
}
}
class Program
{
static void Main(string[] args)
{
User u = new User();
SqlServerFactory f = new SqlServerFactory();
IUser it = f.CreateUser();
it.Insert(u);
it.GetUser(1);
}
}
}
这样以后增加表Department,就需要增加IDepartment,并实现SqlServerDepartment,和AccessDepartment,还要修改IFactory增加CreateDepartment接口方法,并且在SqlServerFactory和AccessFactory分别实现。其中增加三个是必须的,但是修改IFactory,SqlServerFactory和AccessFactory是多点多于的,也是不可容忍的。
使用简单工厂改进:
namespace switchDB
{
class User
{
public int ID { get; set; }
public string Name { get; set; }
}
interface IUser
{
void Insert(User user);
void GetUser(int id);
}
class SqlServerUser : IUser
{
public void Insert(User user)
{ }
public void GetUser(int id)
{ }
}
class AccessUser : IUser
{
public void Insert(User user)
{ }
public void GetUser(int id)
{ }
}
class DateAccess
{
private static readonly string db = "sqlserver";
public static IUser CreateUser()
{
IUser result = null;
switch(db)
{
case "sqlserver":
result = new SqlServerUser();
break;
case "access":
result = new AccessUser();
break;
}
return result;
}
}
class Program
{
static void Main(string[] args)
{
User u = new User();
IUser iu = DateAccess.CreateUser();
iu.Insert(u);
iu.GetUser(1);
}
}
}
使用简单工厂放弃IFactory,SqlServerFactory,AccessFactory这三个类,用DataAccess来改进,将来要是要用Oracle,就在DataAccess中增加一个Case,使用这个分支也是不完美的,要是我们可以根据字符串db的值,去某一个地方去找应该实例化的类,这样我们就可以不使用swith了,这种编程方式叫做 依懒注入,使用net反射就可以了:
格式:Assembly.Load("程序集名称").CreateInstance("类的全权名");
只要引入Using System.Reflection;就可以了。
使用反射和不使用发射比较:
//常规
IUser result = new SqlServerUser();
//反射
using System.Reflection;
反射中,可以灵活替换"程序集名称"和"类的全权名",灵活性和可以替换性达到了。
例如:
class DateAccess
{
private static readonly string AssemblyName = "factory";
private static readonly string db = "sqlserver";
public static IUser CreateUser()
{
string className = AssemblyName + "." + db + "user";
return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
}
}
修改之后我们就和switch说再见了,但是我们每次修改数据库,还是修改了db这个字符串的值,要不修改它就更好了,符合开发-封闭原则
使用反射+配置文件再作改进:
利用配置文件来解决更改DataAccess的问题
添加一个App.Config文件:
<Configuration>
<appSettings>
<add key="db" value="sqlserver">
</appSettings>
</Configuration>
代码:
using System.Configuration;
private static readonly string db = ConfigurationManager.AppSettings["db"];
现在我们应用反射,抽象工厂解决了数据库访问,可维护可扩展问题。
从这个角度,所有的用简单工厂的地方,都可以考虑修改为反射去除switch来完成。
下面试调用静态方法的反射运用:
public class T_SizeType
{
public static bool pro_T_SizeType_SelectAll(string p_ConnString)
{
return true;
}
}
class Program
{
static void Main(string[] args)
{
object[] arg = new object[1]{string.Empty};
typeof(T_SizeType).InvokeMember("pro_T_SizeType_SelectAll",
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, arg);
}
}