Duplicates in database - c#

I'm using vs2017 with entityframework 6.1.0 and winforms . As for entityframework i use code-first. I need to make a movie app, I have made all classes for them Movie, Genre and Cast(actors).
All Genres are pre inserted in the database. When using update-database everything is created including joining tables moviegenre and movie cast and also foreignkeys. When i insert a movie object. it links the id's from genre cast and movies but it also reinserts every genre which means i have duplicates. I only want the linking of course. So far this is my code.
movie class:
public class Movie
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MovieId { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
//[ForeignKey("GenreId")]
public virtual List<Genre> Genre { get; set; }
//[ForeignKey("CastId")]
public virtual List<Cast> cast { get; set; }
[Required]
public int GenreId { get; set; }
[Required]
public int CastId { get; set; }
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
public Movie()
{
Genre = new List<Genre>();
cast = new List<Cast>();
}
}
Genre and cast (the same for both classes)
public class Genre
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int GenreId { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
public List<Movie> Movies { get; set; }
}
and of course my code (this piece of code is from the button click event to add a movie into the db.):
private void btnAddMovie_Click(object sender, EventArgs e)
{
List<Genre> genrelist = new List<Genre>();
List<Cast> castlist = new List<Cast>();
var movie = new Movie();
movie.Name = txtTitle.Text;
movie.ReleaseDate = released;
//creating lists
foreach (string item in lbgenre2.Items)
{
var genre = new Genre();
genre.Name = item;
genrelist.Add(genre);
}
foreach (string item in lbCast2.Items)
{
var cast = new Cast();
cast.Name = item;
castlist.Add(cast);
}
movie.Genre = genrelist;
movie.cast = castlist;
_context.movies.Add(movie);
Save();
}
private async void Save()
{
await _context.SaveChangesAsync();
}
What am I doing wrong that it links and reinserts it?

Your problem is because you are creating the Generes again, and again every time you add a new movie and you have a Identity Key, so, no exception will throw.
foreach (string item in lbgenre2.Items)
{
//Here is your problem, you are creating a new Genere instead of using the already created
var genre = new Genre();
genre.Name = item;
genrelist.Add(genre);
}
So, instead of creating, use ef to get the existing ones
foreach (string item in lbgenre2.Items)
{
//try to get the genere from database
var genre = _context.generes.FirstOrDefault(x => x.Name == item);
//if it doesn't exist..
if(genre == null)
{
//Create it
genre = new Genre();
//And set the name
genre.Name = item;
}
//but, if it already exist, you dont create a new one, just use the existing one
genrelist.Add(genre);
}

Entity Framework cannot figure out if a Genre or Cast already exists, even if you make an instance of one of them, with identical properties to one which exists in the database. Instead you need to get the existing genres and cast from the database and apply them. And only create a new instance, if it is a completely new genre or cast, which is not in the database:
Pseudo-code:
SaveMovie(Movie movie)
{
var existingGenres = GetGenresFromDatabase();
var movieGenres = GetGenresFromListBox();
foreach (var genre in movieGenres)
{
if (genre in existingGenres)
{
existingGenre = existingGenres.Where(genreId = genre.Id);
movie.Genres.Add(existingGenre)
}
else
{
movies.Add(genre)
}
}
dbcontext.Add(movie);
dbcontext.SaveChanges();
}

Related

LiveCharts2 populating chart with Entity Framework

I'm starting with Livecharts2 (I did not try previous versions) and I'm not sure how to bind data with Entity Framework.
I need to display a chart with warehouse name and the quantity of access of each.
This is what I already have:
List<Access> access = context.Access.ToList();
var countAccess = access
.GroupBy(acc => acc.IdWarehouse)
.Select(group => new
{
Warehouse = group.Key,
Quantity = group.Count()
});
List<int> listQt = new List<int>();
List<int> list_warehouses = new();
foreach (var item in countAccess)
{
listQt.Add(item.Quantity);
list_warehouses.Add(item.Warehouse);
}
cartesianChart1.Series = new ISeries[]
{
new LineSeries<int>
{
Values = listQt,
Name = "Quantity"
},
new ColumnSeries<int>
{
Values = list_warehouses,
Name = "Warehouse"
}
};
My model classes:
public partial class Access
{
public int IdAccess { get; set; }
public byte IdWarehouse { get; set; }
}
public partial class Warehouse
{
public byte IdWarehouse { get; set; }
public string Name{ get; set; } = null!;
}
This code works with the IdWarehouse but Name, is there a better way to do it, instead of creating new list, foreach etc? I'm not sure if it is unnecessary
I tried with List<string> to get the warehouse's names, but got an exception cause is not implemented yet by creator/s

Entity Framework performing INSERT where it should not be

I have the following Entity Data models, simplified for brevity;
public abstract class Entity<T> : BaseEntity, IEntity<T>
{
[Key]
public virtual T Id { get; set; }
}
public class User : Entity<int>
{
public List<Category> Categories { get; set; }
}
public class Category : Entity<int>
{
public string Description { get; set; }
}
public class List : Entity<int>
{
public Category Category { get; set; }
}
These are accessed from the DbContext using a DbContext exposed by either a generic service, or a more customised implementation to provide business logic.
When I publish the database and add the following code to the Seed() method, all is well and the data looks good directly in the database.
var user = new User
{
Email = "",
Categories = new List<Category>
{
new Category
{
Description = "Category 1",
},
new Category
{
Description = "Category 2",
}
}
};
context.Users.AddOrUpdate(u => u.Email, user);
var list = new List()
{
Id = 1,
Description = "Test List",
UserId = 1,
Category = user.Categories.FirstOrDefault()
};
context.Lists.AddOrUpdate(u =>u.Id, list);
Please note that the User owns the categories and you can (should only be able to) create them by accessing the Categories Property.
This gives me;
I am using these objects in my controller as such;
public ActionResult Edit(int id)
{
var categories = _usersService.GetUser(User.Id).Categories;
categories.Insert(0, new Category {Description = "", Id = 0});
var list = _listsService.GetList(id);
var viewModel = new EditViewModel
{
Id = list.Id,
Reference = list.Reference,
Description = list.Description,
CategoryId = list.Category?.Id ?? 0,
Categories = new SelectList(categories.AsEnumerable(), "Id", "Description")
};
return View(viewModel);
}
In the above test, I am using the List inserted during the Seed and I can see the List does indeed have a Category, and the values are correct.
For information, I am using the following ViewModel. I have been investigation methods to be able to 'select' the User.Categories from a DropDown and this appeared to work the best at present.
public class EditViewModel
{
[Required]
public int Id { get; set; }
[Required]
public string Description { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
[ScaffoldColumn(false)]
public int CategoryId { get; set; }
[ScaffoldColumn(false)]
public Guid Reference { get; set; }
}
The populated ViewModel looks like this;
and finally, the POST method;
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if (ModelState.IsValid)
{
var categories = _usersService.GetUser(User.Id).Categories;
var list = _listsService.GetList(model.Id);
list.Description = model.Description;
list.Category = categories.FirstOrDefault(x => x.Id == model.CategoryId);
_listsService.Update(list);
categories.Insert(0, new Category { Description = "", Id = 0 });
model.Categories = new SelectList(categories.AsEnumerable(), "Id", "Description");
return View(model);
}
return View(model);
}
So, in the following scenarios, this is what happens. For clarity, each time I am doing this, I go back to the Lists Index and GET Edit again;
Select '' from the Dropdown - NO Categories INSERT,UPDATE on Lists table only, setting [Category_Id] = NULL - Correct
SELECT 'Category 1' from DropDown. INSERT categories, UPDATE lists - NOT Correct
The code being used to update the List is;
public void Update(T entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
}
Now, I know this is something I am doing, but being new to EF, I have no idea.
The problem was down to how the values were being set.
I needed to set the Foreign Key and assign the value to this.
public class List : Entity<int>
{
public int CategoryId { get; set; }
[ForeignKey("CategoryId")
public Category Category { get; set; }
}
and the value was then set as
var list = _listsService.GetList(model.Id);
list.Description = model.Description;
list.CategoryId = model.CategoryId;
list.Category = null;
_listsService.Update(list);
After this, when getting the list from the repository, both the Category and CategoryID would be populated correctly.
The issue was down the setting the Entity as modified, this internally indicated that the Category was 'new' when in fact it was not. You could also 'attach' and existing category to the entity/context but decided the method above was better.
A slightly better approach to the above would be to create a new 'UpdateList' method which could be called rather than the generic update. This method would perform the setting of the relevant properties outside of the controller method.
I am not sure but possible you must write your update method as follow:
public void Update(T entity)
{
if (entity == null) throw new ArgumentNullException(nameof(entity));
var item = _collection.Find(item.Id);
if (item == null) throw new ArgumentNullException(nameof(item ));
_context.Entry(item).CurrentValues.SetValues(entity);
_context.SaveChanges();
}
The problem can be in view, of you don't have the I'd the EF will add new record instead of update the existing record.
In general: Watch out for existing classes when you name your custom class (like List):
public class List : Entity<int>
{
public Category Category { get; set; }
}
Be shoure that you use the right class in the right namespace.
var user = new User
{
Email = "",
Categories = new YourOwnNamespace.List<Category>
{
new Category
{
Description = "Category 1",
},
new Category
{
Description = "Category 2",
}
}
};
Avoid naming classes and properties to existing names. Beter change List class in e.g. 'MyList'.

Entity Framework 6.0 updating List of items creates new records in the Database

The following are the entity classes to make more understanding of relationships:
public class EmployeeCv : UserEntity
{
public byte ProfileImage { get; set; }
public virtual List<Header> Headers { get; set; }
public virtual List<ProjectExperience> ProjectExperiences { get; set; }
public virtual List<Tag> Tags { get; set; } //many to many relationship between employeeCv and tags
[NotMapped]
public List<TagsByTypes> TagsbyTypes
{
get
{
List<TagsByTypes> ListOfTagTypes = new List<TagsByTypes>();
if (Tags != null)
{
var GroupedList = Tags.GroupBy(x => x.TagType.Title).ToList().Select(grp => grp.ToList());
foreach (var currentItem in GroupedList)
{
var TagType = new TagsByTypes()
{
Title = currentItem.FirstOrDefault().TagType.Title,
Tags = currentItem
};
ListOfTagTypes.Add(TagType);
}
return ListOfTagTypes;
}
else
return null;
}
}
}
public class Tag : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<EmployeeCv> EmployeeCv { get; set; }
public virtual TagType TagType { get; set; }
//To post Id's Not added to the database
[NotMapped]
public int TagTypeId { get; set; }
[NotMapped]
public int EmployeeCv_Id { get; set; }
}
public class TagType : AuditableEntity<int>
{
public string Title { get; set; }
public virtual List<Tag> Tags { get; set; }
}
I am writing a function to add new tag to the employeeCv based on the existing tag type. I have got Unit of work and Repositories setup to add/update/delete records in DB. Here is my implementation:
public void UpdateEmployeeCVWithTag(Tag tag)
{
using (var repository = new UnitOfWork<EmployeeCv>().Repository)
{
var EmployeeCv = repository.GetSingleIncluding(tag.EmployeeCv_Id,
x => x.Headers, x => x.Tags,
x => x.ProjectExperiences,
x => x.ProjectExperiences.Select(p => p.AssociatedProject),
x => x.ProjectExperiences.Select(p => p.ProjectSkills));
//x => x.ProjectExperiences.Select(p => p.ProjectSkillTags.Select(s => s.AssociatedSkill)));
//tag.TagType = EmployeeCv;
var repositoryTagType = new UnitOfWork<TagType>().Repository;
var tagtype = repositoryTagType.GetItemById(tag.TagTypeId);
tag.TagType = tagtype; //even after assignment new tagtype is creating everytime code runs
//repositoryTag.UpdateItem(tagtype);
EmployeeCv.Tags.Add(tag);
//EmployeeCv.ProjectExperiences[projectId - 1].ProjectSkills.Add(tag);
repository.UpdateItem(EmployeeCv);
}
}
This function works correctly except one issue. It is creating a new TagType in the database and ignoring the one that already exist. Below is my updateItem code in the repository classs:
public virtual void UpdateItem(TEntity entityToUpdate)
{
var auditableEntity = entityToUpdate as IAuditableEntity;
if (auditableEntity != null)
{
auditableEntity.UpdatedDate = DateTime.Now;
}
//_context
//Attach(entityToUpdate);
_context.Entry(entityToUpdate).State = EntityState.Modified;
_context.SaveChanges();
}
My guess without seeing the full functionality, is that you are using different context for this.
You should update the foreign key not the entire object so there is no need to add the entire TagType object since the tagTypeId is already set. The foreign key should work as is.
Please look into this link for further information.

How To Get list Of Users

I am using Entity Framework 6
public class ALLEvent
{
[Key]
public long Eventid { get; set; }
public string eventname{ get; set; }
public string AttendingUsers{ get; set; }
public virtual List<User> Users{ get; set; }
}
public class Users
{
[Key]
public long id { get; set; }
public string Name{ get; set; }
}
I have an AllEvent Class, in AttendingEvent property I have a list of attending UserIds separated by a comma. Is there any way I can get a list of attending users in the Users property of AllEvents?
You could do it with Select:
allEvent.Users = allEvent.AttendingUsers.Split(',').Select(
u => new User
{
Id = Convert.ToInt64(u)
}).ToList();
Or with a foreach loop:
var users = new List<Users>();
foreach (var u in allEvent.AttendingUsers.Split(',').ToList())
{
users.Add(new Users
{
Id = Convert.ToInt64(u)
});
}
allEvent.Users = users;
The Convert.ToInt64() method is used if you need a long type. But be aware, this could throw an exception.
https://msdn.microsoft.com/en-us/library/0zahhahw(v=vs.110).aspx
FormatException
or
OverflowException
You will have to do a query for every event to get the users:
using(var ctx = new MyContext())
{
ctx.Configuration.LazyLoadingEnabled = false;
var events = ctx.AllEvent.ToList();
foreach(var event in events)
{
event.Users = ctx.Users.Where(u =>
event.AttendingUsers.Split(',').Cast<long>().ToList().Contains(u.id)).ToList();
}
}
A much better option would be to make the Users have a foreignkey to an event (except if it's many to many, then you'll need an intermediate table). Then you can use lazy/eager loading. At the moment the virtual keyword doesn't do anything in your Event model because there's no relation between the entities.
Edited answer because cast from string -> long is needed.

Populating a List<class> from a query, results from List is address of the class

I have a class that I made and I want to use it for a list but when I try adding data to it and loop through the list all I'm getting is the address of the class. I have no idea why its added that as the values.
My Class is
public class Regions
{
public int dataID { get; set; }
public int documentID { get; set; }
public string region_ID { get; set; }
public string region_Name { get; set; }
public string sortID { get; set; }
}
This is how I am trying to add the values. The query has the right data in it, but the list isn't getting the data, just where the class resides.
List<Regions> lst = new List<Regions>();
var q = dt.AsEnumerable()
.Select(region => new {
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID").ToString()
});
foreach (var d in q)
lst.Add(new Regions() { dataID = d.dataID,
documentID = d.documentID,
region_ID = d.region_ID,
region_Name = d.region_Name,
sortID = d.sortID
});
EDIT
Here is a link that I found that is similar what I am trying to do, but it doesn't seem he had the same errors as I did. I tried that answer, but wasn't working for me.
Storing data into list with class
Fixed
When I was looping through the list, I wasn't reading it properly.
Try adding this in Regions
public override string ToString()
{
return region_Name;
}
actually you can create the list in one step
List<Regions> lst = dt.Rows.OfType<DataRow>().Select(region => new Regions{
dataID = region.Field<int>("dataID"),
documentID = region.Field<int>("documentID"),
region_ID = region.Field<string>("Region_ID"),
region_Name = region.Field<string>("Region_Name"),
sortID = region.Field<string>("SortID")
}).ToList();

Categories