ASP.NET MVC 4 Code First Many to Many Adding to Collection - collections

I am using ASP.NET MVC 4 code first pattern for database layer. I have a many to many relationship between UserProfile and Task. When I try to add a task to the the collection of tasks of a user, it's added but if I try to query it and see if it's there it's not showing up.
My model:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string SirName { get; set; }
public string Position { get; set; }
public string Email { get; set; }
public ICollection<TaskModels> Tasks {get; set; }
public bool? isActive { get; set; }
public UserProfile()
{
Tasks = new HashSet<TaskModels>();
}
}
public class TaskModels
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public ICollection<UserProfile> Employees { get; set; }
public int TimeNeeded { get; set; }
public int TimeWorked { get; set; }
public string Status { get; set; }
public bool isActive { get; set; }
public TaskModels()
{
Employees = new HashSet<UserProfile>();
}
}
public class WorkLogModels
{
public int Id { get; set; }
public UserProfile Author { get; set; }
public DateTime TimeBeganWorking { get; set; }
public int TimeWorkedOn { get; set; }
public TaskModels Task { get; set; }
public string Description { get; set; }
}
public class TimeTrackerDb : DbContext
{
public TimeTrackerDb() : base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<TaskModels> Tasks { get; set; }
public DbSet<WorkLogModels> WorkLogs { get; set; }
}
I try to check if a UserProfile already exists in a Task's Employees list and it's always empty.
[HttpPost]
public ActionResult Create(WorkLogModels worklogmodels)
{
var tasks = db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).SingleOrDefault();
if (tasks == null)
{
return View(worklogmodels);
}
if (ModelState.IsValid)
{
var user = db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).FirstOrDefault();
var task = db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).FirstOrDefault();
WorkLogModels log = new WorkLogModels();
log.Description = worklogmodels.Description;
log.TimeBeganWorking = worklogmodels.TimeBeganWorking;
log.TimeWorkedOn = worklogmodels.TimeWorkedOn;
log.Author = user;
log.Task = task;
db.WorkLogs.Add(log);
if (!db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).First().Tasks.Any(x=> x.Name == worklogmodels.Task.Name))
{
db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).FirstOrDefault().Tasks.Add(task);
db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).FirstOrDefault().Employees.Add(user);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(worklogmodels);
}
I've been fighting with this for two days now.
Any help will be greatly appreciated
EDIT:
I am not sure if I made myself clear. In the Crate action for the WorkLog Controller I am trying to put the current user in the current task's collection and vice versa. It works correctly the first time, but then if I do it again it fails to skip the if statement and tries to add it once again and throws an exception : System.Data.SqlClient.SqlException. It's trying to add the same record to the intermediate table.

Related

Receiving AutoMapperMappingException

Currently I'm creating a new feature. It looks simple, but I am stuck at a problem with automapping dto to another one.
I have to create a wishlist [adding /deleting items of wishlist].
All works fine, except one thing: while adding an item to the wishlist, I'm get a message like this:
"type": "AutoMapperMappingException",
"message": "Error mapping types..."
However, I can see it got inserted into the database. Also, can delete it too. I understand the problem is linked to Automapper, but I could not figure out how to map correctly.
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
var itemCreated = await _wishListItemService.AddAsync(itemAdd);
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
//service
public async Task<WishlistItemDto> AddAsync(WishlistItemDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
var entityDetails = await _productDetailsRepository.GetById(item.ProductDetailId);
entity.ProductDetails = entityDetails;
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
DTOs:
public class WishListItemCreationDto
{
[Required]
public string CustomerId { get; set; }
[Required]
public int ProductDetailId { get; set; }
[Min(1)]
[Required]
public int Quantity { get; set; }
}
public class WishlistItemDto
{
public int Id { get; set; }
public string CustomerId { get; set; }
public int ProductDetailId { get; set; }
public ProductDetailsDtoWithPrimaryImage ProductDetails { get; set; }
public int Quantity { get; set; }
}
public class WishlistItem
{
public int Id { get; set; }
public string CustomerId { get; set; }
public Customer Customer { get; set; }
public int ProductDetailsId { get; set; }
public ProductDetails ProductDetails { get; set; }
public int Quantity { get; set; }
}
ProductDetails DTO:
public class ProductDetails
{
public int Id { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public IList<ProductAttributeValue> ProductAttributes { get; set; } = new List<ProductAttributeValue>();
public int Quantity { get; set; }
public string Sku => $"BRD{Id}";
public byte[] RowVersion { get; set; } = new byte[0];
}
public class ProductDetailsDtoWithPrimaryImage
{
public int Id { get; set; }
public int Quantity { get; set; }
public int ProductId { get; set; }
public ProductDisplayEntity Product { get; set; }
public IEnumerable<ProductAttributeWithValueDto> ProductAttributes { get; set; }
public byte[] RowVersion { get; set; }
public string Sku => $"BRD{Id}";
public int? PrimaryImageId { get; set; }
}
AutoMapper:
public WishlistItemProfile()
{
CreateMap<WishlistItem, WishListItemCreationDto>().ReverseMap();
CreateMap<WishlistItemDto, WishListItemCreationDto>().ReverseMap();
CreateMap<WishlistItem, WishlistItemDto>()
.ForMember(wi => wi.ProductDetailId, opt => opt.MapFrom(f => f.ProductDetailsId))
.ForMember(wi => wi.ProductDetails, opt => opt.MapFrom(f => f.ProductDetails))
.ReverseMap();
}
everything is okay, but you missed inner mapping of your classes.
What the error says:
Mapping types:
ProductDetailsDtoWithPrimaryImage -> ProductDetails
SimpleWebApi.Controllers.ProductDetailsDtoWithPrimaryImage -> SimpleWebApi.Controllers.ProductDetails
Add additional mapping in your constructor WishlistItemProfile
CreateMap<ProductDetails, ProductDetailsDtoWithPrimaryImage>().ReverseMap();
And it starts works perfect

Expression of type 'System.Collections.Generic.List`1[API.Entities.CompanySetting]' cannot be used for parameter of type 'System.Linq.IQueryable

I am trying to find a simple way using AutoMapper to return all companies that are linked to a specific User Id in a many-to-many relationship scenario. I followed the SO Automapper many to many mapping but I get the error message "Expression of type 'System.Collections.Generic.List`1[API.Entities.CompanySetting]' cannot be used for parameter of type 'System.Linq.IQueryable" when trying to follow the logic.
My AppUser entity:
public class AppUser
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<AppUserCompanySetting> AppUserCompanySettings { get; set; } = new List<AppUserCompanySetting>();
}
My CompanySetting entity:
public class CompanySetting
{
public int Id { get; set; }
public string CompanyName { get; set; }
public string CompanyRegistrationNumber { get; set; }
public bool isActive { get; set; }
public bool isArchived { get; set; }
public virtual ICollection<AppUserCompanySetting> AppUserCompanySettings { get; set; } = new List<AppUserCompanySetting>();
}
And I have the Join table
public class AppUserCompanySetting
{
public int AppUserId { get; set; }
public virtual AppUser AppUser { get; set; }
public int CompanySettingsId { get; set; }
public virtual CompanySetting CompanySettings { get; set; }
}
I then created a CompanySettingDto
public class CompanySettingDto
{
public int Id { get; set; }
public string CompanyName { get; set; }
public string CompanyRegistrationNumber { get; set; }
public bool isActive { get; set; }
public bool isArchived { get; set; }
}
And a MemberDto:
public class MemberDto
{
public int Id { get; set; }
public string Username { get; set; }
public string PhotoUrl { get; set; }
public string KnownAs { get; set; }
public int TimeActive { get; set; }
public DateTime LastActive {get; set;}
public ICollection<PhotoDto> Photos { get; set; }
public ICollection<CompanySettingDto> CompanyInformation { get; set; }
}
I then tried Automapper to bring the relationships between the User and the Company Information I require:
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<AppUser, MemberDto>()
.ForMember(dest => dest.CompanyInformation, opt => opt.MapFrom(x => x.AppUserCompanySettings.Select(y => y.CompanySettings).ToList()))
CreateMap<CompanySetting, CompanySettingDto>();
}
}
I am writing an API call to get all companies that are linked to a specific UserId.
public async Task<IEnumerable<MemberDto>> GetCompaniesByUserIdAsync(int userId)
{
return await _context.Users
.Where(x => x.Id == userId)
.ProjectTo<MemberDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}

How to save data in many to many relation without duplicate in my Card table [Entity Framework]

I have a problem. I cannot save cards without duplicates.
I have 3 tables a Deck, Card table and a CardDeck table which is the junction table.
The goal is to store a deck in the database without the cards being duplicated.
Table Card
namespace MTG_Deck.Models
{
public class Card
{
[Key]
public int CardId { get; set; }
public string Name { get; set; }
public string Text { get; set; }
public string Type { get; set; }
public string SetName { get; set; }
public string Artist { get; set; }
public string ManaCost { get; set; }
public string Rarity { get; set; }
public string ImageUrl { get; set; }
public string Toughness { get; set; }
public string Power { get; set; }
public string id { get; set; }
public virtual ICollection<DeckCard> Decks { get; set; }
}
}
Table Deck
namespace MTG_Deck.Models
{
public class Deck
{
[Key]
public int DeckId { get; set; }
[Required]
public string Name { get; set; }
[DataType(DataType.Date)]
public DateTime CreateAt { get; set; }
public User User { get; set; }
public virtual ICollection<DeckCard> Cards { get; set; }
}
}
Table CardDeck
namespace MTG_Deck.Models
{
public class DeckCard
{
public int DeckID { get; set; }
public int CardID { get; set; }
public Deck Deck { get; set; }
public Card Card { get; set; }
}
}
CardDeckRequest contains the content of the request.
namespace MTG_Deck.Models
{
public class CardDeckRequest
{
[Required]
public int UserID { get; set; }
[Required]
public string Token { get; set; }
[Required]
public Deck Deck { get; set; }
[Required]
[MinLength(5, ErrorMessage = "The deck must contain 60 cards.")]
public List<Card> Cards { get; set; }
}
}
Controller
[HttpPost]
public IActionResult Add([FromBody] CardDeckRequest request)
{
User user = _context.User.Where(u => u.Token == request.Token).FirstOrDefault<User>();
if (user == null) {
var error = new string[] {"You have to be connected to create a deck!"};
BadRequest(new { errors = new { success = error } });
}
List<DeckCard> deckList = new List<DeckCard>();
if (ModelState.IsValid) {
request.Deck.User = user;
foreach (var item in request.Cards)
{
Card card = _context.Card.FirstOrDefault(c => c.Name == item.Name);
if (card == null) {
card = item;
Console.WriteLine("coucou");
}
DeckCard deckCard = new DeckCard {
Card = card,
Deck = request.Deck
};
deckList.Add(deckCard);
}
_context.DeckCard.AddRange(deckList);
_context.SaveChanges();
var error = new string[] {"Your deck has been successfully saved."};
return Ok(new { errors = new { success = error } });
}
return BadRequest(ModelState.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToArray()
));
}

generate a list of products of a category

I am developing a shop application and I need to show products of each category. The problem is each product is created from a product template which is stored in a table and each template is related to a category. here is the product model:
namespace fardashahr.Models
{
[Table("Product")]
public class ProductModel
{
public ProductModel()
{
if (Specs == null)
{
Specs = new Dictionary<string, SpecItemsModel>();
}
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public int ProductTemplateId { get; set; }
[Required]
public bool IsPublished { get; set; }
[Required]
public bool InStock { get; set; }
[Range(95, 110)]
public float SyncRate { get; set; }
public DateTime? ProductionDate { get; set; }
[Required]
public DateTime RegisterationDate { get; set; }
public string ImageName { get; set; }
public IEnumerable<string> GalleryImages { get; set; }
[NotMapped]
public Dictionary<string, SpecItemsModel> Specs { get; set; }
[ForeignKey("ProductTemplateId")]
public virtual ProductTemplateModel ProductTemplate { get; set; }
[ForeignKey("ManufacturerId")]
public virtual CodingItemModel Manufacturer { get; set; }
[ForeignKey("BrandId")]
public virtual CodingItemModel Brand { get; set; }
[ForeignKey("ModelId")]
public virtual CodingItemModel Model { get; set; }
[ForeignKey("SeriesId")]
public virtual CodingItemModel Series { get; set; }
}
}
and here is the the ProductTemplate:
namespace fardashahr.Models
{
[Table("ProductTemplate")]
public class ProductTemplateModel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(500)]
public string Name { get; set; }
[Required]
public int CategoryId { get; set; }
[StringLength(500)]
public string Description { get; set; }
[ForeignKey("CategoryId")]
public virtual CategoryModel Category{ get; set; }
}
}
and the controller is:
namespace fardashahr.Controllers
{
public class ProductsController : Controller
{
// GET: Products
public ActionResult Index()
{
return RedirectToAction("Index", "Home");
}
public ActionResult Category(string name)
{
//declare a list of products
List<ProductModel> productList;
using(MainModel db = new MainModel())
{
//get category id
CategoryModel category = db.Categories.Where(x => x.CategorytUrl == name).FirstOrDefault();
int catId = category.Id;
//initialize the list
productList = db.Products.Where(x => x. == catId).ToList();
}
}
}
}
finaly, what I want to know is how to initialize a list of products.
In your models, you added virtual keyword which indicates that navigation property will be automatically loaded without the need of LINQ lambda .include() expression.
Hence you can immediately access the navigation property and load the list like this;
productList = db.Products.Where(x => x.ProductTemplate.CategoryId == catId).ToList();
string categoryNameOfFirstProduct = productList.FirstOrDefault().ProductTemplate.Category.Name;
string categoryNameOfFirstProduct = productList.FirstOrDefault().ProductTemplate.Category.CategorytUrl;

Select Max() with GroupBy, using linq method syntax, EF

I have a bunch of classes with some data:
public class Teacher
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime bDate { get; set; }
//One-to-one with course
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection <Student_Course> Student_Courses { get; set; }
public int TeacherId { get; set; }
public virtual Teacher Teacher { get; set; }
}
public class Grade
{
public int Id { get; set; }
public int Mark { get; set; }
//one-to-many with Student_Course
public int Student_CourseId { get; set; }
public Student_Course Student_Course { get; set; }
}
public class Student_Course
{
public int Id { get; set; }
//many-to-many with student
public int StudentId { get; set; }
public virtual Student Student { get; set; }
//many-to-many with course
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime bDate { get; set; }
//one-to-many with student_course
public virtual ICollection <Student_Course> Student_Courses { get; set; }
}
Now i trying to write a query - for everyone teacher(name) needs to output his best student(name) by student mark, and the best mark, from class Grades, my code:
public IActionResult Task9()
{
var task9 = Db.Grades.GroupBy(gr => gr.Student_Course.Course.Teacher.Name).Select(x => new Task9
{
NameOfSt = x.Select(st => st.Student_Course.Student.Name).FirstOrDefault(),//Needs to correct this line
NameOfTeac = x.Select(r => r.Student_Course.Course.Teacher.Name).FirstOrDefault(),
BestMark = x.Max(gr => gr.Mark)
//NameOfSt = Db.Students.FirstOrDefault(st => st.Id ==x.FirstOrDefault().Student_Course.StudentId).Name
});
return View(task9);
}
So that code output correct teacher name and best mark on the course from students. But the student name is actually wrong. How can i fix that? Thanks for any help.
Teac
BestMark
NameStud
You could use OrderByDescending by Mark before select student name:
var task9 = Db.Grades.GroupBy(gr => gr.Student_Course.Course.Teacher.Name).Select(x => new Task9
{
NameOfSt = x.OrderByDescending(st => st.Mark).Select(st => st.Student_Course.Student.Name).FirstOrDefault(),//Needs to correct this line
NameOfTeac = x.Select(r => r.Student_Course.Course.Teacher.Name).FirstOrDefault(),
BestMark = x.Max(gr => gr.Mark)
});
Within the GroupBy you need to use the value of the grouping (in this case x) to select the Grade object with the highest Grade. In the code below, I get the bestGrade object by ordering by the Mark in descending order and then taking the first entry and then I take the student, name and best mark from that object:
public IActionResult Task9()
{
var task9 = Db.Grades.GroupBy(gr => gr.Student_Course.Course.Teacher.Name).Select(x =>
{
var bestGrade = x.OrderByDescending(y => y.Mark).First();
return new Task9
{
NameOfSt = bestGrade.Student_Course.Student.Name,//Needs to correct this line
NameOfTeac = bestGrade.Student_Course.Course.Teacher.Name,
BestMark = bestGrade.Mark
};
});
return View(task9);
}

Resources