/// <summary>根據篩選條件更新庫存
        /// 
        /// </summary>
        ///<param name="strFilter">收貨表篩選條件</param>
        /// <param name="upor">入庫人員s</param>
        /// <param name="strMessage">提示信息</param>
        /// <returns></returns>
        public static bool InStockList(string strFilter,string upor, out string strMessage)
        {
            strMessage = string.Empty;            //Step 1:從[收貨表]獲取未更新入庫的物料
            IList<Model.Tdpur045Info> Lists = GetModelByTranFilter(strFilter, Common.StateOfReciptEnum.InStock);  
            
            //用來過濾Step 1獲取到的物料。因為不是所有未更新入庫的物料都可以入庫,比如庫位凍結了就不能入
            IList<Model .Tdpur045Info> ListsResult=new  List<Model .Tdpur045Info >();
            
            //Step 2:驗証是否可以入庫,比如是否維護了庫位。
            //out ListsResult:根據最初獲取到的未更新的物料,設置完其他字段信息,如目標倉庫、庫位。
            bool isStockAble = IsInStockAble(Lists, out ListsResult, out strMessage);            //問題 1:在驗証的過程當中,別的用戶修改了[收貨表],後面就有可能重復增加庫存            if (!isStockAble) return false;            //Step 3:根據過濾後的[收貨表],生成庫存表Model。
            //方法看下面的方法TransferFromTdpur045(IList<Model.Tdpur045Info> Lists)
            //思路是,先根據物料、批次、倉庫、庫位從[庫存表]獲取記錄,存在則將當前庫存加上收貨表的入庫數,
            // 沒記錄,則new庫存表,直接將入庫數賦給當前庫存。更新的時候會判斷到底是Add、Upate
            IList<Model.Tdilc101Info> Lists101 = BLL.Tdilc101Mgr.TransferFromTdpur045(ListsResult);            SqlConnection Conn = new SqlConnection(DAL.ConnMgr.GetConnString);            Conn.Open();            SqlTransaction Trans = Conn.BeginTransaction(IsolationLevel.ReadUncommitted);            try
            {
                //更新收貨表:狀態為“已更新”
                foreach (Model.Tdpur045Info Info in ListsResult)
                {
                    Info.upor = upor;                                   //更新收貨表
                    DAL.Tdpur045Dal.UpdateModel(Info, Trans);                    //問題 2:如果加時間戳,是否用更新的記錄數來判斷是否更新成功,如果成功了,才執行下一步更新庫存表。不然有可能沒修改[收貨表]的狀態,下面卻增加了庫存
                }                foreach (Model.Tdilc101Info Info in Lists101)
                {
                    //根據唯一編號來判斷是新增、修改
                    if (Info.auid == 0)
                    {
                        BLL.Tdilc101Mgr.AddModel(Info, Trans);
                    }
                    else
                    {
                        BLL.Tdilc101Mgr.UpdateModel(Info, Trans);
                    }
                }                Trans.Commit();                return true;
            }
            catch (Exception ex)
            {
                Trans.Rollback();                strMessage = ex.Message;                return false;
            }
            finally
            {
                Conn.Close();                Conn.Dispose();
            }
          
        }         /// <summary>根據收貨表生成庫存表
        /// 
        /// </summary>
        /// <param name="Lists"></param>
        /// <returns></returns>
        public static IList<Model.Tdilc101Info> TransferFromTdpur045(IList<Model.Tdpur045Info> Lists)
        {
            IList<Model.Tdilc101Info> Lists101 = new List<Model.Tdilc101Info>();            foreach (Model.Tdpur045Info Info in Lists)
            {                Model.Tdilc101Info Info101 = GetModelByID(
                    Info.cwat,
                    Info.loct,
                    Info.item,
                    Info.clot);                if (Info101 == null)
                {
                    //庫存表不存在,則auid為0,后面將根据它來判斷是新增還是修改
                    //直接將收貨數賦給當前庫存
                    Info101 = new WareHouse.Model.Tdilc101Info();
                    Info101.cwar = Info.cwat;
                    Info101.loca = Info.loct;
                    Info101.item = Info.item;
                    Info101.clot = Info.clot;
                    Info101.strs = Info.quin;
                    Info101.amnt = Info.amnt;
                }
                else
                {
                   //存在,則在原來的庫存基礎上累加
                    Info101.strs += Info.quin;
                    Info101.amnt += Info.amnt;
                }                //收貨表其他信息
                Info101.ccde = Info.ccde;
                Info101.ctyo = Info.ctyo;
                Info101.aitc = Info.aitc;
                Info101.dscb = Info.dscb;
                Info101.wght = Info.wght;
                Info101.cpcp = Info.cpcp;
                Info101.stun = Info.stun;
                Info101.ccur = Info.ccur;
                Info101.ratp = Info.ratp;
                Info101.pric = Info.pric;                //如果物料、批次、倉庫、庫位相同,還得累加
                if (!Lists101.Contains(Info101))
                {
                    Lists101.Add(Info101);
                }
                else
                {
                    //如果已經存在,則在Ilist的基礎上累加
                    int int_indx = Lists101.IndexOf(Info101);
                    Model.Tdilc101Info Info101Result = Lists101[int_indx];                    Info101Result.strs += Info.quin;
                    Info101Result.amnt += Info.amnt;                    Lists101.RemoveAt(int_indx);                    Lists101.Insert(int_indx, Info101Result);
                }
            }            return Lists101;
        }

解决方案 »

  1.   

    首先非常感谢大家的关注!感谢大家的回复。
    我知道肯定有方法解决,只是我现在可能已经钻牛角尖出不来了。回6楼:
    使用时间戳的话,我考虑这样一个问题:下面两个循环,第一个循环(即修改收货表状态)的时候,后面的库存表已经被别的用户更新了,
    我再更新的时候,不会更新到已经被别的用户更新的数据。问题是:第一个循环已经修改的,标识为“已更新库存”,而第二个循环却没有
    增加到库存。               //更新收貨表:狀態為“已更新”
                    foreach (Model.Tdpur045Info Info in ListsResult)
                    {
                        Info.upor = upor;               
                        //更新收貨表
                        DAL.Tdpur045Dal.UpdateModel(Info, Trans);                 
                    }                foreach (Model.Tdilc101Info Info in Lists101)
                    {
                        //根據唯一編號來判斷是新增、修改
                        if (Info.auid == 0)
                        {
                            BLL.Tdilc101Mgr.AddModel(Info, Trans);
                        }
                        else
                        {
                            BLL.Tdilc101Mgr.UpdateModel(Info, Trans);
                        }
                    }
      

  2.   

    回10楼:
    我的代码的步骤是这样的:  
    bool isStockAble = IsInStockAble(Lists, out ListsResult, out strMessage);//这里检验更新状态,并且把符合条件的作为参数(out ListsResult)传出来,后面开始事务处理。我担心的问题是,检验的时候是符合条件的,而检验完、更新前这段时间别的用户修改了数据,怎么办?
    检验,在获取数据的时候是没有用事务的。
      

  3.   


    当用户保存的时候,把这个时间和数据库的时间对比对比、保存同一个SQL语句吗?
    不然,如果分两个方法,如下;
    private bool 对比(){}
    //这段时间,即对比完成、保存结束前,有用户更改呢?
    private void 保存()
    {
      if(对比()) { save();}
    }其实我想知道时间戳的对比、和保存的操作是怎么写的。
      

  4.   


    已经说得很清楚了,不知保存GUID是否也可以.没试过.
      

  5.   

    问题1:在驗証的過程當中,別的用戶修改了[收貨表],後面就有可能重復增加庫存.
    >>
    这个有点描述不清楚,别的用户修改了[收貨表],是指修改了DB的数据吗? 什么情况下会才会导至重复增加库存?是不是说在你验证收货表的过程中,另一个用户则已经验证完你的收货表,并且已经(或者正在执行)更新库存这样你的收货记录就会导至库存重复增加?
    問題 2:如果加時間戳,是否用更新的記錄數來判斷是否更新成功,如果成功了,才執行下一步更新庫存表。不然有可能沒修改[收貨表]的狀態,下面卻增加了庫
    >>
    这个问题,在我看来,你不该将"更新收货表的状态"和"更新库存"拆成两个foreach来执行啊!正确的逻辑应该是,对每一笔收货记录来说,先"更新收货表状态",接着"更新库存",这样你的时间戳对比的动作就可以放在"更新收货表状态"之前进行,如果时间戳不对,那你可整个RollBack掉事务.所以,你的TransferFromTdpur045看起来要改成返回 Dictionary<TKey,TKey>比较好,让收货记录跟库存记录能对应起来,这样你的更新逻辑放在一个foreach里边就可以了。如果问题2解决了,那问题1的重复库存就可解决。
      

  6.   

    回22樓:问题1:在驗証的過程當中,別的用戶修改了[收貨表],後面就有可能重復增加庫存.
    >>
    这个有点描述不清楚,别的用户修改了[收貨表],是指修改了DB的数据吗? 什么情况下会才会导至重复增加库存?是不是说在你验证收货表的过程中,另一个用户则已经验证完你的收货表,并且已经(或者正在执行)更新库存这样你的收货记录就会导至库存重复增加? 您說得不錯,也可以這樣說:
    我驗証完收貨表,“準備” 更新庫存表的時候,別的用戶A修改了收貨表,修改操作包括入庫。那麼就會導致
    我、用戶A重復入庫
    你的TransferFromTdpur045看起来要改成返回 Dictionary <TKey,TKey>比较好,让收货记录跟库存记录
    能对应起来,这样你的更新逻辑放在一个foreach里边就可以了。 
    太謝謝了!
      

  7.   

    我得先學習Dictionary 的用法!
      

  8.   

    好像是工厂通过采购批次来进行入库的逻辑么,感觉做得有点复杂了哦,
    一般这样:
    开始一个事务
    try
    {
    取出某票未入库的已审核的采购单和它的采购物料子表数据,取出批次
    某条采购物料子表数据的要入库的库位
    库位是否可用,是否已满
    否则检查库存表该库位有没有该物料,通过物料编号和仓库、库位等信息查询一下
    有就在这条上更新增加这次入库的数量,没有就新加一条库存记录,存入这次的入库数量
    提交事务
    }
    catch
    {
    回滚事务
    }
    从第一步起都包含在事务里,不会有并发问题
      

  9.   

    邏輯跟我的差不多。从第一步起都包含在事务里,不会有并发问题 
    也就是說select也用事務?
      

  10.   


    那变通一下,TransferFromTdpur045不要返回一个容器了,直接返回一笔库存记录,在收货表的
    foreach里面去call TransferFromTdpur045, 总之目的就是并到一个foreach,好做时间戳的判断及事务的控管。
      

  11.   


    明白你的意思。
    隻是以前寫過類似的方法,但是出問題。
    因為foreach范圍內用了事務,而foreach裡面的方法讀數據用SqlDataReader.最後報錯,具體錯誤信息忘了。
    不知道ExecuteDataReader(string cmdText)改怎麼改。 public static SqlDataReader ExecuteDataReader(string cmdText)
            {            SqlCommand cmd = new SqlCommand();            SqlConnection Conn = new SqlConnection(ConnMgr.GetConnString);            cmd.Connection = Conn;            cmd.CommandText = cmdText;            if (Conn.State != System.Data.ConnectionState.Open)
                {
                    Conn.Open();
                }            SqlDataReader reader = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);            return reader;
            }
      

  12.   


    是啊,select数据的时候也把之前开的这个事务传进去啊,而且连接如果开了一个事务,基于这连接的所有命令本身就必须传该事务,即使是select
    这样select后,知道提交或回滚数据前,select的表默认就是锁死的,别人不可能再去改数据,
    如果要求能改,就要降低事务隔离级别,但可能有脏数据,默认是锁表的那个
      

  13.   


    我首先得查一下SqlDataReader怎麼使用事務的。續:30樓
    “因為foreach范圍內用了事務,而foreach裡面的方法讀數據用SqlDataReader.最後報錯,具體錯誤信息忘了。”
    報的錯誤是"Timeout   expire"