我有一个简单的WebForms项目来测试并发性。
我正在使用:
1. Entity Framework 4.3.1 code first approach.
2. DevExpress ASP.net controls to visualize my data. Specifically an ASPXGridView control.
3. MySQL as database backend.
现在我遇到了并发检查的问题。
即使我是唯一编辑数据的用户,如果我使用DevExpress ASPXGridView编辑两次相同的记录,我会得到一个并发异常!
我得到的例外是: System.Data.OptimisticConcurrencyException
**为简洁起见,此处简化
我的代码第一个实体定义如下:
public class Country
{
//Some constructors here
[Required, ConcurrencyCheck]
public virtual string LastUpdate { get; set; }
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int CountryID { get; set; }
//Various other data fields here
}
你可以看到我添加了一个名为LastUpdate的字段,由于设置了[ConcurrencyCheck]属性,正在测试concurrecny检查。
在我的网页上使用DevExpress ASPXGridView我正在使用EntityDataSource来实现网格视图和实体框架之间的绑定。网格视图使用弹出编辑器。我挂了以下事件:
protected void Page_Load(object sender, EventArgs e)
{
//Hook entity datasource to grid view
dbgCountries.DataSource = CountriesEntityDataSource;
dbgCountries.DataBind();
}
protected void CountriesEntityDataSource_ContextCreating(object sender, EntityDataSourceContextCreatingEventArgs e)
{
//Create and hook my DBContext class to the entity
//datasources ObjectContext property.
var context = new MyDBContextClass();
e.Context = ((IObjectContextAdapter)context ).ObjectContext;
}
protected void dbgCountries_InitNewRow(object sender, DevExpress.Web.Data.ASPxDataInitNewRowEventArgs e)
{
//I create a new MyDBContextClass here and use it
//to get the next free id for the new record
}
protected void dbgCountries_CustomErrorText(object sender, DevExpress.Web.ASPxGridView.ASPxGridViewCustomErrorTextEventArgs e)
{
//My code to catch the System.Data.OptimisticConcurrencyException
//excpetion is in here.
//I try to rtefresh the entity here to get the latest data from
//database but I get an exception saying the entity is not being
//tracked
}
protected void dbgCountries_RowValidating(object sender, DevExpress.Web.Data.ASPxDataValidationEventArgs e)
{
//Basic validation of record update in here
}
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//I set the LastUpdate field (my concurrency field)
//to the current time here
}
我也有一些按钮事件被挂钩来测试直接的concurrecny测试。
例如
- Get Entity
- Update Entity
- Update DB directly with sql
- Save Entity
- Get concurrency exception as expected
例如
- Get Entity
- Update Entity
- Save Entity
- No issue.
- Get Entity again.
- Update Entity again.
- Save Entity again.
- No issue.
这些按钮按预期工作。只有网格更新似乎有问题。
也许是因为网格需要使用ObjectContect而我的实体框架类正在使用DBContext?
我已经在互联网上寻找解决方案。查看了DevExpress论坛,查看了StackOverflow上的其他帖子,互联网上的各种帖子,关于并发的Microsoft MSDN文章,我无法解决这个问题。
这些帖子都没有我的那么“简单”。他们都涉及其他数据。例如主人/细节 relashionship。自定义编辑器。我正在使用所有内置的DevExpress控件,只显示一个 我的数据库表/实体上的单个网格视图。
有些帖子建议刷新实体。我试过这个,但得到一个例外,说实体是 没有在对象状态管理器中跟踪。
我尝试通过销毁和重新创建对象context / db来刷新实体框架 上下文,但不知怎的,我仍然得到并发问题。
我尝试使用DBContexct和ObjectContext进行刷新。都没有奏效。 (objContext.Refresh(RefreshMode.StoreWins,entity)。我要么得到一个例外,如所述] 较早说,该实体未被跟踪,或者我告诉它只刷新非修改 然后实体根本没有任何事情发生(没有刷新,没有激发)
我尝试将我的DBContext设为全局,但这并不好,因为WebForms似乎想要重新创建它 整个状态并在每次Web刷新后重新挂起其网格数据上下文等。 (页面加载,用户 点击编辑,用户点击确定更新)
现在所有这些解决方案似乎都要在并发异常之后做些什么。看到我甚至不应该首先得到例外,我猜他们没有帮助。
你们有没有关于如何使这项工作的建议?
在从网格发布数据后,我是否必须强制实体框架手动刷新? (我现在才想起这个)
这似乎是一个非常简单的设置。也许我错过了一些非常明显的东西。我还没有使用WebForms或EntityFramework,所以我可能会找到简单(也许是显而易见的)解决方案吗?
任何帮助表示感谢。
由于 彼得梅斯
答案 0 :(得分:1)
我设法解决了我的问题。
它可能不是最正确的解决方案,但它正在发挥作用,此时我们非常感谢任何进展。
最后,我正在进行各种尝试,并注意到如果我去了另一个 在我的网站上的页面,然后再回来问题不会发生。
E.g:
不同之处在于,如果我执行 not 切换页面,则第二次编辑会创建并发异常。
随着与同事的一些更多测试,我得到了一个预感,也许是观众 在ASPGridView上发布/更新后,实体数据源未刷新。
所以我做的是:
> Set EntityDataSource.StoreOriginalValuesInViewState = FALSE
由于没有存储旧的/预编辑值,因此停止了所有并发工作 没有可用于concurrecny检查。
然后我想我会在编辑之前强制旧值在编辑器中。 我使用ASPXGridView.RowUpdating来做到这一点。
我认为没关系,我可以使用传递给ASPXGridView.RowUpdating的OldValues来 确保实体框架不错。
这样做我发现了一些非常奇怪的行为......
如果我:
- open edit form in browser A
- open edit form in browser B
- save changes in browser B (DB updates with new values here)
- save changes in browser A (DB updated here too. but should have been a
concurrency exception!)
A的帖子成功的原因是A上的OldValues已经神奇地更新了 B发布的新值!
请记住,A上的编辑表单一直是打开的,因此它不应该更新其下面的OldValues。我不知道为什么会这样。很奇怪。
也许OldValues不会被DevExpress ASPXGridView检索到 编辑表格正在关闭?
无论如何,我想。好吧,我会解决这个奇怪的问题。所以我这样做了 网页上的静态成员,用于存储我的国家/地区实体的副本。
当用户打开编辑器时,我会获得当前值并存储它们。
然后当ASPXGridView.RowUpdating触发时,我将存储的旧值推回到 OldValues数据。 (我也在NewValues中更新了我的timstamp / concurrency字段 数据)
通过这种方法,我的并发现在可以正常工作。 Hurah!
我可以根据需要在本地编辑,不会发生冲突。如果我一次在两个浏览器中编辑,则第二个浏览器会引发并发异常。
我也可以在MySQL和MSSQL之间切换,现在都能正常工作。
创建私人会员以保留预编辑国家/地区值
private static Country editCountry = null;
在ASPXGridView上挂起StartRowEditing。在这里,我获取当前的国家/地区值并将其存储为“预编辑”值。请注意,CopyFrom只是我实体的辅助方法。
protected void dbgCountries_StartRowEditing(object sender, DevExpress.Web.Data.ASPxStartRowEditingEventArgs e)
{
if (editCountry == null)
{
editCountry = new Country();
}
var context = new MyDBContext();
var currCountry = context.Countries.Where(c => c.CountryID == (int)(e.EditingKeyValue)).Single();
editCountry.CopyFrom(currCountry);
}
在ASPXGridView上挂钩RowUpdating。在更新继续之前,我确保旧值正确。这可以确保并发性按预期工作。
protected void dbgCountries_RowUpdating(object sender, DevExpress.Web.Data.ASPxDataUpdatingEventArgs e)
{
//Ensure old values are populated
e.OldValues["RowVersion"] = editCountry.RowVersion;
//No need to set other old values as I am only testing against
//the one field for concurrency.
//On row updating ensure RowVersion is set.
//This is the field being used for the concurrency check.
DateTime date = DateTime.Now;
var s = date.ToString("yyyy-MM-dd HH:mm:ss");
e.NewValues["RowVersion"] = s;
}