/// <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;
}
///
/// </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;
}
我知道肯定有方法解决,只是我现在可能已经钻牛角尖出不来了。回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);
}
}
我的代码的步骤是这样的:
bool isStockAble = IsInStockAble(Lists, out ListsResult, out strMessage);//这里检验更新状态,并且把符合条件的作为参数(out ListsResult)传出来,后面开始事务处理。我担心的问题是,检验的时候是符合条件的,而检验完、更新前这段时间别的用户修改了数据,怎么办?
检验,在获取数据的时候是没有用事务的。
当用户保存的时候,把这个时间和数据库的时间对比对比、保存同一个SQL语句吗?
不然,如果分两个方法,如下;
private bool 对比(){}
//这段时间,即对比完成、保存结束前,有用户更改呢?
private void 保存()
{
if(对比()) { save();}
}其实我想知道时间戳的对比、和保存的操作是怎么写的。
已经说得很清楚了,不知保存GUID是否也可以.没试过.
>>
这个有点描述不清楚,别的用户修改了[收貨表],是指修改了DB的数据吗? 什么情况下会才会导至重复增加库存?是不是说在你验证收货表的过程中,另一个用户则已经验证完你的收货表,并且已经(或者正在执行)更新库存这样你的收货记录就会导至库存重复增加?
問題 2:如果加時間戳,是否用更新的記錄數來判斷是否更新成功,如果成功了,才執行下一步更新庫存表。不然有可能沒修改[收貨表]的狀態,下面卻增加了庫
>>
这个问题,在我看来,你不该将"更新收货表的状态"和"更新库存"拆成两个foreach来执行啊!正确的逻辑应该是,对每一笔收货记录来说,先"更新收货表状态",接着"更新库存",这样你的时间戳对比的动作就可以放在"更新收货表状态"之前进行,如果时间戳不对,那你可整个RollBack掉事务.所以,你的TransferFromTdpur045看起来要改成返回 Dictionary<TKey,TKey>比较好,让收货记录跟库存记录能对应起来,这样你的更新逻辑放在一个foreach里边就可以了。如果问题2解决了,那问题1的重复库存就可解决。
>>
这个有点描述不清楚,别的用户修改了[收貨表],是指修改了DB的数据吗? 什么情况下会才会导至重复增加库存?是不是说在你验证收货表的过程中,另一个用户则已经验证完你的收货表,并且已经(或者正在执行)更新库存这样你的收货记录就会导至库存重复增加? 您說得不錯,也可以這樣說:
我驗証完收貨表,“準備” 更新庫存表的時候,別的用戶A修改了收貨表,修改操作包括入庫。那麼就會導致
我、用戶A重復入庫你的TransferFromTdpur045看起来要改成返回 Dictionary <TKey,TKey>比较好,让收货记录跟库存记录
能对应起来,这样你的更新逻辑放在一个foreach里边就可以了。 太謝謝了!
一般这样:
开始一个事务
try
{
取出某票未入库的已审核的采购单和它的采购物料子表数据,取出批次
某条采购物料子表数据的要入库的库位
库位是否可用,是否已满
否则检查库存表该库位有没有该物料,通过物料编号和仓库、库位等信息查询一下
有就在这条上更新增加这次入库的数量,没有就新加一条库存记录,存入这次的入库数量
提交事务
}
catch
{
回滚事务
}
从第一步起都包含在事务里,不会有并发问题
也就是說select也用事務?
那变通一下,TransferFromTdpur045不要返回一个容器了,直接返回一笔库存记录,在收货表的
foreach里面去call TransferFromTdpur045, 总之目的就是并到一个foreach,好做时间戳的判断及事务的控管。
明白你的意思。
隻是以前寫過類似的方法,但是出問題。
因為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;
}
是啊,select数据的时候也把之前开的这个事务传进去啊,而且连接如果开了一个事务,基于这连接的所有命令本身就必须传该事务,即使是select
这样select后,知道提交或回滚数据前,select的表默认就是锁死的,别人不可能再去改数据,
如果要求能改,就要降低事务隔离级别,但可能有脏数据,默认是锁表的那个
我首先得查一下SqlDataReader怎麼使用事務的。續:30樓
“因為foreach范圍內用了事務,而foreach裡面的方法讀數據用SqlDataReader.最後報錯,具體錯誤信息忘了。”
報的錯誤是"Timeout expire"