dotnet2004

博客园 首页 新随笔 联系 订阅 管理
  1 Posts :: 1 Stories :: 11 Comments :: 0 Trackbacks

公告

最新评论

re: 设计 Stephen 2004-12-04 14:00  
谢谢大家的热心!特别感谢wayfarer ,写得清晰有条理,让我受益匪浅!:)
re: 设计 yyanghhong 2004-12-03 01:33  
我采用Adapter Pattern和IOC的Dependency Injection Pattern做过这种设计, 施行过程大概是这样的,

不一上来就做框架, 先用其中一个provider做几个通用的例子, 比如用SQLClient调用Stored proc, query, insert.. 然后用Refactorting里的Extract Interface方法, 先做一个DBAdapter interface, 再做一个类SQLClientAdapter把所有和 SQLClient相关的操作都提取出来, 做为DBAdapter和SQLClientAdapter的方法, 如果你的应用需要访问oracle, 再做一个OracleClientAdapter, 实现DBAdapter 的所有方法.


这样一来你的程序只需要用到DBAdapter interface, 然后用IOC的Dependency Injection Pattern方法创建 DBAdapter(可以用spring.net或pico.net), 这样你就可以通过修改配置文件来切换adapter了.

当然也可以用Factory Method Pattern的方法来配置Adapter, 不过始终需要修改code, IOC其实是对Factory Method Pattern的一种加强, 并且他还可以替换Singleton Pattern 和Prototype Pattern

re: 设计 烂瓶子 2004-12-01 00:11  

using System;

namespace XTEAM.XBLOG.Common
{
    
public enum DataBaseProvider
    
{
        SQLServer 
= 0,
        OLEDB 
= 1
    }

    
public class ConnectionString
    
{
        
private ConnectionString()
        
{
        }


        
private static string strConnectionString = null;

        
public static string getConnectionString()
        
{
            
if(strConnectionString == null)
            
{
                strConnectionString 
= "";//从web.config文件中获得连接字符串 获取Web.config参数的方法还没写,注意用异常
            }

            
return strConnectionString;
        }

    }


    
public class DataBaseProviderString
    
{
        
private DataBaseProviderString()
        
{
        }


        
private static string strDataBaseProviderString = null;

        
public static string getDataBaseProviderString()
        
{
            
if(strDataBaseProviderString == null)
            
{
                strDataBaseProviderString 
= "";//从web.config文件中获得数据源类型 获取Web.config参数的方法还没写,注意用异常
            }

            
return strDataBaseProviderString;
        }

    }


    
public sealed class DataBaseProviderFactory
    
{
        
private static DataBaseProvider dataBaseProvider;
        
static DataBaseProviderFactory()
        
{
            
//从web.config文件中获得连接数据源类型
            string strDataBaseProviderString = DataBaseProviderString.getDataBaseProviderString();
            
if(strDataBaseProviderString == "0" )
                dataBaseProvider 
= DataBaseProvider.SQLServer;
            
else if(strDataBaseProviderString == "1")
                dataBaseProvider 
= DataBaseProvider.OLEDB;
        }

        
public static DataBaseProvider getDataBaseProvider()
        
{
            
return dataBaseProvider;
        }

    }
 



    
internal sealed class DataFactory
    
{
        
private DataFactory(){}
        
internal static IDbConnection CreateEmptyConnection()
        
{
            IDbConnection conn 
= null;
            
if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.OLEDB)
            
{
                conn 
= new OleDbConnection();
            }

            
else if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.SQLServer)
            
{
                conn 
= new SqlConnection();
            }

            
return conn;
        }

        
//        internal static IDbConnection CreateConnection()
        
//        {
        
//            IDbConnection conn = DataFactory.CreateEmptyConnection();
        
//            conn.ConnectionString = ConnectionString.getConnectionString();
        
//            return conn;
        
//        }
        internal static IDbCommand CreateEmptyCommand()
        
{
            IDbCommand comm 
= null;
            
if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.OLEDB)
            
{
                comm 
= new OleDbCommand();
            }

            
else if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.SQLServer)
            
{
                comm 
= new SqlCommand();
            }

            
return comm;
        }

        
//        internal static IDCommand CreateCommand()
        
//        {
        
//            IDbCommand comm = DataFactory.CreateEmptyCommand();
        
//            comm.Connection = DataFactory.CreateConnection();
        
//            return comm;
        
//        }
        internal static IDataAdapter CreateDataAdapter()
        
{
            IDataAdapter adp 
= null;
            
if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.OLEDB)
            
{
                adp 
= new OleDbDataAdapter();
            }

            
else if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.SQLServer)
            
{
                adp 
= new SqlDataAdapter();
            }

            
return adp;
        }

        
internal static IDataParameter CreateParameter()
        
{
            IDataParameter para 
= null;
            
if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.OLEDB)
            
{
                para 
= new OleDbParameter();
            }

            
else if(DataBaseProviderFactory.getDataBaseProvider() == DataBaseProvider.SQLServer)
            
{
                para 
= new SqlParameter();
            }

            
return para;
        }

    }


    
public sealed class DataHelper
    
{
        
//
    }

}


前一阵为我和同事做的blog写的,不知道怎样,有什么需要改进的地方大家帮我看看
re: 设计 kwklover 2004-11-30 23:49  
精彩!
我感觉以上讨论是关于多数据库支持的做法,如果一个项目同时有多个数据库,有时候需要从sqlserver获取数据,有时候需要从oracel获取数据,这个要求,设计数据层的时候应该如何设计比较好呢 ?

我刚开始是想做两个privider :SqlServerProvider和OracleProvider,但个人感觉这样做,在数据操作层的时候,要区分这种操作是调用哪个Provider,感觉不怎么好

的好好思考一下上面朋友的言论了!
re: 设计 wildfish 2004-11-30 22:31  
我们现在的dataaccess层基本上可以适应多种数据库,目前可以支持db2 olddb,不过麻烦就在于sql语句要重新写。因为各种数据驱动会有一些语法差异。
re: 设计 小陆 2004-11-30 21:03  
我曾经遇到过一个这样的项目,一开始的时候就在SQL和Oracle之间晃来晃去,最后选择了PostSql(免费的)。还有一次是编码已经开始了,才告诉我,还有几个报表是在Oracle数据库里做的。
http://www.cnblogs.com/dotnet2004/archive/2004/11/30/71066.html
文章的最后举的例子是我在实际工作中应用的情景,希望能代给大家一些值得讨论的东西。
设计不能解决合同签不下来的问题,也不能解决工程款结不下来的问题,只能在传来坏消息的时候减少一点绝望。从某种角度上说,好的设计可以化解一些需求上的风险,增加一些讨价还价的砝码。
re: 设计 有时狂欢 2004-11-30 20:14  
看了你的文章,说了一点看法,在这里http://www.cnblogs.com/anxing/archive/2004/11/30/71143.html
re: 设计 wayfarer 2004-11-30 19:36  
为了你能看得更方便一点,我还是直接把JGTM的评论贴出来吧。说实话,他写的这段代码很精彩,应该对你有帮助:

关于FACTORY METHODS的应用,在很多地方均有涉及(尤其是framework代码中),在类似于data provider的场合尤其多见。.NET中IDbConnection中的CreateCommmand()方法是我最常举的例子,它使得client代码的开发对具体类的依赖大幅度减少。

正好现在有时间,随手举个例子给你吧,我用重构的方法根据你文中提到的要求来推导,okay!先看这个原始代码:

public DataSet ClientSample()
{
 SqlCommand command = new SqlCommand(commandText);
 SqlDataAdapter adapter = new SqlDataAdapter(command);
 using (SqlConnection connection = new SqlConnection(connectionString))
{
  DataSet result = new DataSet();
  connection.Open();
  adapter.Fill(result, "tableName");
  return result;
 }
}

注意看它依赖了多少个具体的数据源相关的类。为了让这段代码可以适应多种数据源,首先要把具体类换成抽象类或接口,如下:

public DataSet ClientSample2()
{
 IDbCommand command = new SqlCommand(commandText);
 IDataAdapter adapter = new SqlDataAdapter(command);
 using (IDbConnection connection = new SqlConnection(connectionString))
 {
  DataSet result = new DataSet();
  connection.Open();
  adapter.Fill(result, "tableName");
  return result;
 }
}

然后把构造具体类的逻辑封装成所谓provider:

public DataSet ClientSample2()
{
 IMyProvider provider = new MySqlProvider();
 IDbCommand command = provider.GetCommand(commandText);
 IDataAdapter adapter = provider.GetDataAdapter(command);
 using (IDbConnection connection =
provider.GetConnection(connectionString))
 {
  DataSet result = new DataSet();
  connection.Open();
  adapter.Fill(result, "tableName");
  return result;
 }
}

现在你就只依赖一个具体类了,再把这个依赖性通过引入字符串来消除:

const providerTypeName = "MyCompany.MyProject.MySqlProvider, SqlProvider";

public DataSet ClientSample2()
{
 IMyProvider provider =
(IMyProvider)Activator.CreateInstance(providerTypeName);
 IDbCommand command = provider.GetCommand(commandText);
 IDataAdapter adapter = provider.GetDataAdapter(command);
 using (IDbConnection connection =
provider.GetConnection(connectionString))
 {
  DataSet result = new DataSet();
  connection.Open();
  adapter.Fill(result, "tableName");
  return result;
 }
}

这个常量是个可配置的信息,如果放到配置文件中,这段代码就已经做到数据源无关了。这里面使用FACTORY METHODS模式利用GetCommand、GetDataAdpator和GetConnection三个方法把对IDbCommand、IDataAdapter和IDbConnection的构造延迟到子类中。如果把GetCommand合并入GetDataAdapter,就得到比较理想的client逻辑:

public DataSet ClientSample2()
{
 IMyProvider provider =
(IMyProvider)Activator.CreateInstance(providerTypeName);
 using (IDbConnection connection =
provider.GetConnection(connectionString))
 {
  IDbCommand command = connection.CreateCommand();
  IDataAdapter adapter = provider.GetDataAdapter(command);
  DataSet result = new DataSet();
  connection.Open();
  adapter.Fill(result, "tableName");
  return result;
 }
}

这里你可以发现,我们利用了IDbConnection中的CreateCommand()这个FACTORY METHOD进一步减少了provider的责任,使得provider内部也只需要依赖于两个和数据源相关的具体类:XxxConnection和XxxDataAdaptor:

public class MySqlProvider: IMyProvider
{
 public IDbConnection GetConnection(string connectionString)
 {
  return new SqlConnection(connectionString);
 }
 public IDataAdapter GetDataAdapter(IDbCommand command)
 {
  command.CommandType = CommandType.StoredProc;
  command.CommandText = "myStoredProc";
  return new SqlDataAdapter(command);
 }
}

这里,IMyProvider应该是由你的框架代码来定义,所有的provider都要reference你的框架并依赖这个接口,这也是所谓IOC的具体应用——在没有任何具体的provider类之
前client可以独立的编译,它并不依赖、引用任何具体的可扩展的provider类库。

上面的例子希望能够对你理解FACTORY METHODS(其实是TEMPLATE METHODS的一种具体情形)有所帮助吧!另外,我写的例子千万不要拷贝粘贴到开发环境中,否则引起蓝屏
甚至硬件故障本人一概不负责任。嘿嘿!:)

JGTM'2004 [MVP]
JGTM Studio .NET


btw:JGTM,我这样贴你的内容,不会怪我侵权吧,嘿嘿:)
re: 设计 dudu 2004-11-30 18:57  
这样的文章当然可以放在首页。希望大家发表更多的有关软件设计与编辑思想方面的文章。我们专注于.NET, 但我们更要超越.NET, 去领悟软件设计的真谛!
re: 设计 青水泛舟 2004-11-30 18:49  
看看我在的组所做的设计,你也许会感到欣慰一点。

public class myObject  //基类,所有的东西(除了控件)都要继承这里。
{
  public string ID;
  public string  Name;
  public object userobj1 =  new object(); // 附加的,做其它用。
  public object userobj2 =  new object(); 

  ////.....
}

public class DataBase  //负责与数据库打交道。
{
  public OracleConnection connection;  //
  protected OracleDataReader Reader;  //  protected, 供子类调用。
  public int ExecuteNonQuery(string sql); //执行sql语句。结果是在上面的Reader中。
   //其他
}
 
public class SQL //负责获得sql语句。sql语句是放在xml文件中的。
{     
 public string GetSql(string sqlNmae)
     {
          return "";  //....
     }
  //其他
}

 

然后开始写业务层。上面的算是数据库访问层吧。
public class UserInfo : myObject
{
    //public string UserID;  因为基类有ID,可以不定义了。
   //public  string UserName; 和ID一样,不定义。
     public int Age;

     //如果使用到其他对象,比如所在科室,一般这样定义
    myObject Dept = new myObject();   //可以用Dept里的ID........
   

}
下面是UserInfo的管理类:
public class UserManager : DataBase // 继承DataBase !!!!!
{
    public UserInfo SelectById(object id) {}
    public ArrayList  SelectAll()   //返回的是集合。!
   {
       ArrayList list = new ArrayList();  
       ......
       while( Reader.Read() )
      {
           UserInfo info = new UserInfo();
          info.ID = Reader[0].ToString();
          info.Name = Reader[1].ToString();
          info.Age = (int)Reader[2];  
          info.Dept.ID = Reader[3].ToString();  
         //其他.....
         list.Add(info);
       }
      return list;
   } 
    public int Update(UserInfo info) {}
    public int Insert(UserInfo info) {}
   //一般的功能就是Select , Update, Insert. 。
}
 
业务层就这样子了。然后就写表现层。

把ArrayList中的UserInfo的值付到DataTable等中,操作完后再从DataTable中生成UserInfo, 并调用UserManager进行添加、删除等工作。
^ ^包括数据验证一系列乱78糟的操作。

到这里差不多完成了一个从后台到前台的过程。呵呵。

还有:这个系统大概有6、700张表。晕......

羡慕别人那。
re: 设计 wayfarer 2004-11-30 17:34  
感觉这种设计灵活性并不好。多用接口而不是具体的类,灵活性更高。楼主看看这篇文章,尤其是文章后JGTM'2004 [MVP]的评论。

http://blog.joycode.com/musicland/articles/16642.aspx

同样是调用两种数据库,这种方式应该会好些。