How to add JObject to Entity Framework as an object - asp.net

I have a list of variables representing deserlialized json jobjects that I would like to add them to the entity database. How can I do this?
The solution attempted so far:
var obj = JsonConvert.DeserializeObject<dynamic>(strtest);
foreach (var item in obj.results)
{
db.ReedAPIModels.Add(item);
db.SaveChanges();
}
Model class represents the database:
public class APIModel
{
[Key]
public int jobId { get; set; }
public int employerId { get; set; }
public string employerName { get; set; }
public string employerProfileId { get; set; }
public string employerProfileName { get; set; }
public string jobTitle { get; set; }
public string locationName { get; set; }
public int minimumSalary { get; set; }
public int maximumSalary { get; set; }
public string currency { get; set; }
public string expirationDate { get; set; }
public string date { get; set; }
public string jobDescription { get; set; }
public int applications { get; set; }
public string jobUrl { get; set; }
}
API Results
{
"results": [
{
"jobId": 39650785,
"employerId": 375315,
"employerName": "Spectrum IT Recruitment (South)",
"employerProfileId": null,
"employerProfileName": null,
"jobTitle": "Senior Front End Developer",
"locationName": "Reading",
"minimumSalary": 60000,
"maximumSalary": 70000,
"currency": "GBP",
"expirationDate": "17/01/2020",
"date": "03/01/2020",
"jobDescription": " Senior Front End Developer - Reading, Berkshire Senior Front End Developer required by a growing company based in Reading, Berkshire. The company are looking to recruit a Senior Front End Developer to join their expanding development team to work on the full design and development lifecycle of new products. The successful Senior Front End Developer will have lots of opportunities for development of both techni... ",
"applications": 0,
"jobUrl": "https://www.reed.co.uk/jobs/senior-front-end-developer/39650785"
}
],
"ambiguousLocations": [],
"totalResults": 9007
}
Also, I have tried to use ToObject method

First of all you need a RootObject model which can be created by this website:
http://json2csharp.com/
RootObject datalist = JsonConvert.DeserializeObject<RootObject>(strtest);
foreach (var item in datalist.results)
{
db.APIModels.Add(item);
db.SaveChanges();
}

You should Add APIModel instead of ReedAPIModels.
Your can try this way
var apiResult = JsonConvert.DeserializeObject<dynamic>(strtest);
foreach (var item in apiResult.results)
{
db.APIModel.Add(item);
db.SaveChanges();
}

Related

.NET web API one to many relation

I'm building a one to many relation web API with .NET and got error 400.
I have two tables:
public class CarBrand
{
public int CarBrandId { get; set; }
public string Name { get; set; }
[JsonIgnore]
public List<CarModel> CarModels { get; set; } = new List<CarModel>();
}
public class CarModel
{
public int CarModelId { get; set; }
public string ModelName { get; set; }
public int CarBrandId { get; set; }
[JsonIgnore]
public CarBrand CarBrand { get; set; } = new CarBrand();
}
My Controller is:
[HttpPost]
public async Task<ActionResult<CarModel>> PostCarModel(CarModel carModel)
{
_context.CarModels.Add(carModel);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCarModel", new { id = carModel.CarModelId }, carModel);
}
But when I make a POST request and try to add CarModel, I get error 400:
CarBrand:[
"This Field is required!"
]
Thanks!

Translate LINQ to LAMBDA Entity Framework Core using INCLUDE (NOT JOIN)

How to call multiple entities using Include method (not Join method) in Entity Framework Core? I am trying to translate this LINQ query to EF Core 5 syntax, but I do not know how to call multiple entities and join them together using include method.
var reservations = from reservation in _dbContext.Reservations
join customer in _dbContext.Users on reservation.UserId equals customer.Id
join movie in _dbContext.Movies on reservation.MovieId equals movie.Id
select new
{
Id = reservation.Id,
ReservationTime = reservation.ReservationTime,
CustomerName = customer.Id,
MovieName = movie.Name
};
I tried using multiple include and select method, but do not know how to call multiple entities and join
Here are my models
public class Reservation
{
public int Id { get; set; }
public int Qty { get; set; }
public double Price { get; set; }
public string Phone { get; set; }
public DateTime ReservationTime { get; set; }
public int MovieId { get; set; }
public int UserId { get; set; }
}
public class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Language { get; set; }
public string Duration { get; set; }
public DateTime PlayingDate { get; set; }
public DateTime PlayingTime { get; set; }
public double TicketPrice { get; set; }
public double Rating { get; set; }
public string Genre { get; set; }
public string TrailorUrl { get; set; }
public string ImageUrl { get; set; }
[NotMapped]
public IFormFile Image { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Role { get; set; }
public ICollection<Reservation> Reservations { get; set; }
}
Controller code:
var reservations = _dbContext.Reservations
.Include(r => r.Id)
.Include(c => c.User)
.Select(x => new { x.Id, x.ReservationTime, x.User, x.User.Name });
If add to Reservation navigation properties Movie and User, your query can be simplified. Include cannot be used with Select together, it is ignored by EF translator.
var reservations = _dbContext.Reservations
.Select(r => new
{
Id = r.Id,
ReservationTime = r.ReservationTime,
CustomerName = r.User.Id,
MovieName = r.Movie.Name
});

Why is my database table not loading using in ASP.NET Core

I am using a dbinitializer to try and seed data to a couple tables on my database. Everything seems to be ok except my Enrolls table doesn't want to populate. The Enrolls table holds all the List<> values of my Students and Courses. From that Enroll table I should be able to see which student is enrolled in which course. I am following a Microsoft docs tutorial but seem to hit a snag.
The thing is when I include the Enrolls property while creating an object in the initializer the entire database does not seed. However when I comment it out all tables except Enroll seed. I poked around a bit before I came here. Just hoping for a little guidance with some fresh pair of eyes as to where I should check/debug.
Truly hope I asked in a way that is understandable.
This is the DbInitializer:
public static class DbInitializer
{
public static void Initialize(StudentRegistrationContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Students.Any() )
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student
{
FirstName="A",
LastName="B",
Address = new Address
{
HouseNumber = "19",
Street="A ",
City="C",
Country = "T",
Email="s",
PhoneNumber = "6"
},
**// Enrollments = new List<Enroll> { new Enroll { CourseID = 2, ID=1 } }**
},
},
};
foreach (Student s in students)
{
context.Students.Add(s);
}
context.SaveChanges();
var course = new Course[]
{
new Course{CourseName = "Introduction to Computer Hardware",CourseCode = "ITEC120", NoOfCredits = 3, Category = Category.COMPULSORY},
new Course{ CourseName = "Introduction to Operating Systems", CourseCode= "ITEC122", NoOfCredits = 3, Category = Category.COMPULSORY },
new Course{ CourseName = "Programming 1",CourseCode = "ITEC133", NoOfCredits = 3, Category = Category.COMPULSORY},
new Course{CourseName = "Human and Computer Interface Design", CourseCode = "ITEC229", NoOfCredits = 3, Category = Category.COMPULSORY},
new Course{CourseName = "Webpage Design", CourseCode = "ITEC240", NoOfCredits = 3, Category = Category.COMPULSORY},
new Course{CourseName = "Computer Networks Architecture and Protocol",
};
foreach (Course c in course)
{
context.Course.Add(c);
}
context.SaveChanges();
var teacher = new Advisor[]
{
new Advisor{
FirstName="Teacher",
LastName="One",
Address = new Address{ HouseNumber = "123456",
Street="Teacher Street",
City="College City",
Country = "Trinidad",
Email="teacher#gmail.com",
PhoneNumber = "5648965"
},
Department="Department1",
Specialization=Category.ISD
},
};
foreach (Advisor t in teacher)
{
context.Advisor.Add(t);
}
context.SaveChanges();
var enroll = new Enroll[]
{
new Enroll{CourseID = 2, ID =2 },
};
foreach (Enroll e in enroll)
{
context.Enroll.Add(e);
}
context.SaveChanges();
}
}
My model classes:
public class Enroll
{
public int ID { get; set; }
public int CourseID { get; set; }
public Student Student { get; set; }
public Course Course { get; set; }
// public DateTime RegistrationDate { get; set; }
}
public class Student : Person
{
// public List<Course> Attends { get; set; }
public DateTime RegistrationDate { get; set; }
public List<Enroll> Enrollments { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public string CourseCode { get; set; }
public double NoOfCredits { get; set; }
public Category Category { get; set; }
public List<Enroll> Enrollments { get; set; }
}
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
}
So I figured out my issues. It was a mixture of ...
Editing the OnModelCreating function in the context
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Enroll>()
.HasOne(s => s.Student)
.WithMany(en => en.Enrollments)
.HasForeignKey(fk => fk.ID);
modelBuilder.Entity<Enroll>()
.HasOne(s => s.Course)
.WithMany(en => en.Enrollments)
.HasForeignKey(fk => fk.CourseID);
}
....and making my collection properties virtual:
public class Student : Person
{
public DateTime RegistrationDate { get; set; }
public virtual List<Enroll> Enrollments { get; set; }
}
public class Enroll
{
public int ID { get; set; }
public int CourseID { get; set; }
public virtual Student Student { get; set; }
public virtual Course Course { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public string CourseCode { get; set; }
public double NoOfCredits { get; set; }
public Category Category { get; set; }
public virtual List<Enroll> Enrollments { get; set; }
}
Thanks guys

Entity Framework 1:1 relationship Code First

I'm struggling here. I've tried through data annotations and via the Fluent API and still not working correctly. Desperate for help now. Basically, I have two tables. A Company table and an Address Table. A company must have a head office address (which should live in the Address Table) and an Address must have a Company which is belongs too. I'm really struggling to set this up correctly.
I'll put the Code First Entities then show what I have already got.
[Table("Address")]
public class Address
{
[Key]
public long AddressId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string Address4 { get; set; }
public string Address5 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Country { get; set; }
public string PostCode { get; set; }
public virtual Company Company { get; set; }
public DateTime? RemovedDate { get; set; }
public long? RemovedBy { get; set; }
}
[Table("Company")]
public class Company
{
[Key ]
public long CompanyId { get; set; }
public string Name { get; set; }
public string WebsiteUrl { get; set; }
public virtual Address Address { get; set; }
public User LeadUser { get; set; }
public DateTime ActiveSince { get; set; }
public DateTime? ActiveTill { get; set; }
public string VatRegistration { get; set; }
public string LicenseKey { get; set; }
public LicenseStatus LicenseStatus { get; set; }
public bool CanAgreementBeExtended { get; set; }
public string BillingEmail { get; set; }
public string PhoneNumber { get; set; }
public string MobileNumber { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateUpdated { get; set; }
public virtual ICollection<User> Users { get; set; }
public virtual ICollection<LicenseHistory> LicenseHistories { get; set; }
}
//Seeded data inserted as follows
var testCompany = new Company
{
ActiveSince = DateTime.UtcNow,
Name = "Test Company",
LeadUser = adminUser,
DateCreated = DateTime.UtcNow,
DateUpdated = DateTime.UtcNow,
BillingEmail = "admin#test.co.uk",
CanAgreementBeExtended = true,
LicenseStatus = LicenseStatus.PendingAgreement,
MobileNumber = "1234567890",
PhoneNumber = "1234567890",
VatRegistration = "1234567890"
};
context.Companies.AddOrUpdate(u => u.Name, testCompany);
var testAddress = new Address
{
Address1 = "Test Ltd",
Address2 = "1 Test Gardens",
Address3 = "Test Heath",
Address4 = string.Empty,
Address5 = string.Empty,
County = "Test",
Town = "Test",
Country = "United Kingdom",
PostCode = "TE5 T11",
Company = testCompany
};
context.Addresses.AddOrUpdate(u => new { u.AddressId }, testAddress);
testCompany.Address = testAddress;
context.Companies.AddOrUpdate(u => u.Name, testCompany);
//Fluent API set up as follows in the OnModelCreating
modelBuilder.Entity<Address>()
.HasRequired(ad => ad.Company)
.WithOptional(s => s.Address);
Can anyone spot what I'm doing wrong? I've been playing round with different combinations for the past few days and it just doesn't work. I just keep getting errors, the latest error based on the code above is...
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'AddressId'.
Any ideas please?
You can't have a true one to one in SQL Server (see How do I create a real one-to-one relationship in SQL Server), but there is a workaround in EF where you make the primary key of the second entity also a foreign key:
// [Table("Company")] -- not needed unless different
public class Company
{
// [Key ] -- will be key by convention
public long CompanyId { get; set; }
...
public virtual Address Address { get; set; }
}
public class Address
{
[Key, ForeignKey("Company")]
public long AddressId { get; set; }
public string Address1 { get; set; }
...
public virtual Company Company { get; set; }
}
You can also do it with fluent code like:
modelBuilder.Entity<Company>()
.HasRequired(t => t.Address)
.WithRequiredPrincipal(t => t.Company);

Code-First Entity Framework Multiple Collections with Many to Many

I have another Entity Framework question here. I have a complicated object called Book and that object has a number of collections of type Contributor such as Writer, Letterer, Colorist, etc. Contributors are not necessarily scoped to a particular role though. So the same contributor (with the same ContributorId) could be both a Writer and a Colorist, for example.
public Book {
public ICollection<Contributor> Writers { get; set; }
public ICollection<Contributor> Artists { get; set; }
public ICollection<Contributor> Pencilers { get; set; }
public ICollection<Contributor> Inkers { get; set; }
public ICollection<Contributor> Colorists { get; set; }
public ICollection<Contributor> Letterers { get; set; }
public ICollection<Contributor> CoverArtists { get; set; }
public ICollection<Contributor> OtherContributors { get; set; }
}
public Contributor {
public int ContributorId { get; set; }
public string Name { get; set; }
}
I am having trouble, viewing the examples I have found here and on other sites, determining how I would signify the appropriate model. I would expect a Db Model something like this. What I want to avoid is a model wherein I have a separate table for every Contributor Role, or a separate row in the Contributor table for every instance in which a contributor is associated with a book in any role.
+ Books
--BookId
+ Contributors
--ContributorId
+ BookContributors
--BookId
--ContributorId
--Discriminator
I am such as ADO.NET guy that I am not really finding this too enjoyable, but I am determined to become at least borderline proficient in this important framework.
A Quick Note:
Since opening this question, I got pulled away at work and haven't had the time to thoroughly review the answers and play around with the results. But I didn't want to leave the bounty hanging as I appreciate the answers everyone has provided. So I selected the answer that appeared of the most interest to me starting out. I want to thank everyone though for this.
I have worked on a solution that implements the model you proposed although it works a bit different than what you would expect. Hope this answers your question.
Models
[Table("Book")]
public class Book
{
[Column("BookId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BookId { get; set; }
[NotMapped]
public ICollection<Contributor> Writers { get; set; }
[NotMapped]
public ICollection<Contributor> Artists { get; set; }
[NotMapped]
public ICollection<Contributor> Pencilers { get; set; }
[NotMapped]
public ICollection<Contributor> Inkers { get; set; }
[NotMapped]
public ICollection<Contributor> Colorists { get; set; }
[NotMapped]
public ICollection<Contributor> Letterers { get; set; }
[NotMapped]
public ICollection<Contributor> CoverArtists { get; set; }
[NotMapped]
public ICollection<Contributor> OtherContributors { get; set; }
}
[Table("Contributor")]
public class Contributor
{
[Column("ContributorId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContributorId { get; set; }
}
// Contributor Type is one of the following options: Writer, Artist, Penciler, etc.
[Table("ContributorType")]
public class ContributorType
{
[Column("ContributorTypeId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContributorTypeId { get; set; }
[Column("Name")]
public string Name { get; set; }
}
[Table("BookContributor")]
public class BookContributor
{
[Column("BookContributorId")]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int BookContributorId { get; set; }
[Column("BookId")]
public int BookId { get; set; }
[Column("ContributorId")]
public int ContributorId { get; set; }
[Column("RoleId")]
public int RoleId { get; set; }
[ForeignKey("BookId")]
public virtual Book Book { get; set; }
[ForeignKey("ContributorId")]
public virtual Contributor Contributor { get; set; }
[ForeignKey("RoleId")]
public virtual ContributorType Role { get; set; }
}
Database Context
AppDbContext.cs:
public class AppDbContext : DbContext
{
public AppDbContext()
{
Database.SetInitializer<AppDbContext>(new AppDbInitializer());
}
public AppDbContext(string connectionString)
: base(connectionString)
{
Database.SetInitializer<AppDbContext>(new AppDbInitializer());
}
public DbSet<Book> Books { get; set; }
public DbSet<Contributor> Contributors { get; set; }
public DbSet<ContributorType> ContributorTypes { get; set; }
public DbSet<BookContributor> BookContributors { get; set; }
}
AppDbInitializer.cs:
public class AppDbInitializer : DropCreateDatabaseAlways<AppDbContext>
{
protected override void Seed(AppDbContext context)
{
// default contributor types
var contributorTypes = new List<ContributorType>();
contributorTypes.Add(new ContributorType() { Name = "Writer" });
contributorTypes.Add(new ContributorType() { Name = "Artist" });
contributorTypes.Add(new ContributorType() { Name = "Penciler" });
contributorTypes.Add(new ContributorType() { Name = "Inker" });
contributorTypes.Add(new ContributorType() { Name = "Colorist" });
contributorTypes.Add(new ContributorType() { Name = "Letterer" });
contributorTypes.Add(new ContributorType() { Name = "CoverArtist" });
contributorTypes.Add(new ContributorType() { Name = "OtherContributor" });
// adding it to the context
foreach (var type in contributorTypes)
context.ContributorTypes.Add(type);
base.Seed(context);
}
}
Wrapping everything together
Program.cs:
class Program
{
static void Main(string[] args)
{
// enter name of the connection string in App.Config file
var connectionSettings = ConfigurationManager.ConnectionStrings["..."];
using (var dbContext = new AppDbContext(connectionSettings.ConnectionString))
{
// Creating a book
var book = new Book();
dbContext.Books.Add(book);
dbContext.SaveChanges();
// Creating contributor
var contributor = new Contributor();
dbContext.Contributors.Add(contributor);
dbContext.SaveChanges();
// Adding contributor to the book
var bookContributor = new BookContributor()
{
BookId = book.BookId,
ContributorId = contributor.ContributorId,
RoleId = dbContext.ContributorTypes.First(t => t.Name == "Writer").ContributorTypeId
};
dbContext.BookContributors.Add(bookContributor);
dbContext.SaveChanges();
// retrieving a book
var book = dbContext.Books.Where(b => b.BookId == 2).FirstOrDefault();
if (book != null)
{
book.Writers =
from contributor in dbContext.Contributors
join bookContributor in dbContext.BookContributors on contributor.BookId equals bookContributor.BookId
join contributorType in dbContext.ContributorTypes on contributorType.ContributorTypeId equals bookContributor.ContributorTypeId
where
bookContributor.BookId == 2 and
contributorType.Name == "Writer"
select contributor;
// do the same for other types of contributors
}
}
}
}
Create similar collections in the Contributor entity with M:N mapping and use the InverseProperty attribute to declare which collection in Contributor class corresponds with which collection in the Book class.
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Contributor> Writers { get; set; }
public virtual ICollection<Contributor> Artists { get; set; }
public virtual ICollection<Contributor> Pencilers { get; set; }
public virtual ICollection<Contributor> Inkers { get; set; }
public virtual ICollection<Contributor> Colorists { get; set; }
public virtual ICollection<Contributor> Letterers { get; set; }
public virtual ICollection<Contributor> CoverArtists { get; set; }
public virtual ICollection<Contributor> OtherContributors { get; set; }
public Book()
{
Writers = new List<Contributor>();
Artists = new List<Contributor>();
Pencilers = new List<Contributor>();
Inkers = new List<Contributor>();
Colorists = new List<Contributor>();
Letterers = new List<Contributor>();
CoverArtists = new List<Contributor>();
OtherContributors = new List<Contributor>();
}
}
public class Contributor
{
public int ContributorId { get; set; }
public string Name { get; set; }
[InverseProperty("Writers")]
public virtual ICollection<Book> WriterOfBooks { get; set; }
[InverseProperty("Artists")]
public virtual ICollection<Book> ArtistOfBooks { get; set; }
[InverseProperty("Pencilers")]
public virtual ICollection<Book> PencilerOfBooks { get; set; }
[InverseProperty("Inkers")]
public virtual ICollection<Book> InkerOfBooks { get; set; }
[InverseProperty("Colorists")]
public virtual ICollection<Book> ColoristOfBooks { get; set; }
[InverseProperty("Letterers")]
public virtual ICollection<Book> LettererOfBooks { get; set; }
[InverseProperty("CoverArtists")]
public virtual ICollection<Book> CoverArtistOfBooks { get; set; }
[InverseProperty("OtherContributors")]
public virtual ICollection<Book> OtherContributorOfBooks { get; set; }
public Contributor()
{
WriterOfBooks = new List<Book>();
ArtistOfBooks = new List<Book>();
PencilerOfBooks = new List<Book>();
InkerOfBooks = new List<Book>();
ColoristOfBooks = new List<Book>();
LettererOfBooks = new List<Book>();
CoverArtistOfBooks = new List<Book>();
OtherContributorOfBooks = new List<Book>();
}
}
The usage is then quite simple:
using (var dc = new MyDbContext())
{
// create sample data
var book1 = new Book() { Name = "Book 1" };
dc.Books.Add(book1);
var contrib1 = new Contributor() { Name = "Contributor 1" };
var contrib2 = new Contributor() { Name = "Contributor 2" };
var contrib3 = new Contributor() { Name = "Contributor 3" };
dc.Contributors.Add(contrib1);
dc.Contributors.Add(contrib2);
dc.Contributors.Add(contrib3);
dc.SaveChanges();
// add relationships
book1.Writers.Add(contrib1);
book1.Artists.Add(contrib1);
book1.Artists.Add(contrib2);
book1.OtherContributors.Add(contrib3);
dc.SaveChanges();
}
// verify that the contributor 1 has both Artist and Writer relations
using (var dc = new MyDbContext())
{
var contrib1 = dc.Contributors.Single(c => c.Name == "Contributor 1");
var hasWriter = contrib1.WriterOfBooks.Count == 1;
var hasArtist = contrib1.ArtistOfBooks.Count == 1;
if (!hasWriter || !hasArtist)
{
throw new Exception("Houston, we have a problem.");
}
}
I'm working on books at test.polarcomputer.com
If you have a book object and this object has writer,publisher,designer ..whoever, you need just 3 object :
1.book object
2.contributor object.
3.integration object
book object has
- bookid
- bookname
contributor object has
- contributorid
- name
- typeofcontributor // 0-writer 1-colorist 2-CoverArtists 3-whoever
integration object has
- bookid
- contributorid
- typeofcontributor // 0-writer 1-colorist 2-CoverArtists 3-whoever
Check this if i understant it truly , i can give you full solution.
The data model you show is OK, but one thing is clear. You can't map this as a pure many-to-many association. That's only possible if the junction table BookContributors only contains BookId and ContributorId.
So you always need an explicit BookContributor class, and getting a collection of one of the contributor types is always going to take this basic shape:
book.BookContributors
.Where(bc => bc.Type == type)
.Select(bc => bc.Contributor)
Clunky, compared to what you have in mind. But no way to get around it, I'm afraid. What's left is a few options in the implementation details.
Option 1: Get all contributors, filter later.
First, let's get the basic model right:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public virtual ICollection<BookContributor> BookContributors { get; set; }
}
public class Contributor
{
public int ContributorId { get; set; }
public string Name { get; set; }
public virtual ICollection<BookContributor> BookContributors { get; set; }
}
public class BookContributor
{
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
public string Type { get; set; }
}
And the mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Book>().HasMany(b => b.BookContributors)
.WithRequired(bc => bc.Book)
.HasForeignKey(bc => bc.BookId);
modelBuilder.Entity<Contributor>().HasMany(c => c.BookContributors)
.WithRequired(bc => bc.Contributor)
.HasForeignKey(bc => bc.ContributorId);
modelBuilder.Entity<BookContributor>()
.HasKey(bc => new {bc.BookId, bc.ContributorId, bc.Type});
}
(by the way, here I avoid the term 'Discriminator' because that suggests TPH inheritance, which isn't applicable - yet).
Now you could add properties to Book like these:
[NotMapped]
public IEnumerable<Contributor> Writers
{
get
{
return BookContributors.Where(bc => bc.Type == "writer")
.Select(bc => bc.Contributor);
}
}
The downside of this approach is that you always have to ensure that books are loaded with their BookContributors and their Contributors included, or that lazy loading is possible. And you can't use these properties directly in a LINQ query. Also, it's somewhat hard to get books and only their unique contributors (i.e. distinct).
Option 2: Inheritance - essentially the same
You could make BookContributor an abstract base class having a number of inheritors:
public abstract class BookContributor
{
public int Id { get; set; }
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
}
public class Artist : BookContributor
{ }
public class Writer : BookContributor
{ }
BookContributor now needs a surrogate key, Id, because EF will now use a field Discriminator, which is hidden, so it can't be configured as part of the primary key.
Now Book could have properties like...
[NotMapped]
public ICollection<Artist> Artists
{
get { return BookContributors.OfType<Artist>().ToList(); }
}
...but these will still have the same downsides as mentioned above. The only possible advantage is that you can use types now (with compile-time checking) in stead of strings (or enum values) to get to the various BookContributor types.
option 3: A different model
Maybe the most promising approach is a slightly different model: books and contributors, where each association between them can have a collection of contributor types. BookContributor now looks like this:
public class BookContributor
{
public int BookId { get; set; }
public virtual Book Book { get; set; }
public int ContributorId { get; set; }
public virtual Contributor Contributor { get; set; }
public virtual ICollection<BookContributorType> BookContributorTypes { get; set; }
}
And a new type:
public class BookContributorType
{
public int ID { get; set; }
public int BookId { get; set; }
public int ContributorId { get; set; }
public string Type { get; set; }
}
Modified mapping:
modelBuilder.Entity<BookContributor>().HasKey(bc => new { bc.BookId, bc.ContributorId });
Additional mapping:
modelBuilder.Entity<BookContributor>().HasMany(bc => bc.BookContributorTypes).WithRequired();
modelBuilder.Entity<BookContributorType>().HasKey(bct => bct.ID);
With this model you can either just get books and their distinct contributors, if you're not interested in the contributor's types...
context.Books.Include(b => b.BookContributors
.Select(bc => bc.Contributor))
...or with the types...
context.Books.Include(b => b.BookContributors
.Select(bc => bc.Contributor))
.Include(b => b.BookContributors
.Select(bc => bc.BookContributorTypes));
...or books with only writers...
context.Books.Select(b => new
{
Book = b,
Writers = b.BookContributors
.Where(bc => bc.BookContributorTypes
.Any(bct => bct.Type == "artist"))
})
Again, the latter query can be wrapped in a property...
[NotMapped]
public ICollection<Artist> Artists
{
get
{
return BookContributors
.Where(bc => bc.BookContributorTypes
.Any(bct => bct.Type == "artist"))
.Select(bc => bc.Contributor).ToList();
}
}
...however, with all aforementioned cautions.
Your model should be like that :
[Table("tblBooks")]
public class BookTbl
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int BookID { get; set; }
public string BookName { get; set; }
}
[Table("tblContributor")]
public class ContributorTbl
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ContID { get; set; }
public string Contributor { get; set; }
}
[Table("tblIntegration")]
public class IntegrationTbl
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int IntID { get; set; }
public int BookID { get; set; }
[ForeignKey("BookID")]
public BookTbl Book { get; set; }
public int ContID { get; set; }
[ForeignKey("ContID")]
public IntegrationTbl Integration { get; set; }
}

Resources