我试图弄清楚如何解决一些导致 SQL 中重复行的争用问题。下面我展示了代码,在最底部展示了我的问题/疑虑。
此处提供 VS2012 解决方案的完整下载:http ://www15.zippyshare.com/d/72956037/4552733/EFConcurrency.zip
我使用设计器创建了一个实体框架模型。这是模型:
我已经使用以下代码填充了数据库:
using (EfTestContainer db = new EfTestContainer())
{
User u = new User();
u.Email = "test@test.com";
db.Users.Add(u);
for (int i = 1; i < 10000; i++)
{
Car c = new Car();
c.CarName = "Cool Car";
c.User = u;
db.Cars.Add(c);
}
db.SaveChanges();
}
如您所见,我们的数据库现在有 1 个用户和 10,000 辆汽车。
然后我创建三个相互竞争的线程来添加 SpareTires。
private void button1_Click(object sender, EventArgs e)
{
// This will start three threads that all try to add SpareTires at the same time.
ThreadPool.QueueUserWorkItem(new WaitCallback(Go), new object());
}
public static void Go(object o)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AddSpareTires2), new object());
Thread.Sleep(50);
ThreadPool.QueueUserWorkItem(new WaitCallback(AddSpareTires3), new object());
Thread.Sleep(500);
ThreadPool.QueueUserWorkItem(new WaitCallback(AddSpareTires1), new object());
while (true)
{
if (done1 && done2 && done3) break;
Thread.Sleep(250);
}
MessageBox.Show("Done!");
}
public static bool done1 = false;
public static bool done2 = false;
public static bool done3 = false;
private static void AddSpareTires1(object o)
{
using (EfTestContainer db = new EfTestContainer())
{
User u = db.Users.FirstOrDefault();
var cars = db.Cars.ToList<Car>();
foreach (var car in cars)
{
SpareTire st = new SpareTire();
st.BrandName = "Cool Tire";
car.SpareTire = st;
}
try
{
db.SaveChanges();
}
catch (OptimisticConcurrencyException exc)
{
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, cars); // It doesn't seem to make a difference if I use RefreshMode.ClientWins
db.SaveChanges();
}
catch (Exception)
{
}
done1 = true;
}
}
private static void AddSpareTires2(object o)
{
using (EfTestContainer db = new EfTestContainer())
{
for (int i = 1; i < 100; i++)
{
int min = (i-1) * 10;
int max = i * 10;
User u = db.Users.FirstOrDefault();
var cars = db.Cars.Where(c => c.Id > min && c.Id < max).ToList<Car>();
foreach (var car in cars)
{
SpareTire st = new SpareTire();
st.BrandName = "Cool Tire";
car.SpareTire = st;
}
try
{
db.SaveChanges();
}
catch (OptimisticConcurrencyException exc)
{
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, cars); // It doesn't seem to make a difference if I use RefreshMode.ClientWins
db.SaveChanges();
}
catch (Exception)
{
}
}
done2 = true;
}
}
private static void AddSpareTires3(object o)
{
using (EfTestContainer db = new EfTestContainer())
{
for (int i = 1; i < 10; i++)
{
int min = (i - 1) * 100;
int max = i * 100;
User u = db.Users.FirstOrDefault();
var cars = db.Cars.Where(c => c.Id > min && c.Id < max).ToList<Car>();
foreach (var car in cars)
{
SpareTire st = new SpareTire();
st.BrandName = "Cool Tire";
car.SpareTire = st;
}
try
{
db.SaveChanges();
}
catch (OptimisticConcurrencyException exc)
{
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
objectContext.Refresh(RefreshMode.StoreWins, cars); // It doesn't seem to make a difference if I use RefreshMode.ClientWins
db.SaveChanges();
}
catch (Exception)
{
}
}
done3 = true;
}
}
结果是有 1 个用户、10,000 辆汽车和 10,899 个备用轮胎!
- 为什么 SQL/EF 允许违反 Car / SpareTire 1 到(零或 1)关系?
- 如何修复我的代码,以便在运行后只存在 1 个用户、10,000 辆汽车和 10,000 个备用轮胎?
非常感谢!