我正在尝试使用Entity Framework制作一个主从式Web窗体,并在同一页面上执行插入和更新。我是EF的新手,所以我必须在这里犯很多错误。你能帮我指点一下在EF上执行插入/更新的最佳实践吗?我在这做错了什么?
在此代码中,“新建”模式运行良好,但“编辑”模式会出现此错误:“实体对象不能被多个IEntityChangeTracker实例引用”。
OrdersEntities ordersEntities = new OrdersEntities();
private Order myOrder
{
get { return (Order)Session["myOrder"]; }
set { Session["myOrder"] = value; }
}
public DataTable dtOrderDetails
{
get { return (DataTable)ViewState["dtOrderDetails"]; }
set { ViewState["dtOrderDetails"] = value; }
}
private string Mode
{
get { return (string)ViewState["mode"]; }
set { ViewState["_modo"] = value; }
}
private void btnSaveOrder_Click(object sender, EventArgs e)
{
if (dtOrderDetails.Rows.Count > 0)
{
using (ordersEntities)
{
using (var contextTransaction = ordersEntities.Database.BeginTransaction())
{
try
{
if (Mode == "New")
{
Order newOrder = new Order();
OrderDetails newOrderDetails;
int maxOrderNumber = ordersEntities.Order.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
ordersEntities.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(newOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = myOrder;
OrderDetails editedOrderDetails;
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
ordersEntities.Order.Attach(editedOrder);
ordersEntities.Entry(editedOrder).State = System.Data.Entity.EntityState.Modified;
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
ordersEntities.OrderDetails.Add(editedOrderDetails);
}
ordersEntities.SaveChanges();
contextTransaction.Commit();
}
}
catch (Exception ex)
{
contextTransaction.Rollback();
}
}
}
}
}
答案 0 :(得分:2)
以下是你应该如何处理它。
最好用这个简单的界面抽象DbContext:
public interface IDataRepository : IDisposable
{
IDbSet<Order> Orders { get; set; }
void Save();
}
当然,您IDataRepository
的实施基于EntityFramework。请注意,您需要在web.config文件中包含dataRepositoryConnection
连接字符串:
public class EfDataRepository : DbContext, IDataRepository
{
public EfDataRepository() : base("dataRepositoryConnection")
{
}
public IDbSet<Order> Orders { get; set; }
public void Save()
{
this.SaveChanges();
}
}
根据我的经验,您还需要一个工厂&#39;,它为您提供数据存储库的新实例。这使您成为&#39;所有者&#39;实例,你可以安全地处理它。请注意,与DataContext的交互应该是最小的 - 您可以完成Unity of Work并摆脱它。不要重复使用!您将在下面看到它。
public class DataRepositoryFactory<T> where T : IDataRepository
{
private Type dataRepositoryImplementationType;
public DataRepositoryFactory(T dataRepositoryImplementation)
{
if (dataRepositoryImplementation == null)
{
throw new ArgumentException("dataRepositoryImplementation");
}
this.dataRepositoryImplementationType = dataRepositoryImplementation.GetType();
}
public T Create()
{
return (T)Activator.CreateInstance(this.dataRepositoryImplementationType);
}
}
在您的控制器(如果它是MVC应用程序)或Page后端(表单)中,最好使用Microsoft Unity来获取DataRepositoryFactory
的实例。目前,手动构造也足够了。
IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);
此外,您不需要放置所有这些交易/提交内容。它对你来说应该是透明的。 EF隐含地支持它,你不必明确它。
// See, now you are the 'owner' of the dataRepository
using (var dataRepository = this.dataRepositoryFactory.Create())
{
if (Mode == "New")
{
Order newOrder = new Order();
// This doesn't make sense. Either generate a random order number (e.g. a Guid), or just use the Order.Id as an order number, although I don't recommend it.
int maxOrderNumber = dataRepository.Orders.Select(o => o.OrderNumber).DefaultIfEmpty(0).Max();
maxOrderNumber++;
newOrder.OrderNumber = maxOrderNumber;
newOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
newOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
newOrder.Status = 1;
dataRepository.Orders.Add(newOrder);
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
newOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
newOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
newOrder.OrderDetails.Add(newOrderDetails);
}
myOrder = newOrder;
}
if (Mode == "Edit")
{
Order editedOrder = dataRepository.Orders.FirstOrDefault(o => o.Id == myOrder.Id);
editedOrder.Date = DateTime.ParseExact(txtOrderDate.Text, "dd/MM/yyyy", CultureInfo.InvariantCulture);
editedOrder.CustomerID = Convert.ToInt32(ddlCustomer.SelectedValue);
editedOrder.OrderDetails.Clear();
foreach (DataRow dt in dtOrderDetails.Rows)
{
OrderDetails editedOrderDetails = new OrderDetails();
editedOrderDetails.OrderNumer = editedOrder.OrderNumber;
editedOrderDetails.ProductId = Convert.ToInt32(dt["ProductId"]);
editedOrderDetails.Quantity = Convert.ToInt32(dt["Quantity"]);
editedOrder.OrderDetails.Add(editedOrderDetails);
}
}
dataRepository.Save();
}
另外,我很确定您在EF代码优先方法中错误地设置了Order
和OrderDetails
类之间的关系。
这是错的:
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
如果你在这里发帖,我可以帮你解决。