Attaching several existing modified entitities to the context - c#

What is the best way to do something like this:
var existingEntities = new []
{
new Product {Name = "Name1", Count = 10},
new Product {Name = "Name2", Count = 20}
};
using (var context = new ProductContext())
{
// attach several entities
context.SaveChanges();
}
Should I iterate every entity and set it to State = EntityState.Modified?

Yes, iterating and setting
context.Entry(existingproduct).State = EntityState.Modified;
is the way to go.
Source: https://msdn.microsoft.com/en-us/data/jj592676.aspx

Related

Entity Framework 6 Many to many

I'm trying to learn to use Entity Framework and am working through the Entity Framework Recipes 6 Book (not even going to try and hide that).
Working on 2-4:
3 tables:
Order: OrderID, OrderDate, (Nav Properties OrderItems)
OrderItem: OrderId, SKU, Count (Nav Properties, Item, Order)
Item: SKU, Description, Price (Nav Properties, OrderItems)
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
item = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
item = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
context.Orders.Add(order);
context.SaveChanges();
}
The only thing that gets added to the database is the order and nothing else, none of the items, etc... I had a problem with the previous example and had to add the "other" items individually but I thought that point was that you could just do the one "Add" and it would add all the objects to the Database?
Thanks in advance!
UPDATEOk I made the following changes based on the suggestions below and now it's working
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
var item = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
item = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
oi = new OrderItem { Order = order, Item = item, Count = 3 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
item = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi); // suggestion from Stackoverflow.
context.Orders.Add(order);
context.SaveChanges();
}
At least now I know what to change in their code going forward to get it working. Thanks!!
You never actually add the OrderItems to the DbContext collection.
There is also a problem with your code, which means you are overwriting your items and oi variable values multiple times.
The below code makes some assumptions, since you haven't supplied more of your code, but should be easy to adapt.
using (var context = new EFRecipesContext()) {
var order = new Order { OrderId = 1, OrderDate = new DateTime(2010, 1, 18) };
context.Orders.Add(order);
var item1 = new Item { SKU = 1729, Description = "Backpack", Price = 29.97M };
var oi1 = new OrderItem { Order = order, Item = item, Count = 1 };
context.OrderItems.Add(oi1);
var item2 = new Item { SKU = 2929, Description = "Water Filter", Price = 13.97M };
var oi2 = new OrderItem { Order = order, Item = item, Count = 3 };
context.OrderItems.Add(oi2);
var item3 = new Item { SKU = 1847, Description = "Camp Stove", Price = 43.99M };
var oi3 = new OrderItem { Order = order, Item = item, Count = 1 };
context.OrderItems.Add(oi3);
context.SaveChanges();
}
Unless you are using the variables again, you could do the definition of the objects inline like this aswell, which is slightly less repetitive:
context.OrderItems.Add(new OrderItem {
Order = order,
Item = new Item {
SKU = 1729,
Description = "Backpack",
Price = 29.97M
},
Count = 1
});
This is not really about many to many, but about the way how EF discovers related data.
You need to explicitely add the entry to the database, that is actually referencing the related data. In your case, this is oi, so if you add it
context.OrderItems.Add(oi);
context.SaveChanges();
the related io.Order and io.Item will be considered by EF. But EF has no way of knowing about this relation when you only provide order without providing navigation information from order to io.
Another way of solving the issue (instead of adding io explicitely) would be to update order.OrderItems before saving:
oi = new OrderItem { Order = order, Item = item, Count = 1 };
order.OrderItems.Add(oi);
context.Orders.Add(order);
context.SaveChanges();
You must add an entry to the database to be invoked on appropriate data. In this case, it's the OI.

How to use RemoveRange with 1-query-approach Entity Framework 6

I look for a way of executing RemoveRange() with 1-query-approach. With Remove() method it works like this:
public void Delete()
{
Record record = new Record() {
id = 1,
value = 5
};
using(SomeContext ctx = new SomeContext()) {
ctx.Records.Entry(record).State = EntityState.Deleted;
ctx.SaveChanges();
}
}
But the same approach doesn't work for RemoveRange(). EF documentation says that the method sets each entity to EntityState.Deleted. If it would be so as I understand it - this would work:
public void DeleteAll()
{
List<Record> records = new List<Record>() {
new Record() { id = 1, value = 5 },
new Record() { id = 2, value = 10 }
};
using(SomeContext ctx = new SomeContext()) {
ctx.Records.RemoveRange(records);
ctx.SaveChanges();
}
}
because this works:
public void DeleteAll()
{
List<Record> records = new List<Record>() {
new Record() { id = 1, value = 5 },
new Record() { id = 2, value = 10 }
};
using(SomeContext ctx = new SomeContext()) {
foreach(var item in records)
{
ctx.Entry(item).State = EntityState.Deleted;
}
ctx.SaveChanges();
}
}
But it doesn't and throws the exception
The object cannot be deleted because it was not found in the ObjectStateManager.
Can I use this method without retrieving all of them from database via separate query?
When you use ctx.Entry(item) it adds this item to the context tracking and then you can edit it. With RemoveRange it does not implicitly add to the data context first and that is why you get the exception.
Try using AttachRange before RemoveRange
Edit:
Alternative way to do it. This is a bit how RemoveRange does it behind the scenes. It first disables AutoDetectChanges, removes what it should and then calls DetectChanges. Could wrap this in an extension method to make it a one liner again.
public void DeleteAll()
{
List<Record> records = new List<Record>() {
new Record() { id = 1, value = 5 },
new Record() { id = 2, value = 10 }
};
using(SomeContext ctx = new SomeContext()) {
ctx.Configuration.AutoDetectChangesEnabled = false;
foreach(var item in records)
{
ctx.Entry(item).State = EntityState.Deleted;
}
ctx.ChangeTracker.DetectChanges();
ctx.SaveChanges();
}
}
As I found your entity-collection is detached(as you creating it there) so objectContext not tracking it. You may need to attach it. you can do this as below:
//Find all records in database with an id that is in your record collection
var recordsToBeDeleted = ctx.Records.Where(dbr => records.Any(r => r.id == dbr.id));
ctx.Records.RemoveRange(recordsToBeDeleted);
context.SaveChanges();
Solution taken from this thread

delete multiple entities in disconnected mode

I am working on an application (EF6 Code First approach) that is interacting with a entity/table FloorPlan and has 10 records. I want to delete first 6 records as those are obsolete with new business requirements. Here's how the table currently looks:
To delete this in the Code First approach, I tried following code in disconnected state:
using (var newdbContext = new HomItUpContext())
{
var floorPlansOld = new List<FloorPlan>
{
new FloorPlan { Name = "Unitech Uniworld Gardens 2", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/unitech_uniworld_gardens_2/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() },
new FloorPlan { Name = "Vatika Seven Lamps", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/vatika_seven_lamps/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() },
new FloorPlan { Name = "Bestech Park View Spa", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/bestech_park_view_spa/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() },
new FloorPlan { Name = "Imperia Esfera", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/imperia_esfera/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() },
new FloorPlan { Name = "Raheja Vedas", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/raheja_vedas/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() },
new FloorPlan { Name = "Tulip Violet Grandeur", MainImageUrl = "//cdn.homitup.com/resources/featured-ideas/floor-plans/tulip_violet_grandeur/profile.jpg", IsActive = true, FloorPlanIdeas = new List<FloorPlanIdea>() }
};
floorPlansOld.ForEach(a => newdbContext.FloorPlan.Remove(a));
floorPlansOld.ForEach(a => newdbContext.Entry(a).State = System.Data.Entity.EntityState.Deleted);
newdbContext.SaveChanges();
};
When I run update-database command via package manager console, I get following error:
The object cannot be deleted because it was not found in the ObjectStateManager.
I have also tried without changing the state of the entities but to no avail. I only want to do it in disconnected mode. Can you guys throws some pointers around this problem?
If you want to delete those records the only you need to do is create your entity instances with their existing Ids.
using (var newdbContext = new HomItUpContext())
{
var floorPlansOld = new List<FloorPlan>
{ //Put here the record's Ids you want to delete
new FloorPlan { Id=1 },
new FloorPlan { Id=2 },
new FloorPlan { Id=3 },
new FloorPlan { Id=4 },
new FloorPlan { Id=5 },
new FloorPlan { Id=6 }
};
newdbContext.RemoveRange(floorPlansOld);// You can use RemoveRange method instead a foreach to call Remove method.
newdbContext.SaveChanges();
};
Update
Well, in that case I suggest you make a query first seeking all the entities you want to delete by their names, and after that you can delete them using the RemoveRange method:
var names=new List<string>(){ "Unitech Uniworld Gardens 2", "Vatika Seven Lamps",...};
var entitiesToDelete=newdbContext.FloorPlan.Where(fp=>names.Contains(fp.Name));
newdbContext.RemoveRange(entitiesToDelete);
newdbContext.SaveChanges();
You are removing object from newdbContext.FloorPlan, buy you take them from floorPlansOld.
It looks completely wrong to me.
Try this
var a = newdbContext.FloorPlan.First();
newdbContext.FloorPlan.Remove(a);

Get Identity of all objects after SaveChanges

I am inserting objects at the same time using Entity framework like below.
context = new MyContext();
foreach (var x in lstX)
{
var abc = new abc{ name= x.abcName };
context.abcs.Add(abc);
var xyz = new xyz{ name = x.xyzName };
context.xyzs.Add(xyz);
}
context.SaveChanges();
Is it possible get the identity of all these inserted objects?
When you call SaveChanges, the Identity field is populated on the original entity. So to obtain this id, simply store a reference to the identity and access it after SaveChanges:
context = new MyContext();
List<abc> addedABCs = new List<abc>();
List<xyz> addedXYZs = new List<xyz>();
foreach (var x in lstX)
{
var abc = new abc{ name= x.abcName };
addedABCs.Add(abc);
context.abcs.Add(abc);
var xyz = new xyz{ name = x.xyzName };
addedXYZs.Add(xyz);
context.xyzs.Add(xyz);
}
context.SaveChanges();
foreach (var abc in addedABCs)
{
Console.WriteLine("Added item with ID {0}", abc.Id);
}

an object with the same key already exists in the object statemanager When trying to add the fallowing data Using EF

I have the Relational Database as shown below on the EDM. When I am trying to submit a new F_Orden with two existing F_Producto_ProductoCompra and which has an exisitng FDistributor I get the Error mention on the title. What is the right way to do this? Please any help would really appreciated.
using (var context = new DryTypeEntities())
{
//**************************************
//Insert Order
FDistributor distributor1 = context.FDistributors.FirstOrDefault(p => p.Name == "Dverdesoto");
F_Producto_ProductoCompra productocompra1 = new F_Producto_ProductoCompra
{
FechaExpiracon = "3/13/2015",
CostoCompra = "1",
DescuentoCompra = "10%",
Id = 2
};
F_Producto_ProductoCompra productocompra2 = new F_Producto_ProductoCompra
{
FechaExpiracon = "3/13/2015",
CostoCompra = "1",
DescuentoCompra = "10%",
Id = 3
};
F_Orden orden1 = new F_Orden
{
Fecha = DateTime.Now,
Total = 12,
};
orden1.F_Producto_ProductoCompra.Add(productocompra1);
orden1.F_Producto_ProductoCompra.Add(productocompra2);
distributor1.F_Orden.Add(orden1);
context.F_Orden.AddObject(orden1);
context.FDistributors.AddObject(distributor1);
context.SaveChanges();
}
Why are you setting the value of Id manually?
I believe that identifier in table [F_Producto_ProductoCompra] is Id INT IDENTITY (1,1) PRIMARY KEY.
So there could be a collision with identifiers.
May be the row with the same Id really exists.

Categories

Resources