Entity Framework - many-to-many - entity-framework

I´m having trouble understand EF.
I have 2 tables in a N-to-N relationship. When I create the model in Visual Studio I get it like the picture below.
Artist is related to Album, and Album is related to Artist.
In the database I have a table to relate both tables.
My question is:
Why can I add an artist with two albums,
var artist = new Artist { FirstName = "Alan", LastName = "Jackson" };
var album1 = new Album { AlbumName = "Drive" };
var album2 = new Album { AlbumName = "Live at Texas Stadium" };
artist.Albums.Add(album1);
artist.Albums.Add(album2);
context.Artists.Add(artist);
But can´t do the other way around?
var artist1 = new Artist { FirstName = "Tobby", LastName = "Keith" };
var artist2 = new Artist { FirstName = "Merle", LastName = "Haggard" };
var album = new Album { AlbumName = "Honkytonk University" };
artist1.Albums.Add(album);
artist2.Albums.Add(album);
context.Albums.Add(album);
context.SaveChanges();
It´s not making sense to me, since both tables in VS model have the Navigation Properties correct, isn´t the same adding to left or right?
In my mind, in the second case EF should add artist1 and artist2 to DB.
Thanks for reading.
FranciscoRC

To expand on Ivan's comment.
With this example:
var artist1 = new Artist { FirstName = "Tobby", LastName = "Keith" };
var artist2 = new Artist { FirstName = "Merle", LastName = "Haggard" };
var album = new Album { AlbumName = "Honkytonk University" };
artist1.Albums.Add(album);
artist2.Albums.Add(album);
context.Albums.Add(album);
Prior to SaveChanges, album.Artists hasn't been set with anything, and all EF has to go on is what you've set on album.
In your case, you've created 2x artist objects, and 1 album. You've associated the album to the artist, but you've only notified EF of the album to be saved. Just as with POCOs just because an artist has an album and an album has an artist, setting the album on an artist doesn't automatically set the artist property on an album. EF does this behind the scenes provided that it sees the related entities on the entity(ies) you add to the context.
So the following should work:
var artist1 = new Artist { FirstName = "Tobby", LastName = "Keith" };
var artist2 = new Artist { FirstName = "Merle", LastName = "Haggard" };
var album = new Album { AlbumName = "Honkytonk University" };
album.Artists.Add(artist1);
album.Artists.Add(artist2);
context.Albums.Add(album);
Prior to SaveChanges being called, artist1.Album will be null because you haven't set anything. After SaveChanges, artist1 & 2 .Album will reference to your new entity because EF resolved the Artists collection on the album and wired up the related references. (Even though Artists 1 & 2 were not added to the context explicitly.)

Related

Web API Entity Framework Returning linked table

So I have two Database Tables
Club
- ClubId
- ClubName
ClubMembers
ClubMemberId
ClubId
FirstName
LastName
in my api controller I have
private ClubsEntities db = new ClubsEntities();
// GET: api/Clubs
public IQueryable<Club> GetClub()
{
return db.Club;
}
But when I hit it i get data returned from both tables
[{"ClubMember":[{"ClubMemberId":1,"ClubId":1,"FirstName":"John","LastName":"Smith"}],"ClubId":1,"ClubName":"Test"},{"ClubMember":[],"ClubId":2,"ClubName":"Test 2"}]
How can I get it to just return from club this is baffling me
Ok so ive figured out that if I just specify a new class just with the values I want returned and select those values, it will keep my returned data cleaner.
return db.Club.Select(c => new myClubs { ClubId = c.ClubId, ClubName = c.ClubName }).ToList();

How to save Father and Child document with MongooseJS

I'm quite new in MongoDB & MongooseJS. Actually learning the MEAN stack, I already love it.
I'm not sure to exactly understand noSQL principles, sorry If I duplicate.
I want to join 2 documents, type father and child, with 1 to N relationship.
Here's two solutions:
• Sub documents: http://mongoosejs.com/docs/subdocs.html
• References: http://mongoosejs.com/docs/populate.html
I've chosen the first one, simpler and IHMO closer to my problem.
Here's the code:
var personSchema = new Schema({
name : String
});
var groupSchema = new Schema({
name : String,
persons : [ personSchema ]
});
var Group = module.exports = mongoose.model('Group', groupSchema);
var Person = module.exports = mongoose.model('Person', personSchema);
Group.findById(groupId, function(err, group) {
var person = new Person();
person.name = "john";
group.persons.push(person);
group.save();
});
When I check for all groups, it work perfectly. The groups are returned, with the people saved.
Group.find(function(err, group) {
// group is full of groups & people
}
But when I check for person, nothing is returned.
Person.find(function(err, person) {
// person is empty
}
It seems that only the Group table document was filled. Is the solution implies to save 1/ the person and 2/ the group, and if so, will Person be saved in two different places (in Group document and in Person document)?
Group.findById(groupId, function(err, group) {
var person = new Person();
person.name = "john";
group.persons.push(person);
person.save(function(...) {
group.save();
});
});
Thanks

EF 6 - many-to-many - Join table without duplicates

I'm using EF6 have some confusion on seeding a many to many relationship.
I have the following:
A User has many saved ChartQueries (that they can execute to get a chart).
A ChartQuery typically belongs to only one user, but there are several "shared" ChartQuerys that every User can execute. As a result I set up a many to many relationship using a join table UserChartQuery. The tables are up in the database just fine at 1-to-many on each side of the join table.
However, I'm not quite understanding how to seed or use this relationship. I don't want to end up with several duplicates of the "shared" ChartQuerys (a duplicate for each User). Instead, there should only be a single row for each "shared" ChartQuery that is a part of each User's SavedChartQueries collection (along with other, non-shared ChartQuerys that belong to that User only).
It seems like I'm forced to duplicate for each user:
var sharedChartQuery = new ChartQuery { ... };
var nonSharedChartQuery = new ChartQuery { ... };
var userOneChartQueryOne = new UserChartQuery { User = userOne, ChartQuery = sharedChartQuery };
var userTwoChartQueryOne = new UserChartQuery { User = userTwo, ChartQuery = sharedChartQuery };
var userTwoChartQueryTwo = new UserChartQuery { User = userTwo, ChartQuery = nonSharedChartQuery };
context.UserChartQueries.Add(userOneChartQueryOne);
context.UserChartQueries.Add(userOneChartQueryTwo);
context.UserChartQueries.Add(userTwoChartQueryTwo);
So first of all is this the right way to seed (through UserChartQueries table directly) or should I seed each User's SavedChartQueries navigation property?
And will this result in duplicate sharedChartQuery in the join table for each User? If so is there any way to avoid this?
Ok I understand how this works now. The following works as expected:
var userOne = new User {};
var userTwo = new User {};
var chartQuery = new ChartQuery { };
context.Users.Add(userOne);
context.Users.Add(userTwo);
context.UserChartQueries.Add(new UserChartQuery { User = userOne, ChartQuery = chartQuery });
context.UserChartQueries.Add(new UserChartQuery { User = userTwo, ChartQuery = chartQuery });
context.ChartQueries.Add(chartQuery);
The last line adds it to the table where the record actually resides. Checking the join table in SSMS shows that it only holds the foreign keys and nothing else. There are no duplicates.

EF 4.3 Beta 1 on Optional Relationship the Cascade Delete leaves Orphans

Is this odd?
When deleting relationships of one to many, if a the relationship is optional and you delete the parent object the rest will remain orphan and it does not cascade delete.
var album = new Album
{
Name = "Test Album",
Description = "Test Album Description",
Images = new Collection<Image>
{
new Image {
Name = "Image 1",
Description = "Image 1 Description"
},
new Image {
Name = "Image 2",
Description = "Image 2Description"
},
}
};
albumRepository.Add(album);
albumRepository.UnitOfWork.Commit();
Under the Image Entity I got the AlbumId as Nullable since some images can be orphaned.
And then I call.
albumRepository.Delete(toRemove);
albumRepository.UnitOfWork.Commit();
The Album gets deleted but the images that where once related are Orphaned and their AlbumId is removed from the row.
This did it.
modelBuilder.Entity<Image>()
.HasOptional(d => d.Album)
.WithMany(d => d.Images)
.WillCascadeOnDelete(true);

Entity Framework attach trouble in many to many update scenario

I have a scenario where I wish to update a movie entity and its many to many relationship with Genres.
The navigation property Genres in movie contains stub Genre objects that only contain the GenreID because I want to save querying the DB for all genres.
See the code below, its fairly self explanatory. The problem is that I need to attach my "Stub" genres to the context so the EF will only modify the M:M relationship and not try to create new Genre records. This causes an error if a genre is already attached to the context from when I load the movie's current genres. It can't track multiple objects with the same keys.
How should this be handled? Is there a way to check if the context is already tracking the entity, or is there a better solution to this problem?
The code is below:
public override void Update(Movie movie)
{
//Backup new genres before clearing genres from movie
var newGenres = movie.Genres;
movie.Genres = new List<Genre>();
_ctx.Entry(movie).State = System.Data.EntityState.Modified;
//Load movie's current genres from DB and remove them
_ctx.Entry(movie).Collection(m => m.Genres).Load();
movie.Genres.Clear();
//Add new genres to movie
foreach (var genre in newGenres)
{
_ctx.Genres.Attach(genre); //Problem if genre already attached
movie.Genres.Add(genre);
}
}
Thanks for any help.
You could try to avoid to attach the stubs if an entity with the same key is already attached:
public override void Update(Movie movie)
{
//Backup new genres before clearing genres from movie
var newGenres = movie.Genres;
movie.Genres = new List<Genre>();
_ctx.Entry(movie).State = System.Data.EntityState.Modified;
//Load movie's current genres from DB and remove them
ctx.Entry(movie).Collection(m => m.Genres).Load();
var currentGenres = movie.Genres.ToList();
movie.Genres.Clear();
//Add new genres to movie
foreach (var genre in newGenres)
{
var currentGenre = currentGenres.SingleOrDefault(g => g.Id == genre.Id);
if (currentGenre != null)
movie.Genres.Add(currentGenre);
else
{
_ctx.Genres.Attach(genre);
movie.Genres.Add(genre);
}
}
}