I have the following database schema and relation
What I'm trying to do is to select from the ProductActiveIngredient table with the known of productId, and I want to select every ActiveIngredientOne, ActiveIngredientTwo, ActiveIngredientThree, and ActiveIngredientFour from inner joining the ActiveIngredient table with ProductActiveIngredient.
Executing this query will return each ActiveIngredientOne, and ActiveIngredientTwo columns name:
select
a.ActiveIngredientOne, b.ActiveIngredientName, c.ActiveIngredientName
from
ProductActiveIngredient a
inner join
ActiveIngredient b on a.ActiveIngredientOne = b.ActiveIngredientId
inner join
ActiveIngredient c on a.ActiveIngredientTwo = c.ActiveIngredientId
and the query result is:
query result
What I want to do is to make this query using Entity Framework.
I have the following models written in my solution:
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string ProductGuid { set; get; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { set; get; }
[Required]
[Display(Name = "Cost")]
public int Cost { set; get; }
[Required]
[Display(Name = "Tax")]
public int Tax { set; get; }
[Required]
[Display(Name = "Profit")]
public int Profit { set; get; }
[Required]
[Display(Name = "Discount")]
public int Discount { set; get; }
[Required]
[Display(Name = "Price")]
public int Price { set; get; }
[Required]
[Display(Name = "Units")]
public int Units { set; get; }
[Required]
[Display(Name = "Pack size")]
public int PackSize { set; get; }
[Display(Name = "Description")]
public string Description { set; get; }
[Display(Name = "Manufacturer id")]
public int ManufacturerId { set; get; }
public virtual Contact Contact { set; get; }
public virtual ProductActiveIngredient ProductActiveIngredient { set; get; }
}
public class ProductActiveIngredient
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string ProductActiveIngredientGuid { set; get; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductActiveIngredientId { set; get; }
[Required]
[Display(Name = "Active Ingredient 1")]
public int ActiveIngredientOne { set; get; }
//[Display(Name = "Active Ingredient 2")]
#nullable enable
public int ActiveIngredientTwo { set; get; }
public int ActiveIngredientThree { set; get; }
public int ActiveIngredientFour { set; get; }
#nullable disable
public int ProductId { set; get; }
public int ForUser { set; get; }
public virtual Product Product { set; get; }
public virtual User User { set; get; }
public virtual ActiveIngredient ActiveIngredient { set; get; }
}
public class ActiveIngredient
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string ActiveIngredientGuid { set; get; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ActiveIngredientId { set; get; }
[Required]
[Display(Name = "Active ingredient name")]
public string ActiveIngredientName { set; get; }
public virtual ProductActiveIngredient ProductActiveIngredient { set; get; }
}
and this is my db context relation:
modelBuilder.Entity<Product>()
.HasOne(pai => pai.ProductActiveIngredient)
.WithOne(p => p.Product)
.HasForeignKey<ProductActiveIngredient>(p => p.ProductActiveIngredientId);
modelBuilder.Entity<ActiveIngredient>()
.HasOne(ai => ai.ProductActiveIngredient)
.WithOne(aco => aco.ActiveIngredient)
.HasForeignKey<ProductActiveIngredient>(a => a.ActiveIngredientOne);
modelBuilder.Entity<ActiveIngredient>()
.HasOne(ai => ai.ProductActiveIngredient)
.WithOne(aco => aco.ActiveIngredient)
.HasForeignKey<ProductActiveIngredient>(a => a.ActiveIngredientTwo);
modelBuilder.Entity<ActiveIngredient>()
.HasOne(ai => ai.ProductActiveIngredient)
.WithOne(aco => aco.ActiveIngredient)
.HasForeignKey<ProductActiveIngredient>(a => a.ActiveIngredientThree);
modelBuilder.Entity<ActiveIngredient>()
.HasOne(ai => ai.ProductActiveIngredient)
.WithOne(aco => aco.ActiveIngredient)
.HasForeignKey<ProductActiveIngredient>(a => a.ActiveIngredientFour);
This LinQ query executes fine, and get the same result as the T-SQL query, but I need to do it in EF not in LinQ:
var s = from pac in _db.ProductActiveIngredient
join ai in _db.ActiveIngredient on pac.ActiveIngredientOne equals ai.ActiveIngredientId
join ai2 in _db.ActiveIngredient on pac.ActiveIngredientTwo equals ai2.ActiveIngredientId
where pac.ProductId == productId
select new
{
pac.ActiveIngredientOne,
ai.ActiveIngredientName,
pac.ActiveIngredientTwo,
b2 = ai2.ActiveIngredientName
};
Any help please?
It's done, Many thanks.
What I did is added another three virtual properties in my ProductActiveIngredient class, and in ActiveIngredient class.
Then i used the .Include(x=>x.ProductActiveIngredientOne)
.ThenInclude(z=>z._ActiveIngredientOne).FirstOrDefaultAsync();
and updated my db context as the properties above.
Related
I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample
My application was working fine until I added 3 new classes(Administrator,Department and Depot) and altered the IssueContext.cs in the Data Access Layer.
Since my IssueContext has been altered I needed to update my database so I used update-database in the Package Manager Console then I got this(Open tab in the new window)
Which is weird because it was working perfectly fine before. I then installed Entity Framework then update-database worked.
However when I click on my User tabs or About tabs they give me a "Webpage not available" but the Index page works. Could this be a relational database mapping problem where I didn't map my new classes correctly?
Note: I also tried installing MVC but MVC Razor just crashes everything.
Here's my Entity Diagram
User.cs
public class User
{
public int UserID { get; set; }
[StringLength(50, MinimumLength = 1)]
public string LastName { get; set; }
[StringLength(50, MinimumLength = 1, ErrorMessage = "First name cannot be longer than 50 characters.")]
[Column("FirstName")]
public string FirstMidName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
public string FullName
{
get { return LastName + ", " + FirstMidName; }
}
public int AdministratorID { get; set; }
[ForeignKey("AdministratorID")]
public virtual Administrator Administrator { get; set; }
public int DepartmentID { get; set; }
[ForeignKey("DepartmentID")]
public virtual Department Department { get; set; }
public int DepotID { get; set; }
[ForeignKey("DepotID")]
public virtual Depot Depot { get; set; }
public int TicketID { get; set; }
//Setting up relationships A use can apply for any number of tickets, so Tickets is defined as a collection of Ticket entities.
public virtual ICollection<Ticket> Users { get; set; }
}
Ticket.cs
public class Ticket
{
public string Issue { get; set; }
[DisplayFormat(NullDisplayText = "No Priority")]
public Priority? Priority { get; set; }
//Category One to Many Ticket
public int CategoryID { get; set; }
[ForeignKey("CategoryID")]
public virtual Category Category { get; set; }
//User (One to Many) Ticket
public int UserID { get; set; }
public int TicketID { get; set; }
[ForeignKey("TicketID")]
public virtual User User { get; set; }
public int AdminID { get; set; }
public virtual ICollection<Administrator> Administrators { get; set; }
}
Depot.cs
public class Depot
{
public int DepotID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Department.cs
public class Department
{
public int DepartmentID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
Category.cs
public class Category
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CategoryID { get; set; }
public string Title { get; set; }
public virtual ICollection<Ticket> Tickets { get; set; }
}
Administrator.cs
public class Administrator
{
[Key, ForeignKey("User")]
public int UserID { get; set; }
public int AdminID { get; set; }
public int TicketID { get; set; }
[StringLength(50)]
public string AdminRole { get; set; }
public virtual ICollection<Ticket> Tickets { get; set; }
public virtual User User { get; set; }
}
Context.cs
public class IssueContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Administrator> Administrators { get; set; }
public DbSet<Depot> Depots { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Ticket>()
.HasMany(c => c.Administrators).WithMany(i => i.Tickets)
.Map(t => t.MapLeftKey("TicketID")
.MapRightKey("AdministratorID")
.ToTable("AdministratorsTickets"));
modelBuilder.Entity<Administrator>()
.HasKey(e => e.UserID);
modelBuilder.Entity<User>()
.HasOptional(s => s.Administrator) // Mark StudentAddress is optional for Student
.WithRequired(ad => ad.User); // Create inverse relationship
}
}
I'm using Entity framework using code first. I have 3 tables in database:
Place
Profil
PlaceMember
It's a many to many relation. A place can have many Profils as members and a profil can be a member to many Places. The PlaceMember is the relational table.
Code of PlaceDTO:
[Table("Place")]
public partial class PlaceDTO
{
public PlaceDTO()
{
Members = new HashSet<ProfilDTO>();
}
public int id { get; set; }
[StringLength(100)]
public string description { get; set; }
public DateTime dateOut { get; set; }
public DateTime createDate { get; set; }
public int? canceled { get; set; }
public int profilId { get; set; }
public int addressId { get; set; }
public virtual AddressDTO Address { get; set; }
public virtual ProfilDTO Profil { get; set; }
public virtual ICollection<ProfilDTO> Members { get; set; }
}
Code of Profil class:
public partial class ProfilDTO
{
public ProfilDTO()
{
Devices = new HashSet<DeviceDTO>();
Notifications = new HashSet<NotificationDTO>();
Places = new HashSet<PlaceDTO>();
}
public int id { get; set; }
[Required]
[StringLength(50)]
public string pseudo { get; set; }
[Required]
[StringLength(50)]
public string firstname { get; set; }
[Required]
[StringLength(50)]
public string lastname { get; set; }
[Required]
[StringLength(15)]
public string phone { get; set; }
[Required]
[StringLength(50)]
public string emailAdresse { get; set; }
[Required]
[StringLength(150)]
public string password { get; set; }
public DateTime lastUpdatePassword { get; set; }
public DateTime lastUpdateProfil { get; set; }
public DateTime createDate { get; set; }
public byte[] picture { get; set; }
public virtual ICollection<PlaceDTO> Places { get; set; }
}
Code of PlaceMember class:
[Table("PlaceMember")]
public partial class PlaceMemberDTO
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int placeId { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int invitedId { get; set; }
}
In database, the placeId and invitedId are both identity key of the table.
When I'm trying to add a new place to the Place table, I'm getting that PlaceDTO_id is not a valid column name despite the fact that the place table in database has an column named id and is the identity column.
context.Address.Attach(place.Address);
context.Address.Add(place.Address);
place.addressId = place.Address.id;
context.Places.Attach(place);
context.Places.Add(place);
context.SaveChanges();
The place object, put in param, contains 3 members that exists in Profil table.
When I remove the Members field from the PlaceDTO table, the place is inserted but nothing added to the PlaceMember table.
What's wrong ?
I cant seem to figure out why my navigation property is not getting built by my include statement.
Here is my method:
public async Task<IHttpActionResult> GetCompanies(string id)
{
DbContext.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
var company = await DbContext.Companies.Where(x => x.Id.ToString() == id).Include(x => x.StartelAccounts).FirstOrDefaultAsync();
if (company != null)
{
return Ok(this.TheModelFactory.Create(company));
}
return NotFound();
}
When I test the SQL from the debug log I get all the fields and values for both objects.
Here are the models:
public class CompanyGroup
{
[Key]
public Guid Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime FirstBillingDate { get; set; }
[Required]
public int TermLength { get; set; }
public virtual ICollection<ApplicationUser> Members { get; set; }
public virtual ICollection<AccountStartel> StartelAccounts { get; set; }
public CompanyGroup()
{
Members = new HashSet<ApplicationUser>();
StartelAccounts = new HashSet<AccountStartel>();
}
}
public class AccountStartel
{
[Key]
public Guid Id { get; set; }
[Required]
public string ClientID { get; set; }
[Required]
public int DbId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string TimeZone { get; set; }
[Required]
public string AccountNum { get; set; }
public Guid CompanyId { get; set; }
public virtual CompanyGroup Company { get; set; }
public virtual ICollection<UsageReport> UsageReports { get; set; }
public AccountStartel()
{
Company = new CompanyGroup();
CompanyId = Guid.Empty;
UsageReports = new List<UsageReport>();
}
}
EF Fluent API
modelBuilder.Entity<AccountStartel>()
.HasRequired<CompanyGroup>(x => x.Company)
.WithMany(x => x.StartelAccounts)
.HasForeignKey(x => x.CompanyId);
modelBuilder.Entity<AccountStartel>()
.Property(p => p.DbId)
.IsRequired()
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(
new System.ComponentModel.DataAnnotations.Schema.IndexAttribute("IX_StartelDbId", 1) { IsUnique = true }));
Can anyone see what im missing here?
Could it have to do with setting Company and/or CompanyId in the
AccountStartel constructor? Does it work if you remove those lines? –
Peter
Initializing the navigation properties to a default value caused EF to not load them correctly.
Here is the updated model which does work now
public class AccountStartel
{
[Key]
public Guid Id { get; set; }
[Required]
public string ClientID { get; set; }
[Required]
public int DbId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string TimeZone { get; set; }
[Required]
public string AccountNum { get; set; }
public Guid CompanyId { get; set; }
public CompanyGroup Company { get; set; }
public virtual ICollection<UsageReport> UsageReports { get; set; }
public AccountStartel()
{
UsageReports = new List<UsageReport>();
}
}
I have the following models in my solution:
internal class Customer
{
[Key]
public int CustomerId { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(12)]
public string PhoneNumber { get; set; }
}
internal class Product
{
[Key]
public int ProductId { get; set; }
[MaxLength(100)]
public string ProductName { get; set; }
public decimal Price { get; set; }
public double ProductWeight { get; set; }
public bool InStock { get; set; }
}
internal class Order
{
[Key]
public int OrderId { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public DateTime OrderDate { get; set; }
[MaxLength(30)]
public string PoNumber { get; set; }
}
class Cart
{
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Product> Products { get; set; }
public uint Quantity { get; set; }
}
...and DB context
class Store : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Cart> Carts { get; set; }
}
When I debug, an exception is thrown saying "'Carts' is based on type 'Cart' that has no keys defined".
I've removed the Cart class from the DB context and the solution runs fine.
I've tried several different ways to declare the keys in the Cart class including:
[ForeignKey("Order")]
[Column(Order = 1)]
public int OrderId { get; set; }
public Order Order { get; set; }
[ForeignKey("Product")]
[Column(Order = 2)]
public int ProductId { get; set; }
public Product Product { get; set; }
or
[Key]
public int OrderId { get; set; }
[Key]
public int ProductId { get; set; }
Any ideas where I might be going wrong? (Please keep in mind this is an educational project so feedback on the DB design is unnecessary)