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?
Related
hi I want to write an update function to check the data in my database and if there was a similar column don't add the filed update that filed
here in the code below, I deserialize JSON file
public class CustomerDeserializer
{
public static List<DtoCustomer> Deserialize()
{
List<DtoCustomer> result =
JsonConvert.DeserializeObject<List<DtoCustomer>>(
File.ReadAllText(#"Information.json"));
return result;
}
}
in this part, I read the data and build it
public static Customer CustomerBuild(DtoCustomer dto)
{
return new Customer()
{
FirstName = dto.FirstName,
LastName = dto.LastName,
Address = dto.Address,
Email = dto.Email,
ComapnyName = dto.CompanyName,
PhoneNumber = dto.Phone
};
and then I process data and save them into database
////update function
public static void Process(Customer customer)
{
using (var context = new PracticeEntities1())
{
context.Customers.Add(customer);
context.SaveChanges();
}
}
I want to write an update function before saving to check the data and update it what should I do?
You may do something like this:
using (var context = new PracticeEntities1())
{
var existingCustomer = context.Customers.FirstOrDefault(c => c.Email == customer.Email);
if (existingCustomer != null) {
existingCustomer.FirstName = customer.FirstName;
existingCustomer.LastName = customer.LastName;
existingCustomer.Address = customer.Address;
existingCustomer.CompanyName = customer.CompanyName;
existingCustomer.Phone = customer.Phone;
}
else
{
context.Customers.Add(customer);
}
context.SaveChanges();
}
I've got two classes as follows:
public class Movie
{
...
public virtual ICollection<Actor> Actors { get; set; }
}
public class Actor
{
...
public virtual ICollection<Movie> Movies { get; set; }
}
And Entity Frameworks creates a table in between to record the relationship between them.
Now, my question is how can I add a new record with existing Movies and Records? There are two parts to this question:
With the following method, how can I add the actors to the movie without replacing all the existing relationships for the movie:
public void AddRelationship(int movieId, int[] actorIds)
{
var movie = new Movie { Id = movieId };
context.Movies.Attach(movie);
foreach(var actorId in actorIds)
{
movie.Actors.add(new Actor{ Id = actorId });
}
context.SaveChanges();
}
This creates a new Actor which is not what I want.
With the following method, how can I replace all the actors for a movie with the given list:
public void ReplaceRelationship(int movieId, int[] actorIds)
{
}
A way with the second method is to delete all the existing ones and readd them, but I'm trying to keep the amount of Db trips down.
Also when adding I don't want to add duplicates, will I have to get all the relationships out and compare in my code?
1.
When you're doing this, you're actually creating a new actor.
movie.Actors.add(new Actor{ Id = actorId });
What you should be doing is first attaching the existing one, and then add it.
var actor = new Actor{ Id = actorId };
context.Actors.Attach(actor);
movie.Actors.Add(actor);
Or in full example:
public void AddRelationship(int movieId, int[] actorIds)
{
var movie = _context.Movies.FirstOrDefault(x => x.Id == movieId);
// You might need to do include actors like this:
//_context.Movies.Include(x => x.Actors).FirstOrDefault(x => x.Id = movieId);
if(movie == null)
{
// Now what?
throw new Exception("Invalid movieId");
}
foreach(var actorId in actorIds)
{
var actor = new Actor
{
Id = actorId
};
_context.Actors.Attach(actor);
movie.Actors.Add(actor); // EF will detect if it already exists or not.
}
_context.SaveChanges();
}
2.
public void ReplaceRelationship(int movieId, int[] actorIds)
{
var movie = _context.Movies.FirstOrDefault(x => x.Id = movieId);
// You might need to do include actors like this:
//_context.Movies.Include(x => x.Actors).FirstOrDefault(x => x.Id = movieId);
if(movie == null)
{
// Now what?
throw new Exception("Invalid movieId");
}
// Get a list of the already existing actors, so we know which to remove.
var existingActorIds = movie.Actors.Select(x => x.Id).ToList();
// Add new actors.
foreach (var actorId in actorIds.Where(x => !existingActorIds .Contains(x.Id)))
{
var newActor = _context.Actors.Find(actorId );
// You might be able to use this instead.
// var newActor = new Actor { Id = actorId };
// _context.Actors.Attach(newActor);
movie.Actors.Add(newActor );
}
var idsToRemove =
existingActorIds.Where(x => !actorIds.Contains(x));
// Remove the old ones
foreach (var actorId in idsToRemove)
{
var actorEntity = movie.Actors.FirstOrDefault(x => x.Id== actorId );
// Again, you should be able to use Attach like above.
// I use FirstOrDefault() since I actually need the full entity later.
movie.Actors.Remove(actorEntity);
}
_context.SaveChanges();
}
A way with the second method is to delete all the existing ones and
readd them, but I'm trying to keep the amount of Db trips down.
Yeah, I totally get you. Unfortunately I haven't found a better solution than to actually call Remove() on each one.
Also when adding I don't want to add duplicates, will I have to get
all the relationships out and compare in my code?
You can check if the item exists first. But in my cases EF has managed this for me. My mapping-table has two PK (one for MovieId and one for ActorId, which doesn't allow duplicates.
this.HasMany(t => t.Actors)
.WithMany(t => t.Movies)
.Map(m =>
{
m.ToTable("ActorMovies");
m.MapLeftKey("ActorId");
m.MapRightKey("MovieId");
});
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 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();
}
}
}
I have a database setup with the following: -
Person Table
Hobby Table
Game Table
GameInfo Table
Person [1 - M] Hobby [1 - M] Game [M - 1] GameInfo
Game is just a join from Hobby to GameInfo
I am having an issue whereby I would fetch Person which has a Collection<Game> and add to this collection (i.e. I am just updating links, not wanting to insert new GameInfo).
Upon Calling SaveChanges() EntityFramework will insert the links AS WELL AS inserting new GameInfo, which is not my desired result.
I have looked at Entry().State etc but the problem is where I am handling the updates of my Person is outside of the context.
I am basically fetching a Person creating a new Game with Ids that I know exist already and then calling the SaveChanges() and would expect that it would just insert into the Game Table, not the GameInfo table
EDIT 1: Code Sample - sort of
public void Save(Profile profile)
{
using (GDContext context = GetContext())
{
DataProfile dataProfile = context.Profiles.Single(u => u.ProfileId == profile.Id);
ProfileHandler.HandleDataModelChanges(dataProfile, profile);
context.SaveChanges();
}
}
public override void HandleDataModelChanges(DataProfile dataModel, Profile model)
{
dataModel.ProfileId = model.Id;
dataModel.FirstName = model.FirstName;
dataModel.LastName = model.LastName;
dataModel.DateOfBirth = model.DateOfBirth;
dataModel.Email = model.Email;
foreach(var hobby in model.Hobbies)
{
DataHobby dataHobby = dataModel.Hobbies.SingleOrDefault(p => p.HobbyId == hobby.HobbyId);
if (dataHobby == null)
{
dataHobby = new DataHobby();
}
HobbyHandler.HandleDataModelChanges(dataHobby, hobby);
}
}
public override void HandleDataModelChanges(DataHobby dataModel, Hobby model)
{
dataModel.HobbyId = model.Id;
HandleGames(dataModel, model);
HandleCrafts(dataModel, model);
HandleCollections(dataModel, model);
}
private void HandleGames(DataHobby dataModel, Hobby model)
{
IEnumerable<DataGame> gamesToRemove = dataModel.Games.Where(g => !model.Games.Any(ds => ds.Id == g.GameId)).ToArray();
foreach (var game in gamesToRemove)
{
dataModel.Games.Remove(game);
}
foreach (var game in model.Games)
{
if (!dataModel.Games.Any(e => e.GameId == game.Id))
{
DataGame dataGame = new DataGame();
dataGame.GameId = game.Id;
dataGame.GameName = game.Name;
dataModel.Games.Add(dataGame);
}
}
}
EDIT 2 - Context configuration
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.AutoDetectChangesEnabled = true;
public GameInfoConfiguration()
{
HasKey(x => x.GameId);
ToTable("GameData");
}
public PersonConfiguration()
{
HasKey(x => x.PersonId);
ToTable("Person");
}
public HobbyConfiguration()
{
HasKey(x => x.HobbyId);
HasRequired(x => x.Person).WithMany(x => x.Hobbies);
HasMany(x => x.Games).WithMany(g => g.Hobbies).Map(x => x.MapLeftKey("HobbieId").MapRightKey("GameId").ToTable("PersonGame"));
ToTable("HobbyGame");
}
Well I still don't see where you work with GameInfo - your description absolutely doesn't correspond with your code. By looking at your code I guess the problem will be in the snippet like this:
foreach (var game in model.Games)
{
if (!dataModel.Games.Any(e => e.GameId == game.Id))
{
DataGame dataGame = new DataGame();
dataGame.GameId = game.Id;
dataGame.GameName = game.Name;
dataModel.Games.Add(dataGame);
}
}
This will always insert a new Game - you told EF to insert a new Game. If you want to add existing Game you must do:
foreach (var game in model.Games)
{
if (!dataModel.Games.Any(e => e.GameId == game.Id))
{
DataGame dataGame = new DataGame();
dataGame.GameId = game.Id;
dataGame.GameName = game.Name;
context.Games.Attach(dataGame); // Now the context knows that it is not a new entity
dataModel.Games.Add(dataGame);
}
}
I think the mistake I have made here is that I am dealing with DataGame when really what I should be dealing with is a POCO class to represent the "join" between DataGame and Hobby Like a HobbyGame POCO.