I have the following code and I cannot achieve saving the changes.
The parameter of my method is a string containing the RefCode of a product I want to modify in the database, then the query is pulling the BaseProduct that is supposed to be modified.
(I tried to simplify the code and set it in English, so I have probably introduced some syntactic errors, but in my code in debug mode, I get all the info FROM the DB). Is there something wrong with the "select new" in the Linq query ?
public static void UpdateProduct(ViewProduct productToUpdate)
{
using (var context = new my_Entities())
{
var BaseProduct = (from prod in context.Product
where prod.Ref == productToUpdate.BaseProduct.RefPrd
select new ViewBaseProduct
{
RefPrd = prod.Ref,
DescrPrd = prod.DescrPrd,
NormeCe = (bool)prod.NormeCE
}).FirstOrDefault();
if (BaseProduct != null)
{
//BaseProduct.NormeCe = false;
BaseProduct = productToUpdate.BaseProduct;
context.SaveChanges();
}
}
}
But BaseProduct is a ViewBaseProduct object, is ViewBaseProduct a entity class? It seems it is a ViewModel class.
You have to get de Product entity, modify his fields and savechanges. It seems you only apply changes to the ViewModel class.
Try this:
public static void UpdateProduct(ViewProduct productToUpdate)
{
using (var context = new my_Entities())
{
var BaseProduct = (from prod in context.Product
where prod.Ref == productToUpdate.BaseProduct.RefPrd)
.FirstOrDefault();
if (BaseProduct != null)
{
//BaseProduct.NormeCe = false;
BaseProduct.field1 = productToUpdate.BaseProduct.field1;
BaseProduct.field2 = productToUpdate.BaseProduct.field2;
//update the necesary fields
//......
context.SaveChanges();
}
}
}
This won't work that way. You should use the CurrentValues.SetValues() method:
contexte.Entry(BaseProduct).CurrentValues.SetValues(productToUpdate.BaseProduct);
I think you have to Try this
public static void UpdateProduct(ViewProduct productToUpdate)
{
using (var contexte = new my_Entities())
{
var BaseProduct = (from prod in contexte.Product
where prod.Ref == productToUpdate.BaseProduct.RefPrd
select new ViewBaseProduct
{
RefPrd = prod.Ref,
DescrPrd = prod.DescrPrd,
NormeCe = (bool)prod.NormeCE
}).FirstOrDefault();
if (BaseProduct != null)
{
BaseProduct.BaseProduct.RefPrd=productToUpdate.BaseProduct.RefPrd
BaseProduct.BaseProduct.DescrPrd=productToUpdate.BaseProduct.DescrPrd
BaseProduct.BaseProduct.NormeCE==(bool)productToUpdate.BaseProduct.NormeCE
contexte.SaveChanges();
}
}
}
Related
If I retrieve an object from the database using Linq to SQL in one method using an instance of DataContext, which closes on exit of that method, can I edit the object in a different method and with a different DataContext and have the changes take effect in the database?
i.e. Would something like the below work?
public void Foo()
{
using (var db = new DataContext())
{
Bar a = this.GetBar();
if (a != null)
{
a.Property1 = true;
db.SubmitChanges();
}
}
}
private Bar GetBar(string val)
{
using (var db = new DataContext())
{
return db.FirstOrDefault(x => x.Property2 == val);
}
}
There should be some kind of Attach method
Something like:
public void Foo()
{
using (var db = new DataContext())
{
Bar a = this.GetBar();
if (a != null)
{
db.Bars.Attach(a);
a.Property1 = true;
db.SubmitChanges();
}
}
}
I'm trying to make a master-detail Web Form working with Entity Framework and performing insert and update on the same page. I'm new at EF, so I must be making a lot of mistakes here. Can you help me pointing me what's the best practices to perform insert/update on EF? What am I doing wrong here?
In this code, the "New" mode works well, but the "Edit" mode gets this error: "An entity object cannot be referenced by multiple instances of 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();
}
}
}
}
}
Here is how you should approach it.
It would be best if you abstract the DbContext away, with this simple interface:
public interface IDataRepository : IDisposable
{
IDbSet<Order> Orders { get; set; }
void Save();
}
Of course, your implementation of IDataRepository is based on EntityFramework. Note that you will need to have a dataRepositoryConnection connection string in your web.config file:
public class EfDataRepository : DbContext, IDataRepository
{
public EfDataRepository() : base("dataRepositoryConnection")
{
}
public IDbSet<Order> Orders { get; set; }
public void Save()
{
this.SaveChanges();
}
}
In my experience, you also need a 'factory', which gives you a new instance of the data repository. This allows you to be the 'owner' of the instance, and you can safely dispose it. Note that the interaction with the DataContext should be minimal - you do your Unity of Work and get rid of it. Don't reuse! You will see it as an example below.
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);
}
}
In your controller (if it were MVC app), or Page backend (forms), it would be best if you use Microsoft Unity to get an instance of DataRepositoryFactory. For now, a manual construction would suffice too.
IDataRepository dataRepository = new EfDataRepository();
var dataRepositoryFactory = new DataRepositoryFactory<IDataRepository>(dataRepository);
Also, you don't need all this Transaction/Commit stuff you have put. It should be transparent for you. EF supports it implicitly, you don't have to be explicit about it.
// 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();
}
Also, I am pretty sure you have setup the relation between Order and OrderDetails classes incorrectly, in your EF code-first approach.
This is just wrong:
OrderDetails newOrderDetails = new OrderDetails();
newOrderDetails.OrderNumer = maxOrderNumber;
If you post them here, I can fix them for you.
I have stumbled upon a problem with Entity Framework this morning.
I have following code mapping a modified entity and saving it into database.
public Group Save(Group x)
{
using (var db = new HostContext())
{
db.Projects.Attach(x.Project);
if (x.ID != 0)
{
db.AttachableObjects.Attach(x);
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeObjectState(x, EntityState.Modified);
}
else
{
db.AttachableObjects.Add(x);
}
db.SaveChanges();
return x;
}
}
I call Save method with existing group as a parameter. Group contains one user I want to add as a member.
The method finishes successfully, however the relationship is not persisted in database.
Any help is very appreciated.
EDIT: These are my classes
class User : AttachableObject
{
...
private List<Group> memberof;
[DataMember]
[InverseProperty("Members")]
public List<Group> MemberOf
{
get { return memberof; }
set { memberof = value; }
}
...
}
class Group : AttachableObject
{
...
private List<User> members;
[DataMember]
[InverseProperty("MemberOf")]
public List<User> Members
{
get { return members; }
set { members = value; }
}
...
}
EDIT2: This is where the Save method is called
public Group AcceptInvite(int id)
{
var mapper = new InviteMapper();
var userMapper = new UserMapper();
var groupMapper = new GroupMapper();
var invite = mapper.Find(id);
if (invite != null)
{
var group = groupMapper.Find(invite.GroupID);
var user = userMapper.Find(invite.InviteeID);
group.Members.Add(user);
mapper.Delete(invite.ID);
return groupMapper.Save(group);
}
return null;
}
EDIT3: My mappers
public class GroupMapper
{
public Group Find(int id)
{
using (var db = new HostContext())
{
return db.AttachableObjects
.Include("Project")
.OfType<Group>().FirstOrDefault(x => x.ID == id);
}
}
}
The rest of the mappers is the same, only using their own tables.
You are not changing the relationship info of Project, you are only setting x to modified, relationship info must be changed explicitly.
So x.Project must have some property that points back to Group, you need to set it so the change is recorded.
I am guessing that x is resurrected via some deserialization process?
I'm doing some complex modifications to our EF model (we're using code-first) and I know it should affect my database in particular and testable ways; for instance, I should be able to assert;
The db will have an 'Item' table
'Item' will have an integer 'Id' field which is the primary key
'Item' will have a 50-character string 'Name' field
These are ripe for unit testing, but I can't find a way to test these kinds of assertion.
What I'd like is a way to get the database model from the DbContext, so that I can do something like;
var model = BuildModel(MyEntityContext);
var itemTable = model.Db.Tables["Item"]
Assert.IsNotNull(itemTable);
Assert.IsTrue(itemTable.Columns["Id"] != null);
Assert.IsTrue(itemTable.Columns["Name"] != null);
Assert.IsTrue(itemTable.Columns["Name"].Length == 50);
...
Does anyone know of a way to get hold of a description of the database structure implied by a DbContext object?
you could start with something like this...see if this takes you anywhere.
using(ObjectContext context = new ObjectContext())
{
var queryResult = from meta in context.MetadataWorkspace.GetItems(DataSpace.CSpace)
.Where(m => m.BuiltInTypeKind == BuiltInTypeKind.EntityType)
from p in (meta as EntityType).Properties
.Where(p => p.DeclaringType.Name == context.GetType().Name
&& p.Name == PropertyName
Select new {Length = p.TypeUsage.Facets["MaxLength"].Value, Name=p.TypeUsage.Facets["Name"].Value, p.TypeUsage.Facets["FacetType"].Value
}
(#steve-cooper) A really helpful start! thanks. I've come up with a working class based closely on your suggestion -- I've stuck it here so I can give you the accepted answer, and so others can see the worked-up code.
public class ModelSummary
{
public Dictionary<string, System.Data.Metadata.Edm.EntityType> Entities { get; private set; }
public static ModelSummary Load(DbContext context)
{
var adapter = (IObjectContextAdapter)context;
var objectContext = adapter.ObjectContext;
var summary = new ModelSummary();
var items = objectContext.MetadataWorkspace.GetItems(DataSpace.SSpace);
summary.Entities =
objectContext.MetadataWorkspace.GetItems(DataSpace.SSpace)
.OfType<EntityType>()
.ToDictionary(et => et.Name);
return summary;
}
public bool EntityExists(string entityName)
{
return this.Entities.ContainsKey(entityName);
}
public bool EntityHasProperty(string entityName, string propertyName)
{
if (!EntityExists(entityName))
{
return false;
}
var entity = this.Entities[entityName];
return entity.Properties.Contains(propertyName);
}
}
And the test looks like this;
[Test]
public void Context_SchemaIsExpected()
{
var summary = ModelSummary.Load(new MyContext());
Assert.IsTrue(summary.EntityExists("Item"));
Assert.IsTrue(summary.EntityHasProperty("Item", "Id"));
}
i have a simple relationship m:m between classes Thing and People but my solution when going to update a record need to delete previous record and then create the new
Example:
My tables
People (PeopleId, Name)
Thing (ThingId, Name)
PeopleHasThing (PeopleId, ThingId)
My Model
PeopleModel.cs
// The following method works!
// I've tried but I have my doubts about deleting records
public static void Update(PeopleModel p)
{
using (_context = new myDataContext())
{
var result = (from r in _context.People
where r.PeopleId == p.PeopleId
select r).SingleOrDefault();
if (null == result) return;
result.Name = p.Name;
PeopleHasThing.DeleteAllByPeopleId(result.PeopleId);
EntitySet<PeopleHasThing> set = new EntitySet<PeopleHasThing>();
//HasThing = List<ThingModel>
//ThingModel: { (Int32)ThingId, (bool)IsMarked. (string)Description }
m.HasThing.ForEach(e =>
{
if (e.IsMarked)
{
set.Add(new PeopleHasThing
{
ThingId = e.ThingId,
People = result
});
}
});
result.PeopleHasThing = set;
_context.SubmitChanges();
}
}
ahd the question is How to update correctly an M:N relationship?