In my MVC application, I have an entity relation between Student and Lookup tables with StatusID-LookupKey and GenderID-LookupKey pairs inestead of (StatusID-ID and GenderID-ID pairs). However, not being able to define the LookupKey column as the matching parameter with the StatusID and GenderID, I have to use ID column of Lookup table. How can I use InverseProperty and match the related columns to the LookupKey column? Thanks in advance.
Here are the Lookup table and the entities below:
Lookup Table:
ID | LookupType | LookupKey | LookupValue |
1 | Status | 0 | Passive |
2 | Gender | 1 | Male |
3 | Status | 1 | Active |
4 | Gender | 2 | Female |
5 | Status | 2 | Cancelled |
Lookup Entity:
public class Lookup
{
[Key]
public int ID { get; set; }
public string LookupType { get; set; }
public int LookupKey { get; set; }
public string LookupValue { get; set; }
public virtual ICollection<Student> Students { get; set; }
//This works when using PK (ID) as matching column.
public ICollection<Student> StatusLookupStudents { get; set; }
public ICollection<Student> TermLookupStudents { get; set; }
}
Student Entity:
public class Student
{
[Key]
public int ID { get; set; }
//Foreign key for Status
public int StatusID { get; set; }
//Foreign key for Gender
public int GenderID { get; set; }
//This works when using PK (ID) as matching column. But I want to use LookupKey column for matching
[ForeignKey("StatusID")]
[InverseProperty("StatusLookupStudents")]
public virtual Lookup StatusLookup { get; set; }
[ForeignKey("TermID")]
[InverseProperty("TermLookupStudents")]
public virtual Lookup TermLookup { get; set; }
}
Related
In an ASP Core project, with EF, have a table containing the details in respect of different types of items in inventory like:
public class InventoryDetails
{
[Key, StringLength(15)]
public string InventoryID { get; set; }
[Required, MaxLength(150)]
public string InventoryName { get; set; }
[MaxLength(200)]
public string Details { get; set; }
public ICollection<PriceLog> PriceLogs { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
}
Because of frequently changing prices of the said items, a separate price log is maintained as follows:
public class PriceLog
{
[Key]
public int ID { get; set; }
[Required]
public DateTime PriceTime { get; set; }
[Required, StringLength(15)]
public string InventoryID { get; set; }
public virtual InventoryDetails InventoryItems { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal PricePerUnit { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal TaxPerUnit { get; set; }
}
Similarly, Sales are being recorded in a table like the following invoices model:
public class SaleInvoice
{
[Key]
public int SInvoiceID { get; set; }
[Required]
public DateTime SalesTime { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
}
while its items are being recorded in a separate table as:
public class SalesItem
{
[Key]
public int ID { get; set; }
[Required]
public int SInvoiceID { get; set; }
public virtual SaleInvoice Sales { get; set; }
[Required, StringLength(15)]
public string InventoryID { get; set; }
public virtual InventoryDetails InventoryItems { get; set; }
[Required, Column(TypeName = "decimal(19, 4)")]
public decimal Qty { get; set; }
}
Now the main objective is to show/use the LATEST price against each item? In other words, if the SalesInvoice is dated 31-Dec-2017, how to use the LATEST Sales rates of EACH item that exist (as per the Price Log table) in the year 2017 or earlier, against the Quantity (Qty) as per the SalesItem, and being SUMMED up at the Invoice Level?
For the sake of clarification, consider the following prices recorded as per Price Log like:
01-Dec-2017 | Strawberry | 8.59
02-Dec-2017 | Strawberry | 7.36
02-Dec-2017 | Apple | 15.68
04-Dec-2017 | Apple | 18.91
05-Dec-2017 | Banana | 8.1
08-Dec-2017 | Strawberry | 7.4
09-Dec-2017 | Strawberry | 10.62
10-Dec-2017 | Orange | 11.16
12-Dec-2017 | Apple | 15.79
14-Dec-2017 | Strawberry | 9.57
15-Dec-2017 | Banana | 8.34
16-Dec-2017 | Orange | 10.37
18-Dec-2017 | Banana | 10.07
19-Dec-2017 | Strawberry | 10.05
20-Dec-2017 | Apple | 17.59
23-Dec-2017 | Apple | 16.51
27-Dec-2017 | Strawberry | 10.02
30-Dec-2017 | Orange | 10.88
04-Jan-2018 | Orange | 12.00
06-Jan-2018 | Strawberry | 10.74
09-Jan-2018 | Apple | 17.47
09-Jan-2018 | Banana | 10.47
In this regard, this time, consider an invoice dated 5-Jan-2018, with following items:
10 Strawberries
05 Apples
10 Oranges
15 Bananas
what would be the appropriate LINQ part to sum up the entire sales with the rates latest on the said date as:
27-Dec-2017 | Strawberry | 10.02 * 10 = 100.20
23-Dec-2017 | Apple | 16.51 * 05 = 82.55
04-Jan-2018 | Orange | 12.00 * 10 = 120.00
18-Dec-2017 | Banana | 10.07 * 15 = 151.05
Thus, the Index of SalesInvoice reflecting the entire list of Invoices with the entry pertaining to such a date as:
Invoice_ID Sales_Time Net_Sales
.... .... ....
.... .... ....
0007 5-Jan-2018 453.80
.... .... ....
.... .... ....
Looking forward for you experts' help in this regard.
Thanks
How about adding function to your SalesInvoice (or overloading it with a partial class).
This is not exactly LINQ query but it should get the job done.
public class SaleInvoice
{
[Key]
public int SInvoiceID { get; set; }
[Required]
public DateTime SalesTime { get; set; }
public ICollection<SalesItem> SalesItems { get; set; }
/* This returns total amount for each salesitem compared to salesdate*/
public decimal LatestRates(List<PriceLog> priceLogs)
{
var uniquePriceLogs = new List<PriceLog>();
foreach (var p in priceLogs.Where(w => w.PriceTime < this.SalesTime).GroupBy(g => g.InventoryID))
{
var orderedLog = p.OrderByDescending(o => o.PriceTime).FirstOrDefault();
if (orderedLog != null) uniquePriceLogs.Add(orderedLog);
}
decimal total = 0;
foreach(var s in this.SalesItems)
{
var unique = uniquePriceLogs.FirstOrDefault(f => f.InventoryID == s.InventoryID);
if(unique != null)
{
total += s.Qty * unique.PricePerUnit;
}
}
return total;
}
}
However if I am misunderstaing your question then you could please clarify if you wanted to add to the current schema or simply create a single linq query to rule them all :)
How do i make the foreign key column to have a custom name. by default EF will append "_" to the foreign key column
Customer
POCO:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Order Order { get; set; }
}
EF Config:
HasKey(m => m.Id);
HasOptional(m => m.Order).WithOptionalPrincipal(e => e.Customer);
Customer table:
+====+=======+
| Id | Name |
+====+=======+
| 1 | Cust1 |
+----+-------+
| 2 | Cust2 |
+----+-------+
| 3 | Cust3 |
+----+-------+
| 4 | Cust4 |
+----+-------+
| 5 | Cust5 |
+====+=======+
Order
POCO:
public class Order
{
public int Id { get; set; }
public string ItemName { get; set; }
public virtual Customer Customer { get; set; }
}
EF Config:
HasKey(m => m.Id);
Order table:
+====+=============+==========+
| Id | Customer_Id | ItemName |
+====+=============+==========+
| 1 | 2 | Modem |
+----+-------------+----------+
| 2 | 5 | Router |
+====+=============+==========+
How can i make the Order table as follow (to have "CustomerId" as the actual property name)?
+====+=============+==========+
| Id | CustomerId | ItemName |
+====+=============+==========+
| 1 | 2 | Modem |
+----+-------------+----------+
| 2 | 5 | Router |
+====+=============+==========+
I usually use the attributes to define my mapping to SQL directly on the entities (but I can see plenty of reasons why one wouldn't do that). Anyway, here is how to do it using attributes:
public class Order
{
[Key]
public int Id { get; set; }
[Column]
public string ItemName { get; set; }
[Column]
public int? CustomerId { get; set; }
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
UPDATE
Something like
HasKey(m => m.Id);
HasOptional(m => m.Order)
.WithOptionalPrincipal(e => e.Customer)
.Map(m => m.MapKey("CustomerId"));
might do if you don't want to use attributes.
I was handed a project which is using EF Database first, EF Reverse POCO generator, and Auto Mapper. I'm really struggling getting a many-to-many relationship to be modeled properly using all three pieces and would appreciate any help.
I have the following tables in my DB:
+=================+ +==================+ +================+
| Event | | Event_Format | | Format |
+=================+ +==================+ +================+
| Id | | Id | | Id |
| Title | | EventId | | Name |
| Created | | FormatId | | Created |
| CreatedBy | | Created | | CreatedBy |
| | | CreatedBy | | |
+=================+ +==================+ +================+
This generates three POCO classes in my data layer:
public class Event {
public int Id { get; set; } // Id (Primary key)
public string Title { get; set; } // Title
public DateTime Created { get; set; } // Created
public string CreatedBy { get; set; } // CreatedBy
public virtual ICollection<EventFormat> EventFormats { get; set; } // Event_Format.FK_Event_Format_Event
}
public class EventFormat
{
public int Id { get; set; } // Id (Primary key)
public int EventId { get; set; } // EventId
public int FormatId { get; set; } // FormatId
public DateTime Created { get; set; } // Created
public string CreatedBy { get; set; } // CreatedBy
public virtual Event Event { get; set; } // FK_Event_Format_Event
public virtual Format Format { get; set; } // FK_Event_Format_Format
}
public class Format
{
public int Id { get; set; } // Id (Primary key)
public string Name { get; set; } // Name
public DateTime Created { get; set; } // Created
public string CreatedBy { get; set; } // CreatedBy
public virtual ICollection<EventFormat> EventFormats { get; set; } // Event_Format.FK_Event_Format_Format
}
In my MVC project I have a view model for Events and Formats (but not EventFormats as it seems unneeded).
public class Event.ViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public string CreatedBy { get; set; }
public List<Format.ViewModel> Formats { get; set; }
}
public class Format.ViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
public String CreatedBy { get; set; }
}
Here are the Mapper profiles:
protected internal class BookingProfile : Profile
{
public new string ProfileName = "Admin_Booking";
protected override void Configure()
{
CreateMap<Data.Models.Event, Models.Booking.WisconsinFilmFest.ViewModel>()
.ForMember(dst => dst.Created, x => x.MapFrom(src => src.Created.ToLocalTime()))
.ForMember(dst => dst.EventFormats, x => x.MapFrom(src => src.EventFormats.ToList()));
}
}
protected internal class FormatProfile : Profile
{
public new string ProfileName = "Admin_Format";
protected override void Configure()
{
CreateMap<Data.Models.Format, Models.Format.ViewModel>()
.ForMember(dst => dst.Created, x => x.MapFrom(src => src.Created.ToLocalTime()))
.ForMember(dst => dst.Modified, x => x.MapFrom(src => src.Modified.ToLocalTime()));
}
}
When I attempt to use AutoMapper to map an Event to an Event.ViewModel it doesn't work because EF returns the EventFormat type instead (because of the extra columns). Is there a way to tell Automapper to get the Format of each EventFormat item and then map those into the Formats property on the ViewModel?
Currently what I'm doing is using the AfterMap() feature of Automapper, and looping through each item in EventFormats to get the Format and add it to the ViewModel's property like so:
protected internal class BookingProfile : Profile
{
public new string ProfileName = "Admin_Booking";
protected override void Configure()
{
CreateMap<Data.Models.Event, Models.Booking.WisconsinFilmFest.ViewModel>()
.ForMember(dst => dst.Created, x => x.MapFrom(src => src.Created.ToLocalTime()))
.ForMember(dst => dst.EventFormats, x => x.Ignore())
.AfterMap((src, dst) =>
{
if(src.EventFormats.Any(x => x.Format.Deleted == null))
{
foreach(Data.Models.EventFormat ef in src.EventFormats)
{
dst.EventFormats.Add(Mapper.Map(ef.Format, new Models.Format.ViewModel()));
}
}
});
}
This feels somewhat hacky to me and I was hoping there was a better way to do this.
Sure you can. Specify that AutoMapper should map Event.ViewModel.Formats to EventFormats.Select(ef => ef.Format):
Mapper.CreateMap<Format,Format.ViewModel>();
Mapper.CreateMap<Event,Event.ViewModel>()
.ForMember(dest => dest.Formats,
m => m.MapFrom(src => src.EventFormats.Select(ef => ef.Format)));
Now you can do
var result = db.Events.ProjectTo<Event.ViewModel>();
You should be able to just update the first mapping in the profile to read like this:
CreateMap<Data.Models.Event, Models.Booking.WisconsinFilmFest.ViewModel>()
.ForMember(dst => dst.Created, x => x.MapFrom(src => src.Created.ToLocalTime()))
.ForMember(dst => dst.Formats, x => x.MapFrom(src => src.EventFormats.Select(y => y.Format).ToList()));
As it stands, the profile is telling the mapper to project the source type's EventFormats property into the destination's EventFormats property, when it sounds like what you really want is to project the EventFormats.Format property into a Formats collection.
As you can see at below, there are a Lookup table for the enum values and I want to create a relationship between a table's enum values and LookupKey column of the Lookup table (instead of ID column of the Lookup table).
Lookup table:
ID | LookupType | LookupKey | LookupValue |
101 | Status | 0 | Passive |
106 | Gender | 1 | Male |
113 | Status | 1 | Active |
114 | Gender | 2 | Female |
118 | Status | 2 | Cancelled |
Main Table:
ID | Status | Gender | Name | ...
1 | 0 | 1 | John Smith | ...
2 | 1 | 2 | Christof Jahnsen | ...
3 | 2 | 1 | Alexi Tenesis | ...
4 | 0 | 2 | Jurgen Fechtner | ...
5 | 1 | 2 | Andreas Folk | ...
However, when using PK-FK relation and InverseProperty as on DataAnnotations - InverseProperty Attribute the relation is created with the ID column of the Lookup table and I cannot make the relation to the LookupKey column. Could you give an example how to achieve this?
We have a common lookup table here. It looks simlar to yours. LookupData has a primary key and a foreign key to LookupTypes which is equivalent to your enum and the value. We might also have some other simple fields like a flag or code which are identified in the LookupType metadata table. Then in out main table we might have "GenderLookupId" which points to the LookupData.Id field. The IDs themselves have no meaning and can be entered in any order. If you want gender 1 and 2 to have meaning, you should probably add another attribute for that (see surrogate keys).
Example with data:
LookupType
ID Description CodeDesc BooleanDesc
1 Genders Gender Code NULL
2 Races Race Code Is Active
LookupData
ID LookupTypeId Description Code Boolean
789 1 Male M NULL
790 2 White W True
791 1 Female F NULL
792 2 Hispanic H False
Main Name Table
NameId Name GenderLookupId RaceLookupId
1234 Joe Smith 789 790
1235 Mary Meyers 791 792
Classes:
public class LookupType
{
public int Id { get; set; }
public string Description { get; set; }
public string CodeDescription { get; set; }
public string BooleanDescription { get; set; }
}
public class LookupData
{
public int Id { get; set; }
public int LookupTypeId { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public bool? BooleanValue { get; set; }
public LookupType LookupType { get; set; }
}
public class Name
{
public int Id { get; set; }
public string FullName { get; set; }
public int? GenderLookupId { get; set; }
public LookupData Gender { get; set; }
}
LookupData Config:
HasRequired(p => p.LookupType).WithMany(p=>p.LookupData).HasForeignKey(p=>p.LookupTypeId).WillCascadeOnDelete(false);
Name Config:
HasOptional(p => p.Gender).WithMany(p=>p.Name).HasForeignKey(p=>p.GenderLookupId).WillCascadeOnDelete(false);
I have 3 tables
================= ================= =================
| TableA | | RelationAC | | TableC |
================= ================= =================
| IdA | | Id | | IdC |
| | | IdA | | |
| Field1A | | IdC | | Field1C |
================= ================= =================
And this are my model's code:
public partial class TableA
{
public TableA()
{
this.RelationAC = new HashSet<RelationAC>();
}
[Key]
public decimal IdA { get; set; }
public string Field1A { get; set; }
[ForeignKey("IdA")]
public virtual ICollection<RelationAC> RelationAC { get; set; }
}
public partial class TableC
{
[Key]
public decimal IdC { get; set; }
public string Field1C { get; set; }
}
public partial class RelationAC
{
public RelationAC ()
{
this.TableC= new HashSet<TableC>();
}
[Key]
public decimal Id { get; set; }
public decimal IdA{ get; set; }
public decimal IdC{ get; set; }
[ForeignKey("IdC")]
public virtual ICollection<TableC> TableC { get; set; }
}
If made this query
var query = from d in db.TableA
select d;
foreach( TableA ta in query.Tolist())
{
foreach(RelationAC rac in ta.RelationAC.Tolist())
{
TableC tc = rac.TableC.First(); // It allways has count = 0 , even my db has data
}
}
Why TableC tc is allways empty ?
Your property is probably having issues w/ lazy loading. Try eager loading the collection:
db.TableA.Include("RelationAC.TableC")