Add a navigation property to a plain object - c#

So I have a viewmodel in my application that looks like:
public class CountryVM
{
[Key]
public int CountryID { get; set; }
[ForeignKey("Country_CountryID")]
public virtual ICollection<OrganisationVM> Organisations { get; set; }
public CountryVM(Country country)
{
if (country == null) return;
this.CountryID = country.CountryID;
}
}
That is seeded by the class:
public class Country
{
[Key]
public int CountryID { get; set; }
public virtual ICollection<Organisation> Organisations { get; set; }
}
The other relevant classes are:
public class Organisation
{
[Key]
public int OrganisationId { get; set; }
[Required]
public virtual Country Country { get; set; }
}
public class OrganisationVM
{
[Key]
public int OrganisationId { get; set; }
[ForeignKey("Country_CountryID")]
public virtual CountryVM Country { get; set; }
public int? Country_CountryID { get; set; }
}
Now Organisation and Country are tables in my database and OrganisationVM is a view. CountryVM exists only in the application and at the moment EF tries to create a table in my database 'dbo.CountryVMs'.
Is there anyway to hook it up like this so I can use a navigation property in CountryVM without it creating tables?
The reason I want to do this is:
I have this code in my web.api controller at the moment:
// GET: odata/Country
[EnableQuery]
public IQueryable<CountryVM> Get()
{
var a = db.Countries.ToList().Select<Country, CountryVM>(c => new CountryVM(c)).AsQueryable();
return a;
}
I was hoping that odata would allow me to use expand and select to select the countryVMs Organisations but it hasn't so far.

Related

Which one is the correct one-to-many relation in EF

i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}

Entity Framework Conditional join

I have created an MVC using Entity framework and I've encountered a situation which I don't know how to resolve.
I'm using the EF auto joins and relations (all my table models were created automatically by EF) .
Now for the problem - I have a table of customers, which has two(relavent) fields - personID and employerID . Only one of them contains data , the other will be null (a customer is either a person , or an employer) . When I try to include employer model in the result set, I'm getting thrown (without any message , when I debug I see that the content has data but the employeer is sometimes NULL) I'm also not sure about how the design should look like. This is my code :
Customer:
public partial class Customer
{
public Customer()
{
Account = new HashSet<Account>();
}
public long Id { get; set; }
public int? PersonId { get; set; }
public int Type { get; set; }
public int? EmployerId { get; set; }
public Employer Employer { get; set; }
public ICollection<Account> Account { get; set; }
}
Employer:
public partial class Employer
{
public Employer()
{
Customer = new HashSet<Customer>();
}
public int Id { get; set; }
public string Name { get; set; }
public int? IdType { get; set; }
public ICollection<Customer> Customer { get; set; }
}
Person:
public partial class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Sex { get; set; }
public DateTime? BirthDate { get; set; }
public int IdType { get; set; }
}
Now when I'm running in my repository:
var collectionBeforePaging = _context.Customer
Everything works, but Employer is NULL. If I use :
var collectionBeforePaging = _context.Customer.Include(a => a.Employer)
Then the project fails .
How can I make this joins?
Please define ForeignKey for Customer
public int? EmployerId { get; set; }
[ForeignKey(nameof(EmployerId))]
public virtual Employer Employer { get; set; }
What version of EF you use? I think you missing something like that :
In Employer :
[ForeignKey("EmployerId")]
[InverseProperty("Customers")]
public virtual Employer Employer { get; set; }
public int? EmployerId { get; set; }
In Customer :
[InverseProperty("Employer")]
public virtual ICollection<Customer> Customers { get; set; }
It can also be done in the Dbontext object

Many to Many relationship with ApplicationUser

I am building a Code-First, Many-To-Many relationship between my ApplicationUser class and a Lesson class. When the model is created, Entity Framework builds the two tables and the intersecting pivot table. However, neither table seems to take in data from the pivot table (LessonApplicationUsers). Both List variables do not seem to hold either the list of Students or the list of Lessons. Both entities i'm trying to marry up already exist in the database
ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string Address { get; set; }
public int? Age { get; set; }
public ClassLevel? ClassLevel { get; set; }
public string FirstName { get; set; }
public int? Height { get; set; }
public string LastName { get; set; }
public string MobileNumber { get; set; }
public string Postcode { get; set; }
public string Town { get; set; }
public int? Weight { get; set; }
public ApplicationUser()
{
Lessons = new List<Lesson>();
}
public ICollection<Lesson> Lessons { get; set; }
}
Lesson Class
public class Lesson
{
[Key]
public int LessonID { get; set; }
public LessonType ClassType { get; set; }
public ClassLevel? ClassLevel { get; set; }
public DateTime ClassStartDate { get; set; }
public DateTime ClassEndDate { get; set; }
public float ClassCost { get; set; }
public int? InstructorID { get; set; }
public Lesson()
{
Students = new List<ApplicationUser>();
}
public ICollection<ApplicationUser> Students { get; set; }
public enum LessonType {Group,Private}
}
My DBContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Lesson> Lessons { get; set; }
public DbSet<ApplyViewModel> Applications { get; set; }
And finally, the code i'm using to add in the pivot table data. This is activated when the user presses a button on the booking form.
public ActionResult BookUser()
{
//Gather required variables
ApplicationUser user = db.Users.First(i => i.UserName == User.Identity.Name);
int classID = int.Parse(Request.Form["classID"]);
using (db)
{
var editedLesson = db.Lessons.Single(s => s.LessonID == classID);
db.Lessons.Attach(editedLesson);
var editedUser = db.Users.Single(s => s.Id == user.Id);
db.Users.Attach(editedUser);
editedLesson.Students.Add(editedUser);
db.SaveChanges();
}
return View("Index");
When I try and run it, when i press my book button, it runs through the code and executes. checking the database it has indeed inserted the key values into the pivot table. When i load the model of the lesson to view its details, the Student attribute has a count of 0. I've been at this for days and i've got the feeling i'm missing something kickself simple....but i've gone over it a dozen times and can't see what i'm doing wrong...
Mark your lists with virtual to enable lazy loading. Also is not required to initialize the lists Lessons = new List<Lesson>();

Configure Many to Many relationship

For this I have 3 models: Hospitals, AspNetUsers, UserHospitals.
this is the UserHospitals model:
public class UserHospital
{
[Key]
public int UserHospitalID { get; set; }
public int HospitalID { get; set; }
public Hospital Hospitals { get; set; }
public string Id { get; set; }
public ApplicationUser Users { get; set; }
}
With it I can add User ID and Hospital ID to this table.
Now, I need to check which hospitals user's connected.
On my controller that return an hospital list i need to return only Hospitals that user's have a connection.
This return all hospitals, how can I filter it to show only if user have a connection with hospital on UserHospitals?
public ActionResult Index()
{
return View(db.Hospitals.ToList());
}
I don't want to add a new viewmodel that join models or whatever
== EDIT ==
Hospital Model
public class Hospital
{
[Key]
public int HospitalID { get; set; }
public string Name { get; set; }
public virtual ICollection<HospitalSpeciality> HospitalSpecialities { get; set; }
public virtual ICollection<UserHospital> UserHospitals { get; set; }
}
Try this:
public ActionResult Index()
{
var result =db.Hospitals.Include("UserHospitals").where(x=> x.UserHospitals.Any(x=>x.Id== userId)).ToList();
return View(result);
}

Recipe - ingredients database in Entity Framework ASP.NET MVC

This will create two tables "Ingredient" and "Recipe" and an additional table for many-to-many mapping.
public class DC : DbContext {
public DbSet<Ingredient> Ingredients { get; set; }
public DbSet<Recipe> Recipes { get; set; }
}
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Recipe> Recipes { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
Question: I want to include additional column "quantity" in the third mapping table that will be created by Entity Framework. How to make that possible? Thanks in advance.
When you've got some extra information, I suspect it won't really count as a mapping table any more - it's not just a many-to-many mapping. I think you should just model it as another table:
public class Ingredient {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> RecipeParts { get; set; }
}
public class RecipePart {
public int Id { get; set; }
public Ingredient { get; set; }
public Recipe { get; set; }
// You'll want to think what unit this is meant to be in... another field?
public decimal Quantity { get; set; }
}
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<RecipePart> Parts { get; set; }
}
So now you don't really have a many-to-many mapping - you have two ordinary many-to-one mappings. Do you definitely need to "ingredient to recipes" mapping exposed in your model at all? If you want to find out all the recipes which use a particular ingredient, you could always do a query such as:
var recipies = DB.Recipies.Where(r => r.Parts
.Any(p => p.Ingredient == ingredient));

Categories