IDbDal.dll
public interface ISqlDal
{
bool Test();
int OpenByConnection(string cnStr);
int ExecuteNonQuery(string sql);
DataTable GetDataTable();
byte[] GetDataTable_CompressdBytes(string sql);
//more: DbTransaction, DataReader, GetString, ...
}
DbDal.dll
public class SqlDal : MarshalByRefObject, ISqlDal
{
public bool Test()
{
return true;
}

public int OpenByConnection(string cnStr) { ... }
public int ExecuteNonQuery(string sql) { ... }
public DataTable GetDataTable() { ... }

public byte[] GetDataTable_CompressdBytes(string sql)
{
return myCompress.CompressDataTable(GetDataTable(sql));
}
}
DbFty.dll
//为了支持Remoting,增加了[Serializable],并把所有静态改为了需实例化,且继承自[MarshalByRefObject]
[Serializable]
public sealed class DbDataAccess : MarshalByRefObject
{
public IDbDal CreateSql();
}
DbBll.dll
public class SqlBll: ISqlDal
{
private ISqlDal dal;
public ftRemoteDataAccessBll rda = new ftRemoteDataAccessBll();

/// <summary>
/// 访问远程对象之前需要先检查远程对象是否可用
/// </summary>
private void CheckCreateDalIf()
{
if (dal != null)
{
try
{
dal.Test();
}
catch
{
//Test failed, set to null and then recreate it.
dal = null;
}
} if (dal == null)
{
DbDataAccess da = null;
IDbDal ii = null;
da = rda.RDA(); //
ii = da.CreateSql();
dal = ii;
} //return true;
} public bool Test()
{
CheckCreateDalIf();
return dal.Test();
}

public int OpenByConnection(string cnStr);
{
this._connectionString = cnStr;
CheckCreateDalIf();
dal.OpenByConnectionString(this._connectionString);
}

public DataTable GetDataTable(string sql, bool bWithCompress)
{
CheckCreateDalIf(); DataTable dt = null;
try
{
if (!bWithCompress)
{
dt = dal.GetDataTable(sql);
}
else
{
byte[] data = dal.GetDataTable_CompressdBytes(sql);
dt = myCompress.DeCompressDataTable(data);
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return dt;
}

public int ExecuteNonQuery(string sql)
{
CheckCreateDalIf();
ActiveIf();
return dal.ExecuteNonQuery(sql);
}

public DataTable GetDataTable()
{
return GetDataTable(sql, true);
}

public byte[] GetDataTable_CompressdBytes(string sql)
{
return dal.GetDataTable_CompressdBytes(sql);
}
}
Client.Bll(静态类部分)
public class pub
{
public static IDbDal dal
{
get
{
IDbDal ret = new SqlBll();
//ret.OpenByBook("TestDB");
string sCn = myCommon.GetSetting("ConnectionString");
if (sCn.StartsWith("tfswfs=")) { sCn = mymmon.SimpleCrypt(sCn, false); } //"server="
ret.OpenByConnectionString(sCn);
return ret;
}
}
}
Client.Bll(动态类部分)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data; //
using FT.Common; //
using FT.ISqlDal; //
using Client.IBll; //namespace Client.Bll
{
    public class SoOrdBll : SoOrdHdrBll
    {
        public SoOrdDtlBll _dtl = null; public SoOrdDtlBll dtl { get { if (this._dtl == null) this._dtl = new SoOrdDtlBll(this.dal); return this._dtl; } }
        public SysBll _sys = null; public SysBll sys { get { if (this._sys == null) this._sys = new SysBll(this.dal); return this._sys; } }
        //
        public CstBll _cst = null; public CstBll cst { get { if (this._cst == null) this._cst = new CstBll(this.dal); return this._cst; } }
        public EmpBll _emp = null; public EmpBll emp { get { if (this._emp == null) this._emp = new EmpBll(this.dal); return this._emp; } }
        public CrrBll _crr = null; public CrrBll crr { get { if (this._crr == null) this._crr = new CrrBll(this.dal); return this._crr; } }
        public PmtBll _pmt = null; public PmtBll pmt { get { if (this._pmt == null) this._pmt = new PmtBll(this.dal); return this._pmt; } }
        public TdtBll _tdt = null; public TdtBll tdt { get { if (this._tdt == null) this._tdt = new TdtBll(this.dal); return this._tdt; } }
        public SpwBll _spw = null; public SpwBll spw { get { if (this._spw == null) this._spw = new SpwBll(this.dal); return this._spw; } }
        //
        public ItmBll _itm = null; public ItmBll itm { get { if (this._itm == null) this._itm = new ItmBll(this.dal); return this._itm; } }
        public UntBll _unt = null; public UntBll unt { get { if (this._unt == null) this._unt = new UntBll(this.dal); return this._unt; } }
    }    public class SoOrdHdrBll : AHdrBll, IDocHdrBll
    {
        public SoOrdHdrBll()
        {
            this.dal = pub.dal;
            Init();
        }        public SoOrdHdrBll(ftISqlDal dal)
        {
            this.dal = dal;
            Init();
        }        private void Init()
        {
            this.SQL_DEF = new SqlStr("a.* ", "SoOrd a ", "(a.ST>=0) and (1=0) ", "a.CD ");
            this.SQL_EXT = new SqlStr(
                "a.*, \ncst.DS as _CST, crr.DS as _CRR, sal.DS as _SAL ",
                "SoOrd a \nleft join CST cst on cst.CD=a.CST \nleft join CRR crr on crr.CD=a.CRR \nleft join EMP sal on sal.CD=a.SAL \nleft join PMT pmt on pmt.CD=a.pmt \nleft join TDT tdt on tdt.CD=a.TDT \nleft join SPW spw on spw.CD=a.SPW ",
                "(a.ST>=0) and (1=0) ",
                "a.CD ");
            this.SQL_ALL = new SqlStr(this.SQL_EXT.Select, this.SQL_EXT.From, "(1=0) ", this.SQL_EXT.OrderBy);
            this.SQL_LST = new SqlStr(
                "a.CD, a.DT, a.CST, a.SAL, a.IsPrn, a.IsApv, a.IsEnd, a.CrDt, a.CrUsr, \ncst.DS as _CST, crr.DS as _CRR, sal.DS as _SAL, cru.DS as _CrUsr, \nb.SEQ, b.ITM, b.QTY, b.UNT, b.UP, b.AMT, b.BAT, b.OvDt, b.RM, \nitm.DS as _ITM, unt.DS as _UNT ",
                "SoOrd a \nleft join SoOrd2 b on b.CD=a.CD and b.ST>0 \nleft join CST cst on cst.CD=a.CST \nleft join CRR crr on crr.CD=a.CRR \nleft join EMP sal on sal.CD=a.SAL \nleft join EMP cru on cru.CD=a.CrUsr \nleft join ITM itm on itm.CD=b.ITM \nleft join UNT unt on unt.CD=b.UNT ",
                "a.ST>0 and (1=0)",
                "a.CD, b.SEQ ");
            this.SQL_DELETE = new SqlStr("update SoOrd set ST=-abs(ST), CD='-'+CD where CD='{0}' ");
        }        public override DataRow InitHdrNewRow(DataRow row)
        {
            row["CD"] = "";
            row["CrDt"] = DateTime.Now;
            row["IsPrn"] = false;
            row["IsApv"] = false;
            row["IsEnd"] = false;
            row["ST"] = 1;
            return row;
        }
    }    public class SoOrdDtlBll : ADtlBll, IDocDtlBll
    {
        public SoOrdDtlBll()
        {
            this.dal = pub.dal;
            Init();
        }        public SoOrdDtlBll(ftISqlDal dal)
        {
            this.dal = dal;
            Init();
        }        private void Init()
        {
            this.SQL_DEF = new SqlStr("b.* ", "SoOrd2 b ", "(b.ST>=0) and (1=0) ", "b.CD, b.SEQ ");
            this.SQL_EXT = new SqlStr(
                "b.*, \n_ITM=itm.DS, unt.DS as _UNT ",
                "SoOrd2 b \nleft join ITM itm on itm.CD=b.ITM \nleft join UNT unt on unt.CD=b.UNT ",
                "(b.ST>=0) and (1=0) ",
                "b.CD, b.SEQ ");
            this.SQL_ALL = new SqlStr(this.SQL_EXT.Select, this.SQL_EXT.From, "(1=0) ", this.SQL_EXT.OrderBy);
            this.SQL_DELETE = new SqlStr("update SoOrd2 set ST=-abs(ST), CD='-'+CD where CD='{0}' and SEQ='{1}' ");
        }        public override DataRow InitHdrNewRow(DataRow row)
        {
            row["CD"] = "";
            row["SEQ"] = 0;
            row["ST"] = 1;
            row["CrDt"] = DateTime.Now;
            return row;
        }
    }}

解决方案 »

  1.   

    Server.exe (Remoting部分)namespace FT.SqlApplicationServer
    {
        public partial class Form1 : Form
        {
    public Form1()
            {
                InitializeComponent();            //可以把此段放到服务程序中,开机后无需登录即可加载
                RegistServerByInstance("");            //if (string.Empty == "aa")
                {
                    DataTable dtInstanse = ftCommon.strToDataTable(ftCommon.GetSetting("SqlInstances"), ';', ':');
                    for (int i = 0; i < dtInstanse.Rows.Count; i++)
                    {
                        string sInstanse = dtInstanse.Rows[i]["CD"].ToString();
                        RegistServerByInstance(sInstanse);
                    }
                }
            }        private void RegistServerByInstance(string sInstance) //SqlInstances
            {
                string sKey = "";
                if (sInstance != "") sKey = "_" + sInstance;
                try
                {
                    //// HTTP ////////////////////////////////////////////////
                    string sHttpPort = ftCommon.GetSetting("SqlHttpPort" + sKey);
                    int nHttpPort = sHttpPort == "" ? 13101 : int.Parse(sHttpPort);
                    IDictionary htHttpProp = new Hashtable();
                    htHttpProp["name"] = "Http" + nHttpPort.ToString();
                    htHttpProp["port"] = nHttpPort;
                    HttpChannel HttpChannel = new HttpChannel(htHttpProp, new SoapClientFormatterSinkProvider(), new SoapServerFormatterSinkProvider());
                    ChannelServices.RegisterChannel(HttpChannel, false);
                    RemotingConfiguration.RegisterWellKnownServiceType(typeof(ftSqlDataAccess), ftCommon.GetSetting("SqlHttpObjectUri" + sKey), WellKnownObjectMode.Singleton); //统一资源标识符(URI)
                    // Tcp ////////////////////////////////////////////////
                    string sTcpPort = ftCommon.GetSetting("SqlTcpPort" + sKey);
                    int nTcpPort = sTcpPort == "" ? 13102 : int.Parse(sTcpPort);
                    BinaryServerFormatterSinkProvider TcpServerProvider = new BinaryServerFormatterSinkProvider();
                    BinaryClientFormatterSinkProvider TcpClientProvider = new BinaryClientFormatterSinkProvider();
                    TcpServerProvider.TypeFilterLevel = TypeFilterLevel.Full;
                    IDictionary htTcpProp = new Hashtable();
                    htTcpProp["name"] = "Tcp" + nTcpPort.ToString();
                    htTcpProp["port"] = nTcpPort;
                    //isok? htTcpProp["timeout"] = 1000;
                    TcpChannel TcpChannel = new TcpChannel(htTcpProp, TcpClientProvider, TcpServerProvider);
                    ChannelServices.RegisterChannel(TcpChannel, false);
                    RemotingConfiguration.RegisterWellKnownServiceType(typeof(ftSqlDataAccess), ftCommon.GetSetting("SqlTcpObjectUri" + sKey), WellKnownObjectMode.Singleton);            }
                catch (Exception ex)
                {
                    ftWinCommon.MsgBox(string.Format("Instance={0}",sInstance) + "\n\n" + ex.Message, ftMsgBoxBtn.ftII);
                }
            }
        }
    }(这些是原样,对象名字与题目中的不一致)
      

  2.   

    DbBll.dll (Remoting部分)namespace FT.SqlBll
    {
        public class ftRemoteDataAccessBll
        {
            public ftSqlDataAccess _rda = null;        public ftRemoteDataAccessBll()
            {
                //
            }        private void Unregist()
            {
                IChannel[] channels = ChannelServices.RegisteredChannels;
                foreach (IChannel aChannel in channels)
                {
                    if (aChannel.ChannelName == "ftSqlDataAccess" || (aChannel.ChannelName == "tcp"))
                    {
                        try
                        {
                            TcpChannel ch = (TcpChannel)aChannel;
                            ch.StopListening(null);
                            ChannelServices.UnregisterChannel(ch);
                        }
                        catch (Exception ex) { throw ex; }
                    }
                    if (aChannel.ChannelName == "ftSqlDataAccess.soap" || (aChannel.ChannelName == "http"))
                    {
                        HttpChannel ch = (HttpChannel)aChannel;
                        ch.StopListening(null);
                        ChannelServices.UnregisterChannel(ch);
                    }
                }
            }        public ftSqlDataAccess RDA()
            {
                if (_rda != null)
                {
                    try
                    {
                        _rda.ftCreateSqlTest().Test();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        _rda = null;
                    }
                }            //get if null
                try
                {
                    //if (_rda == null) _rda = GetRDA(false);
                    if (_rda == null)
                    {
                        ftSqlDataAccess aa = new ftSqlDataAccess();
                        aa = GetRDA();
                        _rda = aa;
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(ex.Message);
                }
                //get if disconnect
                try
                {
                    ftISqlDalTest test = _rda.ftCreateSqlTest();
                    test.Test();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    _rda = GetRDA(true);
                }
                //
                return _rda;
            }        private ftSqlDataAccess GetRDA() { return GetRDA(false); }        private ftSqlDataAccess GetRDA(Boolean bShowLink)
            {
                object remoteObj = null;
                ftSqlDataAccess ret = null;            Unregist();            //Get Server.Object 
                //String sRemoteType = System.Configuration.ConfigurationManager.AppSettings["RemoteType"].ToString().ToUpper();
                String sRemoteType = ftCommon.GetSetting("SqlRemoteType").ToUpper(); //HTTP, TCP, (Other)
                string sUri = ""; //统一资源标识符(URI)
                switch (sRemoteType)
                {
                    case "HTTP":
                        try
                        {
                            HttpChannel channel1 = new HttpChannel();
                            ChannelServices.RegisterChannel(channel1, false);
                            //OK remoteObj = (DAL.DataAccess)Activator.GetObject(typeof(DAL.DataAccess), "http://127.0.0.1:13101/MyDataAccess.soap");
                            //remoteObj = (BLDB)Activator.GetObject(typeof(DataAccess), System.Configuration.ConfigurationManager.AppSettings["HttpURI"].ToString());
                            sUri = ftCommon.GetSetting("SqlHttpURI");
                            //@@@if (bShowLink) { sUri = ftFormCommon.InputBox("","Server address:", sUri); }
                            remoteObj = (ftSqlDataAccess)Activator.GetObject(typeof(ftSqlDataAccess), sUri);
                        }
                        catch (Exception ex) { throw ex; }
                        break;
                    case "TCP":
                        try
                        {
                            TcpChannel channel2 = new TcpChannel();
                            ChannelServices.RegisterChannel(channel2, false);
                            //OK remoteObj = (DAL.DataAccess)Activator.GetObject(typeof(DAL.DataAccess), "tcp://127.0.0.1:13102/MyDataAccess");
                            //remoteObj = (BLDB)Activator.GetObject(typeof(DataAccess), System.Configuration.ConfigurationManager.AppSettings["TcpURI"].ToString());
                            sUri = ftCommon.GetSetting("SqlTcpURI");
                            //@@@if (bShowLink) { sUri = ftFormCommon.InputBox("", "Server address:", sUri); }
                            remoteObj = (ftSqlDataAccess)Activator.GetObject(typeof(ftSqlDataAccess), sUri);
                        }
                        catch (Exception ex) { throw ex; }
                        break;
                    default:
                        //Local Obect
                        remoteObj = new ftSqlDataAccess();
                        //remoteObj = new BLDB();
                        break;
                }
                ret = (ftSqlDataAccess)remoteObj;            int TryCount = 3;
                while (TryCount > 0)
                {
                    //执行这段的主要目的是试预连远程服务器,如果服务器未启动,则启动本地服务器。
                    try
                    {
                        ftISqlDalTest test = ret.ftCreateSqlTest();
                        test.Test();
                        return ret;
                    }
                    catch (System.Net.WebException ex)
                    {
                        Console.WriteLine(ex.Message);
                        ////if (!isDebug) 
                        //if (ftFormCommon.MsgBox("Server(HTTP) disconnect!!\n" + ex.Message + "\n\nOpen local server?", ftMsgBoxBtn.ftQQ) == ftMsgBoxRet.ftYesBoxRet)
                        ShellLocalServer();
                    }
                    catch (System.Net.Sockets.SocketException ex)
                    {
                        Console.WriteLine(ex.Message);
                        ////if (!isDebug) 
                        //if (ftFormCommon.MsgBox("Server(TCP) disconnect!!\n" + ex.Message + "\n\nOpen local server?", ftMsgBoxBtn.ftQQ) == ftMsgBoxRet.ftYesBoxRet)
                        ShellLocalServer();
                    }
                    catch (Exception ex)
                    {
                        //ftFormCommon.MsgBox("GetRDA() error!!\n\n" + ex.Message, ftMsgBoxBtn.ftEE);
                        throw new Exception("FT.SqlBll.ftRemoteDataAccess.GetRDA() error!!\n\n" + ex.Message);
                    }                TryCount--;                if (TryCount == 0) throw new Exception("FT.SqlBll.ftRemoteDataAccess.GetRDA() error!!\n\n");
                }            return ret;
            }        private void ShellLocalServer()
            {
                try
                {
                    ftWinCommon.FlashBox("Loading SQL-AppServer...");
                    ftWinCommon.Shell(@".\FT.SqlApplicationServer.exe");
                }
                //catch (Exception ex) { Console.WriteLine("Try run [FT.SqlApplicationServer.exe]\n\n" + ex.Message); }
                catch (Exception ex)
                {
                    //Console.WriteLine("Try run [FT.SqlApplicationServer.exe]\n\n" + ex.Message);
                    //ftWinCommon.MsgBox("Try run [FT.SqlApplicationServer.exe]\n\n" + ex.Message);
                    try
                    {
                        ftWinCommon.Shell(@"E:\FT\FT50\$bin\FT.SqlApplicationServer.exe");
                    }
                    catch (Exception ex2)
                    {
                        try
                        {
                            ftWinCommon.MsgBox("Try run [FT.SqlApplicationServer.exe]\n\n" + ex.Message + "\n" + ex2.Message);
                        }
                        catch { }
                    }
                }
            }    }
    }
      

  3.   

    1. DbDal.dll中的SqlDal类与DbBll.dll中的SqlBll类都实现了数据访问接口IDbDal,为的是无论是三层还是两层都可以用此接口访问。
    2. 为了能让WinForm程序跨Internet,必需做成Remoting的形式,
    所以把DbFactory的DbDataAccess类定义成了动态可序列化的,因为只有这里是关口(也就是说从这里划开远程AppServer与客户端)。
    3. 查询语句sql用的是明文,是否需要加密传输。DataTable现在可以以压缩加密方式传送。
    4. 服务器状态还没弄清楚,但测试结果显示第二天状态仍有效。
    5. 如果存在多帐套的情况,好像后面的帐套连接字符串会冲掉前面的连接字符串。现在程序可以运行,但担心的是安全问题,因为考滤将来要跨Internet,
    很多客户是没有固定IP,用花生壳代理:http://cust_server.vicp.net/DbDataAccess.soap(谢谢楼上的,因为:一个用户只允许连续回复3次。
    我的FineTemp帐号哪时被收回去了,但收回去又不把我的邮箱空出来,
    申请的时候提示邮箱已占用)
      

  4.   

    不是吧,这叫长?没看过人家几百行的sql存储过程?除了三楼与四楼的代码,像0楼的代码是精简得不能再精简了。
      

  5.   


    以这种架构跨Internet?为什么不做成真三层呢。这种架构跨Internet不现实,很现实一点,中间计算的数据量可能很大,Internet可能支撑不了的。其他的就先不说了,会有很多问题的。
      

  6.   

    你使用的一定是 .NET Remoting 技术,并且还使用了 DataTable 等。那么我想你的客户端程序也是 .NET 的,否则无论是 Java 还是其它语言都难以进行调用了。如果我的猜测是正确的,那么你想,如果我在客户端引用了你的 DLL 或者 EXE,然后传入“DROP DATABASE XXX”或者“DELETE XXX”,你如何应对?正确的做法是,服务器端是提供业务逻辑接口,通过参数进行调用,另外服务器端一定要对参数进行安全检查,甚至对调用行为进行检查。
      

  7.   

    18楼所言极是,当然也知道传sql是不安全的。
    可以屏蔽 delete/drop/alter 等语句,但没屏蔽exec语句(调用存储过程要用到)还是有隐患。当然了,如果人家真要搞破坏用参数也防不了:
    List<OrderInfo> infos = bll.GetListAll();
    foreach(OrderInfo info in infos)
    {
        bll.DeleteByCode(info.Code);
    }C#客户端程序是可以反编译的,内衣都可以转换为玻璃,这个很烦人。
      

  8.   

    真三层对小系统来讲太麻烦了,比如0楼的 SQL_DEFault, SQL_EXTend, SQL_LiST 这三个表就要做成三个逻辑对象,到时改了表结构的话就要一大串的级联修改。用DataRow/DataTable逻辑对象的参数可以统一,也可以省去很多字段的付值过程,且DataTable是支持状态及排序的。(人家说丢不了DataTable就进不了真OOP,我也只能默默承认)sql语句是写在Client.Bll.dll中的,Client.exe里边不会出现sql语句,只会出现DataRow/DataTable的字段,当然sql语句放在客户端的BLL中也是不安全,毕竟此BLL也是客户端。中间计算数据可能很大,我用存储过程实现,只要传个exec语句即可。另外,我在程序中是很少用delete语句的,一般都是用 update aTable set Status=-1, Code='-'+Code 这样在维护时可以恢复误删除,甚至可以知道是谁哪时删的。Code栏位一般用30个字符,对于一般的代号或编号足用了,前面加几个“-”号一般不会超过字段大小。
      

  9.   

    将来只会用ms的产品,所以不用考滤跨开发环境的问题。
    用datatable简单,且功能强大,不用自己写复杂的代码,唯一不足的就是“row["Name"]”中的中括号与引号输起来麻烦。网站在安全性方面一般有什么技术?
    我们以前用delphi开发的三层,只要发布到公网上,ApServer动不动就会被卡死,可能就是外面在黑。