Entity Framework with mapping table adding duplicate entries - entity-framework

I have two tables customer and products with many to many relationship which I handle with mapping table called customerproductmap. I then created entity framework model from this database.
This is what I have for onmodelcreation for the customer:
modelBuilder.Entity<customer>()
.HasMany(e => e.products)
.WithMany(e => e.customers)
.Map(m => m.ToTable("customerproductMap").MapLeftKey("customerId").MapRightKey("productId"));
When I add new data using Context as follows, I see that duplicate entries are being added to customer. The json data I get from the client has parent object with multiple customers. Each customer can have multiple products mapped to it, but these products might have been already added by the previous customer. For e.g. customer1-> products1 and product2 and customer2->Product1. This results in addition of Product1 twice and then these are mapped in the mapping table.
What I want is Product1 is added only once and then mapped to customer1 and customer2 since it’s the same Product. How can I achieve this?
using (var context = new DbEntity())
{
context.ParentObject.Add(GetEntityFromClientData((clientdata)));
try
{
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
.. processing of exception message
}
}
I am new to entity framework, so it might be a simple thing I am missing.
EDIT:
Here is what GetEntityFromClientData does (the relevant parts). You see that I am mapping product1 and product2 to customer1 and product1 to customer2. Then add the region to the database. If I dont then how will I map customer2 to product1.
var customers = new List<Customer>();
foreach (var clientCustomer in clientData.Customers)
{
var q = from o in clientCustomer.Products
select new Product()
{
..basic initialization,
};
var customer = new Customer()
{
Name = clientCustomer.Name,
Products = q.ToArray(),
CustomerId = clientCustomer.Id,
};
customers.Add(customer);
}
var customerLocation = new CustomerLocation ()
{
city = clientData.City,
Customers= customers.ToArray(),
};
var customerLocations = new CustomerLocation []
{
customerLocation
};
var region = new Region()
{
CustomerLocations = customerLocations,
};
return region;

Related

Order By in Linq to Entities

I had sql table with two columns sizename and orderof . I want to select from that table all the sizenames but in ascending order of the orderof .Iam using EF6 and Linq to Entities
I had used the Query Like this .But its not working(sorting)
var sizedetails = (from size in enty.StyleSizes
where size.OurStyleID == ourstyleid
orderby size.Orderof
select new
{
size.SizeName
}).Distinct();
//var sizedetails = enty .StyleSizes.Where(u => u.OurStyleID == ourstyleid).Select(u => u.SizeName ).Distinct();
foreach (var sizedet in sizedetails)
{
dt.Columns.Add(sizedet.SizeName.Trim(), typeof(String));
}
I know this may be already asked. But none of the solution provided in those questions working for me
Since LINQ to Entities translates your query to SQL, ordering before Distinct has no effect. And the problem is that after Distinct you have no access to the property needed for ordering.
So you need an alternative way, which luckily is the GroupBy method - its similar to Distinct but allows you to access the properties of the elements sharing the same key. Thus you can order the result based on some aggregates (in your case looks like Min is the appropriate):
var sizedetails = from size in enty.StyleSizes
where size.OurStyleID == ourstyleid
group size by size.SizeName into sizeGroup
orderby sizeGroup.Min(size => size.Orderof)
select new
{
SizeName = sizeGroup.Key
};
I dint tried with DB but with in memory collection it gives be correct result .
here is my class .
class StyleSizes
{
public int Orderof { get; set; }
public string SizeName { get; set; }
public int OurStyleID { get; set; }
}
// logic to ue orderby
var list = new List<StyleSizes> { new StyleSizes { Orderof=2,SizeName="B",OurStyleID=1 },
new StyleSizes { Orderof=11,SizeName="C" ,OurStyleID=2},
new StyleSizes { Orderof=9,SizeName="D" ,OurStyleID=1},
new StyleSizes { Orderof=9,SizeName="D" ,OurStyleID=1},
new StyleSizes { Orderof=3,SizeName="E" ,OurStyleID=1},
new StyleSizes { Orderof=4,SizeName="F" ,OurStyleID=1}
};
var orderList = list.Where(x=>x.OurStyleID==1).OrderBy(x => x.Orderof).Select(c => new { c.SizeName }).Distinct();

Clear related items followed by parent entity update - Many to Many EntityFramework with AutoMapper

I am trying to remove all references followed by adding them back from a list of disconnected objects.
using(var scope = new TransactionScope())
{
_autoIncidentService.AddNewCompanyVehicles(
autoIncidentModel.CompanyVehiclesInvolved.Where(v => v.Id == 0));
_autoIncidentService.ClearCollections(autoIncidentModel.Id);
_autoIncidentService.Update(autoIncidentModel);
scope.Complete();
}
return Json(ResponseView);
The ClearCollections removes items references. The GetAutoIncident includes the collection.
public void ClearCollections(int id)
{
var autoIncident = GetAutoIncident(id);
foreach (var vehicle in autoIncident.CompanyVehiclesInvolved.ToArray())
{
autoIncident.CompanyVehiclesInvolved.Remove(vehicle);
}
db.SaveChanges();
}
When I try to update the entity right after the ClearCollections method it fails.
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
I am using a singleton to get the DbContext so there shouldn't be any situation where the context is different. It is being stored in the HttpContext.Current.Items.
The update method is as follows:
public override void Update(AutoIncidentModel model)
{
var data = GetData(model.Id);
Mapper.CreateMap<AutoIncidentModel, AutoIncident>()
.ForMember(m => m.CompanyVehiclesInvolved, opt => opt.ResolveUsing(m =>
{
var ids = m.CompanyVehiclesInvolved.Select(v => v.Id);
return db.Vehicles.Where(v => ids.Contains(v.Id)).ToList();
}));
Mapper.Map(model, data);
db.SaveChanges();
}
Obviously, I am missing something important here. Do the entites from my ResolveUsing method need to somehow be associated with the parent entity or is automapper overwriting the property (CompanyVehiclesInvolved) and causing a problem?

How can I update my DTO's ID when inserting multiple new entities

I'm using EF4. I'm adding a series of new entities from a list of DTOs, and I'm not saving changes until after all of them are added. I'm wanting to set the IDs of the DTOs to what the new entities' IDs are. How on earth do I do this? Does EF provide a mechanism for this?
With a single entity I would do this:
public void InsertMyDto(MyDto a_dto)
{
var newEntity = new MyEntity
{
Name = a_dto.Name,
Type = a_dto.Type.ToString(),
Price = a_dto.Price
};
_dataContext.MyEntities.AddObject(newEntity);
_dataContext.SaveChanges();
a_dto.ID = newEntity.ID;
}
This works fine, but what do I do in this case?
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
// ???
}
I want to save all at once, because I have validation work (not shown above) that is done against the database and fails before it gets to SaveChanges, and if it fails I want it to fail as a whole transaction (i.e. rollback).
I don't think that EF can help you here. It even can't help you for a single instance which forces you to write a_dto.ID = newEntity.ID. The counterpart of this code for multiple entites is to keep track of the pairs of dtos and new entities:
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
Dictionary<MyDto, MyEntity> dict = new Dictionary<MyDto, MyEntity>();
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
dict.Add(myDto, newEntity);
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
foreach (var item in dict)
item.Key.ID = item.Value.ID; // Key is MyDto, Value is MyEntity
}

How to seed data with many-to-may relations in Entity Framework Migrations

I use entity framework migration (in Automatic migration mode). Everything is okay, but I have one question:
How should I seed data when I have many-to-many relationships?
For example, I have two model classes:
public class Parcel
{
public int Id { get; set; }
public string Description { get; set; }
public double Weight { get; set; }
public virtual ICollection<BuyingItem> Items { get; set; }
}
public class BuyingItem
{
public int Id { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Parcel> Parcels { get; set; }
}
I understand how to seed simple data (for PaymentSystem class) and one-to-many relationships, but what code should I write in the Seed method to generate some instances of Parcel and BuyingItem? I mean using DbContext.AddOrUpdate(), because I don't want to duplicate data every time I run Update-Database.
protected override void Seed(ParcelDbContext context)
{
context.AddOrUpdate(ps => ps.Id,
new PaymentSystem { Id = 1, Name = "Visa" },
new PaymentSystem { Id = 2, Name = "PayPal" },
new PaymentSystem { Id = 3, Name = "Cash" });
}
protected override void Seed(Context context)
{
base.Seed(context);
// This will create Parcel, BuyingItems and relations only once
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
This code creates Parcel, BuyingItems and their relationship, but if I need the same BuyingItem in another Parcel (they have a many-to-many relationship) and I repeat this code for the second parcel - it will duplicate BuyingItems in the database (though I set the same Ids).
Example:
protected override void Seed(Context context)
{
base.Seed(context);
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.AddOrUpdate(new Parcel()
{
Id = 2,
Description = "Test2",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
How can I add the same BuyingItem in different Parcels?
Updated Answer
Make sure you read "Using AddOrUpdate Properly" section below for a complete answer.
First of all, let's create a composite primary key (consisting of parcel id and item id) to eliminate duplicates. Add the following method in the DbContext class:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Parcel>()
.HasMany(p => p.Items)
.WithMany(r => r.Parcels)
.Map(m =>
{
m.ToTable("ParcelItems");
m.MapLeftKey("ParcelId");
m.MapRightKey("BuyingItemId");
});
}
Then implement the Seed method like so:
protected override void Seed(Context context)
{
context.Parcels.AddOrUpdate(p => p.Id,
new Parcel { Id = 1, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 2, Description = "Parcel 2", Weight = 2.0 },
new Parcel { Id = 3, Description = "Parcel 3", Weight = 3.0 });
context.BuyingItems.AddOrUpdate(b => b.Id,
new BuyingItem { Id = 1, Price = 10m },
new BuyingItem { Id = 2, Price = 20m });
// Make sure that the above entities are created in the database
context.SaveChanges();
var p1 = context.Parcels.Find(1);
// Uncomment the following line if you are not using lazy loading.
//context.Entry(p1).Collection(p => p.Items).Load();
var p2 = context.Parcels.Find(2);
// Uncomment the following line if you are not using lazy loading.
//context.Entry(p2).Collection(p => p.Items).Load();
var i1 = context.BuyingItems.Find(1);
var i2 = context.BuyingItems.Find(2);
p1.Items.Add(i1);
p1.Items.Add(i2);
// Uncomment to test whether this fails or not, it will work, and guess what, no duplicates!!!
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
p2.Items.Add(i1);
p2.Items.Add(i2);
// The following WON'T work, since we're assigning a new collection, it'll try to insert duplicate values only to fail.
//p1.Items = new[] { i1, i2 };
//p2.Items = new[] { i2 };
}
Here we make sure that the entities are created/updated in the database by calling context.SaveChanges() within the Seed method. After that, we retrieve the required parcel and buying item objects using context. Thereafter we use the Items property (which is a collection) on the Parcel objects to add BuyingItem as we please.
Please note, no matter how many times we call the Add method using the same item object, we don't end up with primary key violation. That is because EF internally uses HashSet<T> to manage the Parcel.Items collection. A HashSet<Item>, by its nature, won't let you add duplicate items.
Moreover, if you somehow manage to circumvent this EF behavior as I have demonstrated in the example, our primary key won't let the duplicates in.
Using AddOrUpdate Properly
When you use a typical Id field (int, identity) as an identifier expression with AddOrUpdate method, you should exercise caution.
In this instance, if you manually delete one of the rows from the Parcel table, you'll end up creating duplicates every time you run the Seed method (even with the updated Seed method I have provided above).
Consider the following code,
context.Parcels.AddOrUpdate(p => p.Id,
new Parcel { Id = 1, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 2, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 3, Description = "Parcel 1", Weight = 1.0 }
);
Technically (considering the surrogate Id here), the rows are unique, but from the end-user point of view, they are duplicates.
The true solution here is to use the Description field as an identifier expression. Add this attribute to the Description property of the Parcel class to make it unique: [MaxLength(255), Index(IsUnique=true)]. Update the following snippets in the Seed method:
context.Parcels.AddOrUpdate(p => p.Description,
new Parcel { Description = "Parcel 1", Weight = 1.0 },
new Parcel { Description = "Parcel 2", Weight = 2.0 },
new Parcel { Description = "Parcel 3", Weight = 3.0 });
// Make sure that the above entities are created in the database
context.SaveChanges();
var p1 = context.Parcels.Single(p => p.Description == "Parcel 1");
Note, I'm not using the Id field as EF is going to ignore it while inserting rows. And we are using Description to retrieve the correct parcel object, no matter what Id value is.
Old Answer
I would like to add a few observations here:
Using Id is probably not going to do any good if the Id column is a database generated field. EF is going to ignore it.
This method seems to be working fine when the Seed method is run once. It won't create any duplicates, however, if you run it for a second time (and most of us have to do that often), it may inject duplicates. In my case it did.
This tutorial by Tom Dykstra showed me the right way of doing it. It works because we don't take anything for granted. We don't specify IDs. Instead, we query the context by known unique keys and add related entities (which again are acquired by querying context) to them. It worked like a charm in my case.
You must fill many-to-many relation in the same way as you build many-to-many relation in any EF code:
protected override void Seed(Context context)
{
base.Seed(context);
// This will create Parcel, BuyingItems and relations only once
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
Specifying Id which will be used in database is crucial otherwise each Update-Database will create new records.
AddOrUpdate doesn't support changing relations in any way so you cannot use it to add or remove relations in next migration. If you need it you must manually remove relation by loading Parcel with BuyingItems and calling Remove or Add on navigation collection to break or add new relation.
Ok. I understand how I should be in that situation:
protected override void Seed(Context context)
{
base.Seed(context);
var buyingItems = new[]
{
new BuyingItem
{
Id = 1,
Price = 10m
},
new BuyingItem
{
Id = 2,
Price = 20m,
}
}
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
buyingItems[0],
buyingItems[1]
}
},
new Parcel()
{
Id = 2,
Description = "Test2",
Items = new List<BuyingItem>
{
buyingItems[0],
buyingItems[1]
}
});
context.SaveChanges();
}
There are no duplicates in database.
Thank you, Ladislav, you gave me a right vector to find a solution for my task.

Inserting new record into linking table with linq to entities with POCO

I have a Team table and a Player table in many to many relationship. There is a linking table called TeamOnPlayer. EF with POCO generates navigation propertie called Person for the Team entity and also generates a nav. prop. called Team for the People entity.
I'm trying to insert a new record into the TeamOnPlayer table, but EF and POCO hides it. I tried to do this:
public static void AddPersonToTeam(int TeamId, int PersonId)
{
using (var ef = new korfballReportEntities())
{
var team = GetTeam(TeamId);
var person = GetPerson(PersonId);
team.Person.Add(person);
person.Team.Add(team);
ef.SaveChanges();
}
}
The GetTeam(TeamId) and GetPerson(PersonId) gets the right team and person:
public static Team GetTeam(int id)
{
using (var ef = new korfballReportEntities())
{
var q = from l in ef.Team
where l.Id == id
select l;
return q.Single();
}
}
public static Person GetPerson(int id)
{
using (var ef = new korfballReportEntities())
{
var query = from p in ef.Person
where p.Id == id
select p;
return query.Single();
}
}
When it tries to call the team.Person.Add(person) it throws an exception:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection." System.Exception {System.ObjectDisposedException}
Can anyone please show me the correct way?
Edit
Now I understand what the problem was, thanks to you. I was a bit confused about the using blocks you included. For example this:
using (var ef = new korfballReportEntities())
{
//switch lazy loading off, only in this single context
ef.Configuration.LazyLoadingEnabled = false;
var repository = new MyRepository(ef);
repository.AddPersonToTeam(int TeamId, int PersonId);
}
Where should I put it?
I've done something else. I simply did this, and it worked fine.
public static void AddPersonToTeam(int TeamId, int PersonId)
{
using (var ef = new korfballReportEntities())
{
var q = from t in ef.Team
where t.Id == TeamId
select t;
var team = q.Single();
var q2 = from p in ef.Person
where p.Id == PersonId
select p;
var person = q2.Single();
try
{
team.Person.Add(person);
person.Team.Add(team);
}
catch (Exception e)
{
}
ef.SaveChanges();
}
}
The only problem is, that i coludn't reuse my GetPerson(int id) and GetTeam(int id) method.
What do you think? Is it okay? Is this an ugly way?
My guess is that you are working with lazy loading - your navigation properties Team.Person and Person.Team are marked as virtual in your entity classes. The result is that your methods GetTeam and GetPerson do not exactly return Team and Person objects but instances of dynamically created proxy classes which are derived from those entities. This dynamic proxy supports lazy loading which means that EF tries to load the navigation collections Team.Person and Person.Team when you access them for the first time. This happens in your AddPersonToTeam method when you call Add on these collections.
Now the problem is that the proxies are created within an context which you immediately dispose in your GetTeam and GetPerson methods (at the end of the using block). The proxies have stored a reference to this context internally and will use this context to load the navigation collections from the database.
Because these contexts are already disposed you get the exception.
You should redesign your code a bit: Don't create a new context in your repository methods GetTeam and GetPerson. You should instead use the same context for all operations: Retrieving the Team, retrieving the Person and adding the relationship. For example:
public static void AddPersonToTeam(int TeamId, int PersonId)
{
using (var ef = new korfballReportEntities())
{
var team = GetTeam(ef, TeamId);
var person = GetPerson(ef, PersonId);
team.Person.Add(person);
//person.Team.Add(team); <- not necessary, EF will handle this
ef.SaveChanges();
}
}
public static Team GetTeam(korfballReportEntities ef, int id)
{
var q = from l in ef.Team
where l.Id == id
select l;
return q.Single();
}
public static Person GetPerson(korfballReportEntities ef, int id)
{
var query = from p in ef.Person
where p.Id == id
select p;
return query.Single();
}
Another approach is to make your "Repository"/"Service" not static, inject the context into the constructor and then use this context throughout the repository. Then you don't need to pass in the context into every method. A rough sketch:
using (var ef = new korfballReportEntities())
{
var repository = new MyRepository(ef);
repository.AddPersonToTeam(int TeamId, int PersonId);
}
public class MyRepository
{
private readonly korfballReportEntities _ef;
public MyRepository(korfballReportEntities ef)
{
_ef = ef;
}
public void AddPersonToTeam(int TeamId, int PersonId)
{
var team = GetTeam(TeamId);
var person = GetPerson(PersonId);
team.Person.Add(person);
_ef.SaveChanges();
}
public Team GetTeam(int id)
{
var q = from l in _ef.Team
where l.Id == id
select l;
return q.Single();
}
public Person GetPerson(int id)
{
var query = from p in _ef.Person
where p.Id == id
select p;
return query.Single();
}
}
Edit
One little thing about performance tuning: In this specific case lazy loading is not necessary and more disturbing. It causes to load a (potentially long) collection team.Person when you want to add only one additional Person to the collection. You can switch off lazy loading for this particular operation (I refer to my second example):
using (var ef = new korfballReportEntities())
{
//switch lazy loading off, only in this single context
ef.Configuration.LazyLoadingEnabled = false;
var repository = new MyRepository(ef);
repository.AddPersonToTeam(int TeamId, int PersonId);
}
public void AddPersonToTeam(int TeamId, int PersonId)
{
var team = GetTeam(TeamId);
var person = GetPerson(PersonId);
// if lazy loading is off, the collecton is null, so we must instantiate one
if (team.Person == null)
team.Person = new List<Person>();
team.Person.Add(person);
_ef.SaveChanges();
}