How do I define this relationship with code-first? - asp.net-web-api

I'm trying to create a library of books.
I separated data and users context/database for security reasons (and clarity) but is it that useful?
Specially since BookOfUserEntity.Id should be really a composite key between UserId & BookId.
Something like :
public class BookOfUserEntity
{
[Key]
public Guid UserId { get; set; }
[Key]
public int BookId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BookOfUserEntity>()
.HasKey(b => new { b.UserId, b.BookId });
}
But then if I create a composite key what should be HistoryEntity.BookOfUserEntityId type & value?
I also separated BookEntity & BookOfUserEntity to avoid duplicated data and reuse what can be reused (MainCoverArtId etc...), but this added complexity - should I go back to a simpler model?
public class BookEntity
{
public int Id { get; set; }
public Guid? MainCoverArtId { get; set; }
...
public virtual List<BookOfUserEntity>? BookOfUserEntities { get; set; } // Only purpose is to know how many Users have this very book in their list.
}
public class BookOfUserEntity // Bad name here don't mention it
{
public int Id { get; set; }
public Guid UserId { get; set; }
public int BookId { get; set; }
public BookEntity Book { get; set; } // Useful? Should be virtual?
public List<HistoryEntity>? HistoryEntities { get; set; }
...
}
public class HistoryEntity
{
public int Id { get; set; }
public int BookOfUserEntityId { get; set; }
public BookOfUserEntity BookOfUserEntity { get; set; } // Same questions
public int Vol { get; set; }
public double Chapter { get; set; }
public DateTime? ReadDate { get; init; }
}

Related

Why Entity Framework replaces provided value with a new incremental value

I'm trying to work my way through a Dot Net Core MVC project, but I am facing a problem I couldn't solve.
And honestly, I didn't know what to search for.
The project is a simple vehicle registry,
Each vehicle has a Make, a Model, a some Features.
Here are the domain models:
public class Make
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Model> Models { get; set; }
public Make()
{
Models=new Collection<Model>();
}
}
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
public Make Make { get; set; }
public int MakeId { get; set; }
public Model()
{
Make=new Make();
}
}
public class Feature
{
public int Id { get; set; }
public string Name { get; set; }
}
[Table(name:"VehicleFeatures")]
public class VehicleFeature
{
public int VehicleId { get; set; }
public int FeatureId { get; set; }
public Feature Feature { get; set; }
public Vehicle Vehicle { get; set; }
public VehicleFeature()
{
Feature=new Feature();
Vehicle=new Vehicle();
}
}
public class Vehicle
{
public int Id { get; set; }
public Model Model { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ModelId { get; set; }
public ICollection<VehicleFeature> VehicleFeatures { get; set; }
public string ContactName { get; set; }
public string ContactPhone { get; set; }
public string ContactEmail { get; set; }
public DateTime LastUpdate { get; set; }
public bool IsRegistered { get; set; }
public Vehicle()
{
Model=new Model();
VehicleFeatures=new Collection<VehicleFeature>();
}
}
The problem is that when I send the following request to the corresponding controller, EF is replacing the provided value with a new incremental value.
The request I send:
{
"ModelId":10,
"VehicleFeatures":[
{"featureId":45},
{"featureId":46}
],
"ContactName":"Alice",
"ContactPhone":"1234",
"ContactEmail":"Alice#local",
"LastUpdate":"1980-01-01",
"IsRegistered":true
}
And this is what happens:
The controller receives correct values,
Correct values are added to the context,
And then suddenly all hell breaks loose.
This is the controller code:
[HttpPost]
public async Task<IActionResult> CreateVehicle([FromBody] Vehicle v)
{
context.Vehicles.Add(v);
await context.SaveChangesAsync();
return Ok(v.ModelId);
}
What am I missing here?

Querying Many to Many relationship table to entities MVC

I have many to many relationship tables. Sube and User
namespace Odev.Entities
{
public class User
{
[Key]
public int Id { get; set; }
[Required]
[DisplayName("Kimlik No")]
[ StringLength(11)]
[Index(IsUnique = true)]
public string UserName { get; set; }
[ StringLength(8)]
[DisplayName("Şifre")]
public string Password { get; set; }
[DisplayName("Ad Soyad")]
public string NameSurname { get; set; }
[DisplayName("Bölüm")]
public string Bolum { get; set; }
[DisplayName("Dal")]
public string Dal { get; set; }
[DisplayName("Öğrenci No")]
public int OgrenciNo { get; set; }
public int RoleId { get; set; }
public virtual ICollection<Sube> Subes { get; set; }
public virtual List<Notification> Notifications { get; set; }
public virtual List<Homework> Homeworks { get; set; }
public virtual Role Role { get; set; }
}
}
namespace Odev.Entities
{
public class Sube
{
[Key]
public int Id { get; set; }
[DisplayName("Şube")]
public string Sube_Name { get; set; }
public virtual Homework Homework { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
namespace Odev.DataAccessLayer
{
public class DatabaseContext : DbContext
{
public DatabaseContext() : base("dataConnection")
{
Database.SetInitializer(new OdevInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<Homework> Homeworks { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Sube> Subes { get; set; }
public DbSet<Notification> Notifications { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
and my ViewModel
namespace Odev.Models{
public class ViewModel{
public string Sube_Name { get; set; }
public string NameSurname { get; set; }
public string UserName { get; set; }
public int UserId { get; set; }
public int SubeId { get; set; }
public User User { get; set; }
public Sube Sube { get; set; }
public Role Role { get; set; }
}}
I want to show User's SubeId:
How I select Sube's id ?
You need connect both entities with a foreign key.
Please, read this doc: Creating a More Complex Data Model for an ASP.NET MVC Application

EF 5 ENUM with Navigation

I'm trying to use Enum support with EF 5 and dot.NET 4.5.
The situation I have is the following.
POCO:
public partial class tbl_pp_Message
{
public tbl_pp_Message()
{
this.tbl_pp_MessageDistribution = new List<tbl_pp_MessageDistribution>();
}
public int MessageId { get; set; }
public string Subject { get; set; }
public string From { get; set; }
public string Body { get; set; }
public int PriorityId { get; set; }
public System.DateTime CreateDate { get; set; }
public int CreateById { get; set; }
[ForeignKey("PriorityId")]
public virtual tbl_pp_MessagePriority tbl_pp_MessagePriority { get; set; }
public virtual ICollection<tbl_pp_MessageDistribution> tbl_pp_MessageDistribution { get; set; }
}
public partial class tbl_pp_MessagePriority
{
public tbl_pp_MessagePriority()
{
this.tbl_pp_Message = new List<tbl_pp_Message>();
}
public int MessagePriorityId { get; set; }
public string Description { get; set; }
public Nullable<int> DisplayOrder { get; set; }
public bool ShowAlert { get; set; }
public virtual ICollection<tbl_pp_Message> tbl_pp_Message { get; set; }
}
With the code listed above NOT using an ENUM the world is happy.
When I try to add an Enum the POCO will look like this.
[Flags]
public enum MessagePriorityEnum : int
{
NONE = 0,
Urgent = 1,
Normal = 2,
Low = 3
}
public partial class tbl_pp_Message
{
public tbl_pp_Message()
{
this.tbl_pp_MessageDistribution = new List<tbl_pp_MessageDistribution>();
}
public int MessageId { get; set; }
public string Subject { get; set; }
public string From { get; set; }
public string Body { get; set; }
public MessagePriorityEnum PriorityId { get; set; }
public System.DateTime CreateDate { get; set; }
public int CreateById { get; set; }
[ForeignKey("PriorityId")]
public virtual tbl_pp_MessagePriority tbl_pp_MessagePriority { get; set; }
public virtual ICollection<tbl_pp_MessageDistribution> tbl_pp_MessageDistribution { get; set; }
}
This will not work during a save because it doesn't like the ForeignKey attribute. This error will be thrown during a save.
Schema specified is not valid. Errors:
(129,6) : error 0112: The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'PriorityId' on entity 'tbl_pp_Message' does not match the type of property 'MessagePriorityId' on entity 'tbl_pp_MessagePriority' in the referential constraint 'tbl_pp_MessagePriority_tbl_pp_Message'.
If the navigation for tbl_pp_MessagePriority is removed from tbl_pp_Message POCO. And the navigation removed from tbl_pp_MessagePriority POCO back to tbl_pp_Message then it works.
With out the navigation removed it will generate a SQL statement like this.
insert [dbo].[tbl_pp_Message]
([Subject],
[From],
[Body],
[PriorityId],
[CreateDate],
[CreateById],
[tbl_pp_MessagePriority_MessagePriorityId])
values ('Test ENUM EF Code First 5.0' /* #0 */,
'Daniel.Bivens' /* #1 */,
'Test 01 Enum Body - The Magic Unicorn Edition' /* #2 */,
2 /* #3 */,
'2013-05-23T10:52:09' /* #4 */,
5 /* #5 */,
null)
select [MessageId]
from [dbo].[tbl_pp_Message]
where ##ROWCOUNT > 0
and [MessageId] = scope_identity()
It would be ok if the tbl_pp_MessagePriority_MessagePriorityId was not added to the insert.
What can be done to keep the navigaion in EF code first while using ENUM support? Is there a DataAnnotation or a Mapping API?
Please post any suggestions you may have. This project is not using an EDMX/T4 template.
Working POCO:
public partial class tbl_pp_Message
{
//public tbl_pp_Message()
//{
// this.tbl_pp_MessageDistribution = new List<tbl_pp_MessageDistribution>();
//}
public int MessageId { get; set; }
public string Subject { get; set; }
public string From { get; set; }
public string Body { get; set; }
public MessagePriorityEnum PriorityId { get; set; }
public System.DateTime CreateDate { get; set; }
public int CreateById { get; set; }
//[ForeignKey("PriorityId")]
//public virtual tbl_pp_MessagePriority tbl_pp_MessagePriority { get; set; }
//public virtual ICollection<tbl_pp_MessageDistribution> tbl_pp_MessageDistribution { get; set; }
}
public partial class tbl_pp_MessagePriority
{
//public tbl_pp_MessagePriority()
//{
// this.tbl_pp_Message = new List<tbl_pp_Message>();
//}
public int MessagePriorityId { get; set; }
public string Description { get; set; }
public Nullable<int> DisplayOrder { get; set; }
public bool ShowAlert { get; set; }
//public virtual ICollection<tbl_pp_Message> tbl_pp_Message { get; set; }
}
The working POCO would be ok if no navigation was needed.
The thing I was missing was setting the look-up table primary key to the same ENUM type instead of an int.
Working POCO with ENUM and navigation.
public partial class tbl_pp_Message
{
public tbl_pp_Message()
{
this.tbl_pp_MessageAction = new List<tbl_pp_MessageAction>();
this.tbl_pp_MessageAttachment = new List<tbl_pp_MessageAttachment>();
this.tbl_pp_MessageDistribution = new List<tbl_pp_MessageDistribution>();
}
public int MessageId { get; set; }
public string Subject { get; set; }
public string From { get; set; }
public string Body { get; set; }
public MessageTypeEnum TypeId { get; set; }
public System.DateTime CreateDate { get; set; }
public int CreateById { get; set; }
[ForeignKey("TypeId")]
public virtual tbl_pp_MessageType tbl_pp_MessageType { get; set; }
public virtual ICollection<tbl_pp_MessageAction> tbl_pp_MessageAction { get; set; }
public virtual ICollection<tbl_pp_MessageAttachment> tbl_pp_MessageAttachment { get; set; }
public virtual ICollection<tbl_pp_MessageDistribution> tbl_pp_MessageDistribution { get; set; }
}
public partial class tbl_pp_MessageType
{
public tbl_pp_MessageType()
{
this.tbl_pp_Message = new List<tbl_pp_Message>();
}
[Key]
public MessageTypeEnum MessageTypeId { get; set; }
public string Description { get; set; }
public Nullable<int> DisplayOrder { get; set; }
public bool ShowAlert { get; set; }
public virtual ICollection<tbl_pp_Message> tbl_pp_Message { get; set; }
}

EF-code-first many-to-many related objects generates two different SQL tables (MVC3)

I have read this great Q&A and I tried to make something similar. My Model classes are:
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Course> CoursesAttending { get; set; }
public Person()
{
this.CoursesAttending = new List<Course>();
}
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public ICollection<Person> Students { get; set; }
}
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
public virtual Person Student { get; set; }
public virtual Course StudentCourse { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
public class SchoolContext : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Person> People { get; set; }
public DbSet<PersonCourse> PersonCourseLinks { get; set; }
public SchoolContext()
: base("ManyToManyTest")
{
}
}
Now, I try to add a new person and add a new course to his courses list:
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
Course studentCourse;
try
{
studentCourse = db.Courses.ToList<Course>().First();
}
catch
{
studentCourse = new Course() { Title = "HTML" };
}
person.CoursesAttending.Add(studentCourse);
db.People.Add(person);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(person);
}
Everything goes well, but when I open my created database, I see there are 2 link tables for the Person and Course classes - PersonCourses(with fields: PersonID, CourseID, Mark, Comment) and PersonCourse1(with fields: PersonID, CourseID), and only PersonCourse1 has rows (actually one row). Why does it happen? Did I do something not correct? I expect to see only one link table - the PersonCourses table...
I expect to see only one link table - the PersonCourses table
Then you have to link PersonCourse entity with Person entity though CoursesAttending navigational property. Same thing has to be done to Course entity.
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PersonCourse> CoursesAttending { get; set; }
public Person()
{
this.CoursesAttending = new List<PersonCourse>();
}
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; }
public ICollection<PersonCourse> Students { get; set; }
}
Give foreign key associations to the foreign key properties in your gerund table:
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Student { get; set; }
[ForeignKey("CourseID")]
public virtual Course StudentCourse { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
Just so you now why your code wasn't working as expected:
public class PersonCourse
{
[Key, Column(Order = 0)]
public int PersonID { get; set; }
[Key, Column(Order = 1)]
public int CourseID { get; set; }
public virtual Person Person { get; set; } // Follow built in conventions
public virtual Course Course { get; set; }
public int Mark { get; set; }
public string Comment { get; set; }
}
This now follows the built in Entity Framework convention of having
public int [ClassName][Id] or [Id] //Database Foreign Key column
and
public NavigationProperty [ClassName] // Navigation Property

Entity Framework also Linq advice needed

i have got on my DB 3 tables
movies, workers, workermovies ( this is the Relationship table )
public class Movie
{
public Movie()
{
Genres = new List<Genre>();
Formats = new List<Format>();
ProductionCompanies = new List<ProductionCompany>();
Workers = new List<Worker>();
}
public int Id { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string StoryLine { get; set; }
public int RunTime { get; set; }
[ForeignKey("MPAARateId")]
public MPAARate MPAARate { get; set; }
public int MPAARateId { get; set; }
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
public DateTime CreatedDate { get; set; }
public string OfficialSite { get; set; }
public int Budget { get; set; }
public int StatusId { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
public virtual ICollection<Format> Formats { get; set; }
public virtual ICollection<ProductionCompany> ProductionCompanies { get; set; }
public virtual ICollection<Worker> Workers { get; set; }
}
public class Worker
{
public int Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
public string Biography { get; set; }
public string BornName { get; set; }
public double Height { get; set; }
public DateTime? Died { get; set; }
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
public bool IsActor { get; set; }
public bool IsDirector { get; set; }
public bool IsWriter { get; set; }
public bool IsProducer { get; set; }
public bool IsStar { get; set; }
public virtual ICollection<Movie> Movies { get; set; }
}
in this Relation i got the movieId and the workerId
but i also got some more fields if the person acted or writen or producer etc.
how do i define the relation entity class if needed
and when i want to get just the ppl that acted in the movie how do i wrote such a linq
query
You need to introduce an additional entity in your model WorkerMovie and convert the many-to-many relationship between Worker and Movie into two one-to-many relationships - one between Worker and WorkerMovie and the other between Movie and WorkerMovie. A sketch:
public class WorkerMovie
{
[Key, Column(Order = 0)]
public int WorkerId { get; set; }
[Key, Column(Order = 1)]
public int MovieId { get; set; }
public Worker Worker { get; set; }
public Movie Movie { get; set; }
public bool WorkedAsActor { get; set; }
public bool WorkedAsWriter { get; set; }
public bool WorkedAsProducer { get; set; }
// etc.
}
public class Movie
{
// ...
public virtual ICollection<WorkerMovie> WorkerMovies { get; set; }
// remove ICollection<Worker> Workers
}
public class Worker
{
// ...
public virtual ICollection<WorkerMovie> WorkerMovies { get; set; }
// remove ICollection<Movie> Movies
}
If you want only to find the workers who were actors in a particular movie with a movieId you can write:
var workersAsActors = context.WorkerMovies
.Where(wm => wm.MovieId == movieId && wm.WorkedAsActor)
.Select(wm => wm.Worker)
.ToList();
Here is another answer to a very similar question with many more examples of possible queries: Create code first, many to many, with additional fields in association table

Resources