Entity Framework Many-To-Many with additional field on Joining Table - c#

I have an entity context that includes three tables. The first is a table that contain products, the second contains recipes. The joining table has fields for IDs in both the products and recipes table as well as a 'bit' field called 'featured'.
I've searched and found no example on how to insert only how to select against this type of scenario.Does anyone have any suggestions on how this can be done? Thanks in advance for any help.

I didn't do C# for a while, so I am not sure my syntax is valid, anyway this should be the idea:
Products product = new Products { Blah, Blah, Blah };
bool flag = false;
for (int i = 0; i < 5; i++)
{
Products_Receipes pr = new Products_Receipes
{ Products = product, IsFeatued = flag };
pr.Receipes.Add(new Receipes());
pr.Receipes.Add(new Receipes());
flag = !flag;
}
Context.SaveChanges();
And if the above doesn't work, then let me just tell you that you have to create the main item (either Products or Receipes), then when you create the Products_Receipes set it's Products/Receipes property to the above (or by Products.Pruducts_Receipes.Add(pr)), then add the other side of the relation the same way.

An observation is that your diagram is structured more like a DB schema than an entity diagram. Entities should be designed to meet the business needs independent of the data storage structure so you can use any DB to store the data.
I believe you can remove the "Link" entity and set up a one-to-many or many-to-many associations between Products and Recipes. Then setup your DB schema w/ the link table and do the Table Mapping accordingly.

Related

EF 4.1 code first - How to update/delete many to many join table entries automatically

I have 2 entities, let's say, Trip and Activity. The relationship between them is many to many so a join table is created automatically by EF.
Entity Trip attributes:
-Id (PK) Generated by database
-Name
-Description
-Property1
-Property2
-Property3
Entity Activity attributes (this entity contains fixed records -read only-, no records are inserted here on performing inserts):
-Id (PK) Generated by database
-Name
-Description
-Cost
Join table contains 2 columns, that is, the IDs of the above entities, that are primary and foreign keys at the same time.
I have no problems inserting entries which automatically EF creates join table TripActivities and add entries successfully to it. Also entries are added successfully to entity Trip and it leaves unchanged entity Activity.
My problem is on updating entries, for example, - suppose user can modify information related to a trip from the GUI - so I take all the info from this GUI and I perform the following steps to update the existing trip:
Trip trip = Context.Trips.Find(id); // Search for the appropriate trip to update from Id
trip.Name = ObtainNameFromGUI();
trip.Description = ObtainDescriptionFromGUI();
trip.Property1 = ObtainProperty1FromGUI();
trip.Property2 = ObtainProperty2FromGUI();
trip.Property3 = ObtainProperty3FromGUI();
trip.Activities = new List<Activity>();
// From the GUI user selects from a checkbox list the activities associated to the trip
// So we read its Ids and from those ids we fetch from database the activities to obtain
// the info related to each activity selected in the GUI. This is all done inside the
// below method.
List<Activity> activities = this.ObtainActivitiesSelectedFromGUI();
// If no activites selected (=null) I want EF automatically deletes the entries in the
// joined table for this trip. And of course, if there are activities selected, EF
// should update the respectives entries in the joined table for this trip with the new
// ones.
if (activites != null)
{
activities.ForEach(a =>
{
trip.Activities.Add(a);
});
}
context.Trips.Add(trip);
context.SaveChanges();<br><br>
By doing this I want EF updates all the entities related (except Activity as it has fixed entries, must be kept unchanged), that is, Trip and the joined table automatically but it does not work: a new trip is created and more entries in the joined table (The only thing that is working is that entity Activity is kept unchanged as I want).
How to achieve this? I have spent a lot of hours trying to do this but without success...
Thanks in advance.
EDIT:
I have removed line:
context.Trips.Add(trip);
Now the results are:
-Entity Trip is correctly updated, no new records added which is Ok.
-Entity Activity is kept unchanged which is Ok.
-Join table: The old records for current trip being updated are not updated, instead new records are inserted for the current trip which is not correct.
I have used a different approach for similar scenario that I faced, which works well with Detached Entities. What I ended up was finding out which entities were added and which ones deleted by comparing GUI(detached entity) values to the database values. Here is the sample code that I have used. The entities in play are RelayConfig and StandardContact which have many to many relationship
public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) {
RelayConfig dbRelayConfig = context.RelayConfigs.Include(r => r.StandardContacts)
.Where(r => r.Id == relayConfig.Id).SingleOrDefault();
context.Entry<RelayConfig> (dbRelayConfig).CurrentValues.SetValues(relayConfig);
List<StandardContact> addedExposedContacts =
exposedContacts.Where(c1 => !dbRelayConfig.StandardContacts.Any(c2 => c1.Id == c2.Id)).ToList();
List<StandardContact> deletedExposedContacts =
dbRelayConfig.StandardContacts.Where(c1 => !exposedContacts.Any(c2 => c2.Id == c1.Id)).ToList();
StandardContact dbExposedContact = null;
addedExposedContacts.ForEach(exposedContact => {
dbExposedContact = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id);
dbRelayConfig.StandardContacts.Add(dbExposedContact);
});
deletedExposedContacts.ForEach(exposedContact => { dbRelayConfig.StandardContacts.Remove(exposedContact);});
You will use something like this. Assuming that you will get the related objects from the UI and just you are going to update the same in the database, some thing like the following will work.
context.Products.Attach(product);
context.ObjectStateManager.ChangeObjectState(product, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductDescription, System.Data.EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(product.ProductModel, System.Data.EntityState.Modified);
context.SaveChanges();
As you may see here, we are setting the EntityState as Modified which hints EF to perform update for the related tables too.
Please post back your queries or any issues that you may encounter in this implementation.

Comparing SQL tables and getting Values using Entity Framework

I have three tables viz: Person(PersonID(INT), Personname(varchar)) , Items(ItemID(INT), Itemname(varchar)) and PersonItemAssoc(PersonItemAssoc(Int), PersonID(INT), ItemID(INT)).
PersonItemAssoc is having many to many association for personid and Itemid.
I want to get way in which if I pass the itemId, I should get all the PersonIds which dont have an association witn this ItemId in the PersonItemAssoc table.
I am using Entity Framework 4.0.
Please suggest a way for implementing this.
var peopleWithoutItem = from p in Context.Person
where !p.PersonItems.Any(pi => pi.Item.ItemId == someItemId);
select p;
Note that if you get rid of PersonItemAssoc(int) and make the PersonItemAssoc PK the compound of PersonID and ItemID then the EF can do People to Items as a many to many, instead of two 1 to many relationships.

Entity Framework select single value from row

I am using Entity Framework from .NET 3.5
I have two tables with 0-1 to many relation. Let's say Citizen and City. Each citizen has foreign key column (ID) that connects him to City.
When i select single citizen, i also need to select the name of the city where he lives.
Because city table contains tons of data that is not really related to citizen, so i don't want to retrieve it from database to save some bandwidth.
Currently i am using Include() function, but it grabs all the data from the City related to citizen, while i need only name.
Is there a way to write a query to select single cell from the whole row in EF and without creating new classes or interfaces or repositories?
Here is my Include:
Citizen citizen = db.Citizens.Include("Cities").First(p => p.citizen_id == id);
You do this by projecting, e.g.
var c = from c in db.Citizens
where c.citizen_id == id
select new
{
Name = c.Name,
CityName = c.City.Name
};
You can also project onto POCOs.
You cannot tell the EF to retrieve an object of type Citizen with a related City but with only City.Name filled in. The EF will not partially materialize an entity. Use view / presentation models or DTOs instead of entities when you need only a few fields.

Database doubly connected relationship inserting problem

I have two tables Plants and Information. For every plant there are many information, but for each plant there is a single MainInformation. So there is a one-to-many relationship and a one-to-one relationship between the two. The Information table has a PlantID and the Plants table has a MainInformationID. I want both fields in both tables not to be nulls. But now you can't insert either of the two records into their tables because each one requires their fields not be null, meaning they need the other record to be created first in order to create themselves. Perhaps this is not a good database design and something should be changed? (I am new to databases and entity framework)
I tried inserting into the database itself manually but I cant do it. I also tried this code with EntityFramework.
using (var context = new MyEntities())
{
var p = new Plant()
{
LatinName = "latinNameTest",
LocalName = "localNameTest",
CycleTime = 500
};
var i = new Information()
{
ShortDescription = "ShortDesc",
LongDescription = "LongDesc"
};
p.MainInformation = i;
i.Plant = p;
context.AddToPlants(p);
context.AddToInformation(i);
context.SaveChanges();
}
One of
The 1-1 FK column has to be NULL
The FK has to be disabled to allow parent insert before child
You have a single dummy Information row that is used by default in FL column
SQL Server does not allow deferred constraint checking without "code change" rights so even wrapping in a transaction won't work
Sounds like an EAV schema, which has other problems
You need to change the tables to allow for null. There is no other way to do this.
You may want to look at database transactions and how to use them with the Entity Framework. You can wrap both INSERTS into a single db transaction so the only results are both of them go in or neither go in.
Here is a link for transactions using EF. I didn't read through it but it seems to talk about them enough to get you started.

Saving many-to-many relationship objects in entity framework

I'm trying to save an entity that forms part of a many-to-many relationship in the entity framework and am getting the error: Unable to update the EntitySet 'zRM_OP_defaultloccvgsMapping' because it has a DefiningQuery and no element exists in the element to support the current operation.
My google-fu is weak on this one, but I understand I may have to edit the designer code and hook up a stored procedure to manually do the insert.
My table structure looks like this:
locations:
LOCID,
blah..
coverages:
CVGID,
blah..
loccvgs:
LOCID,
CVGID (no keys)
Help!
I have this exact question, but I've come up with a solution. I'll warn you though, it's a really bad one. In my situation I have 3 tables:
Video
- VideoId , int , PK
- blah
Comment
- CommentId, int, PK
- UserId, int (user that made the comment)
- CommentText, string
VideoComments
- VideoId, int, PK
- CommentId, int, PK
First of all, for entity framework to setup the relationship correctly both columns on the association table (VideoComments in my case) must be a primary key.
That said, here is how I am adding a new comment.
public VideoDTO AddCommentToVideo(VideoDTO pVideo, CommentDTO pComment)
{
Video video = context.Videos.Where(v => v.VideoId ==
pVideo.VideoId).FirstOrDefault();
User user = context.Users.Where(u => u.UserId ==
pComment.User.UserId).FirstOrDefault();
Comment comment = new Comment();
comment.CommentText = pComment.CommentText;
comment.User = user;
comment.Videos.Add(video);
context.AddToComments(comment);
context.SaveChanges();
return pVideo;
}
This is very bad...like crossing the streams bad. I do 2 queries to get the full objects mandated by Entity Framework so I can create a new comment. I would like a better way to do this, but I don't know one. Maybe this can help.

Categories