How to get "child" rows directly in any to many relationship EF6 - c#

I have EF6 model with many to many relationship like this (I simplified my model to make it more clear):
public class Card
{
public int CardId { get; set; }
public string CardTitle { get; set; }
public virtual ICollection<CardLayout> CardLayouts { get; set; }
}
public class Layout
{
public int LayoutId { get; set; }
public string LayoutTitle { get; set; }
public virtual ICollection<CardLayout> CardLayouts { get; set; }
}
public class CardLayout // relationship table
{
public int CardLayoutId { get; set; }
public int CardId { get; set; }
public int LayoutId { get; set; }
public int CardLocation { get; set; }
public virtual Card Card { get; set; }
public virtual Layout Layout { get; set; }
}
This is how I use it for now:
int exampleId = 23;
using (MyContext ctx = new MyContext())
{
Card c = ctx.Cards.Find(23);
foreach (CardLayout cardLayout in c.CardLayouts)
{
Layout layout = cardLayout.Layout;
DoSomethingWithLayout(layout);
}
}
I need Layout collection directly inside my Card object. I don't want to pass thru relationship table. I want to use it like this:
int exampleId = 23;
using (MyContext ctx = new MyContext())
{
Card c = ctx.Cards.Find(23);
foreach (Layout layout in c.Layouts)
{
DoSomethingWithLayout(layout);
}
}
I have tried something like this:
public class Card
{
public int CardId { get; set; }
public string CardTitle { get; set; }
public virtual ICollection<CardLayout> CardLayouts { get; set; }
public virtual ICollection<Layout> Layouts { get; set; }
}
But Layouts collection is always empty.
My question is: how can I access Layouts collection directly from Card object? I want to keep Card.CardLayouts collection too, because my CardLayouts table contains fields (like CardLocation in my simplified example)
Ps. Can someone improve my question title? My english is not good enough to write it better. Thanks in advance.

While I think that #Biscuits' comment is correct. If you don't want to go that route for whatever reason, you should just be able to create another property with a getter only.
public class Card
{
public int CardId { get; set; }
public string CardTitle { get; set; }
public virtual ICollection<CardLayout> CardLayouts { get; set; }
[NotMapped]
public IEnumerable<Layout> Layouts
{
get
{
return this.CardLayouts.Select(cl => cl.Layout);
}
}
}

Related

Lookup for an Entity Column ID

I have an Entity Framework Core project that uses generic repositories and UnitOfWork and is working as expected.
The database is one to many and related by IDs.
The RTCTrials entity contains a FK CourseID related to RTCCourses PK. When loading trials I am trying to get the course name in the datagrid and only achieved by using a union. Is this inefficient and a simpler approach. Ideally I would add a dropdownlist column populated with RTCCourses in the trials grid template and the CourseID in the trials table would select the correct id and show the ValueMember course name.
This is what the current method looks like:
using (var context = new RTCContext())
{
var factory = new EntityFrameworkUnitOfWorkFactory(context);
var unit = factory.Create();
var festivals = unit.RTCFestivals.All().ToList();
var trials = unit.RTCTrials.All().ToList();
var courses = unit.RTCCourses.All().ToList();
var trialcourses = trials.Join(courses, courses => courses.CourseID, trials => trials.CourseID, (trials, courses) => new
{
TrialID = trials.TrialID,
FestivalID = trials.FestivalID,
CourseID = trials.CourseID,
Trial = trials.Trial,
Course = courses.CourseName,
TrialGrade = trials.TrialGrade,
TrialDistance = trials.TrialDistance,
TrialAge = trials.TrialAge,
TrialHurdles = trials.TrialHurdles,
TrialAllowances = trials.TrialAllowances,
TrialMonth = trials.TrialMonth,
TrialActualDate = trials.TrialActualDate,
TrialActualTime = trials.TrialActualTime,
TrialRaceCard = trials.TrialRaceCard,
TrialQualifiers = trials.TrialQualifiers
}).ToList();
this.radGridViewFestivalDestinations.DataSource = festivals;
this.radGridViewFestivalDestinations.Templates[0].DataSource = trialcourses;
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.MasterTemplate.Columns)
{
column.BestFit();
}
foreach (GridViewDataColumn column in radGridViewFestivalDestinations.Templates[0].Columns)
{
column.BestFit();
}
}
RTCTrial Entity
public partial class RTCTrial {
public RTCTrial()
{
this.RTCResults = new List<RTCResult>();
this.RTCWeathers = new List<RTCWeather>();
OnCreated();
}
public virtual int TrialID { get; set; }
public virtual int FestivalID { get; set; }
public virtual int CourseID { get; set; }
public virtual string Trial { get; set; }
public virtual string TrialGrade { get; set; }
public virtual string TrialDistance { get; set; }
public virtual string TrialAge { get; set; }
public virtual int? TrialHurdles { get; set; }
public virtual string TrialAllowances { get; set; }
public virtual string TrialMonth { get; set; }
public virtual DateTime? TrialActualDate { get; set; }
public virtual TimeSpan? TrialActualTime { get; set; }
public virtual string TrialRaceCard { get; set; }
public virtual int TrialQualifiers { get; set; }
public virtual RTCCourse RTCCourse { get; set; }
public virtual RTCFestival RTCFestival { get; set; }
public virtual IList<RTCResult> RTCResults { get; set; }
public virtual IList<RTCWeather> RTCWeathers { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
RTCCourse Entity
public partial class RTCCourse {
public RTCCourse()
{
this.RTCTrials = new List<RTCTrial>();
OnCreated();
}
public virtual int CourseID { get; set; }
public virtual string CourseName { get; set; }
public virtual string CourseCountry { get; set; }
public virtual string CourseDirection { get; set; }
public virtual string CourseCharacteristics { get; set; }
public virtual string CourseAlternateName { get; set; }
public virtual double CourseLat { get; set; }
public virtual double CourseLong { get; set; }
public virtual IList<RTCTrial> RTCTrials { get; set; }
#region Extensibility Method Definitions
partial void OnCreated();
#endregion
}
Regards, Neil
Suggestion would be on the returned courses you would want each course to have its associated trials. In the unit of work that returns all courses - possibly have an option to include them. Your dropdown would bind to each course and your grid would bind to the list of trials in the selected course.
public IEnumerable<RTCCourse> All(bool includeTrials = false)
{
var q = context.RTCCourses;
if (includeTrials)
{
q = q.Include(c => c.RTCTrials)//.ThenInclude(t => t.RTCResults)
;
}
return q.AsEnumerable(); // assuming that is the returned type
}
That should allow your courses to have the list of trials set. Then there is no need to get all trials. And you can bind to courses (and list of trials within each) directly instead of doing the join and binding to the anonymous.
Of 'course' -- this is merely a suggestion ;)

When I create a list that holds the "id" values of other database tables, I cannot reach the "name" values for the related tables

I have a class named "ODL"
Which holds values of :
{
public int ODLId { get; set; }
public string ODLName { get; set; }
public int? RefODLId { get; set; }
public int VehicleTypeId { get; set; }
public int MotorTypeId { get; set; }
public int GearboxTypeId { get; set; }
public int DoorId { get; set; }
public int VehicleLengthId { get; set; }
public string ProjectName { get; set; }
}
In another class which its name is "ODLModel" and used as a Model Class.
In this Model class there is a "List ODLs" property.
So now I can raech the ID values of VehicleType, MotorType etc.. in a foreach loop.
#foreach (var odl in Model.ODLs)
{
<tr>
<td>#odl.ODLName</td>
<td>#odl.VehicleLengthId</td>
<td>#odl.MotorTypeId</td>
!!! In here I dont want to show Id values but I would like to reach
the related tables VehicleLength.cs etc and take their "Name" values
and print it in here !!!
.....
</tr>
}
</tbody>
As I said, I'd like to show the "name" values in VehicleType.cs, MotorType.cs etc.
Im using EntityFramework, and all of my classes referenced as ID properties in ODL.cs are related to a table.
public DbSet<VehicleType> VehicleTypes { get; set; }
public DbSet<MotorType> MotorTypes { get; set; }
public DbSet<GearboxType> GearboxTypes { get; set; }
public DbSet<Door> Doors { get; set; }
public DbSet<VehicleLength> VehicleLengths { get; set; }
public DbSet<ODL> ODLs { get; set; }
In order to be able to get properties of related items you have to change your ODL class. Instead of declaring int ID props you need to declare "navigation properties". You can read more about it at official documentation
And your ODL class will look like that:
{
public int ODLId { get; set; }
public string ODLName { get; set; }
public int? RefODLId { get; set; }
public virtual VehicleType VehicleType { get; set; }
public virtual MotorType MotorType { get; set; }
public virtual GearboxType GearboxType{ get; set; }
public virtual Door Door { get; set; }
public virtual VehicleLength VehicleLength { get; set; }
public string ProjectName { get; set; }
}
And you will be able to call it like:
var odl = ... // get ODL entity from DbSet
odl.VehicleType.Name
odl.GearboxType.SomeProperty

ASP.NET MVC How to access Entity Framework generated foreign key?

Song has a many to many relationship with it's self. I store these ids in a class called SimilarVersion with both id columns.
public class Song
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
public virtual ICollection<SimilarVersion> SimilarVersions { get; set; } = new List<SimilarVersion>();
}
public class SimilarVersion
{
public int Id { get; set; }
public int? Song_Id1 { get; set; }
}
View Models:
public class SongDto
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
public ICollection<SimilarSongDto> SimilarSongDtos { get; set; } = new List<SimilarSongDto>();
}
public class SimilarSongDto
{
public int Id { get; set; }
public string AudioName { get; set; }
public string ArtistName { get; set; }
...
}
The SimilarVersion table in my database now has Id,Song_Id,Song_Id1, as EF has generated Song_Id. How do I get to use that EF generated column in my code though?
_similarVersionService.GetSimiliarVersion().Song_Id will give me an error because there is no property in the class called that. I could manually add it to the class like I have done with Song_Id1 and remove the collection from the other class but I think I must be doing something wrong. Also please tell me if there is a better way of mapping this.
I tried adding public int Song_Id { get; set; } and it just made another column in my table called Song_Id2.
public ActionResult Song(int id)
{
//Map the domainModel to songViewModel
var songDto = Mapper.Map<Song, SongDto>(_songService.GetSong(id));
//Get all of the songs where the id == the Song_Id column in similar version table
var songs = _songService.GetSongs().ToList()
.Where(x => x.SimilarVersions.Any(z => z.Song_Id == songDto.Id))
.ToList(); //z.Song_Id no definition found
//Map the Song domain to SimilarSong ViewModel and assign it to the songDto to be passed to the view
songDto.SimilarSongDtos = Mapper.Map<ICollection<Song>, ICollection<SimilarSongDto>>(songs);
return View(songDto);
}
Edit. Trying to add to a row based on Admir answer:
var songToUpload = new Song
{
AudioName = uploadSongDtos[i].AudioName.Trim(),
ArtistName = uploadSongDtos[i].ArtistName,
};
foreach (var compareAgainstString in _songService.GetSongs().ToDictionary(x => x.Id, x => x.AudioName))
{
var score = SearchContext.Levenshtein.iLD(songToUpload.AudioName, compareAgainstString.Value);
//Don't add the current song
if (score < 50 && songToUpload.Id != compareAgainstString.Key)
songToUpload.SimilarVersionsWhereSimilar.Add(new SimilarVersion { SimilarId = compareAgainstString.Key });
}
Both OriginalId and SimilarId are assigned to whatever the id of songToUpload.Id is given the relationship we defined in models, which is correct for OriginalId but it is also overriding my custom set SimilarId above. How can I stop this?
You can take this approach:
public class Song
{
public int Id { get; set; }
public string ArtistName { get; set; }
public virtual IList<Similarity> SimilaritiesWhereOriginal { get; set; }
public virtual IList<Similarity> SimilaritiesWhereSimilar { get; set; }
}
public class Similarity
{
public int Id { get; set; }
public int OriginalId { get; set; }
public virtual Song Original { get; set; }
public int SimilarId { get; set; }
public virtual Song Similar { get; set; }
}
public class ApplicationDbContext : DbContext
{
public DbSet<Song> Songs { get; set; }
public DbSet<Similarity> Similarities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Song>().HasMany(x => x.SimilaritiesWhereOriginal).WithRequired(x => x.Original).WillCascadeOnDelete(false);
modelBuilder.Entity<Song>().HasMany(x => x.SimilaritiesWhereSimilar).WithRequired(x => x.Similar).WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
}
Similarity class shows relationship between "original" song and "similar" song. This class replaces EF auto-generated table with your own many-2-many relationship table that you can access from the code.
It is likely the ID is actually generated by your Store. Call Context.SaveChanges() to create it, then read the ID.

How can I add in a couple of additional fields to the lowest level of a Linq query

I have the following Linq Statement:
db.AdminTests
.Include(t => t.AdminTestQuestions)
.ToList();
This gives me a list of AdminTests and in each of these there is a collection with AdminTestQuestions:
public class AdminTest
{
public AdminTest()
{
this.AdminTestQuestions = new List<AdminTestQuestion>();
}
public int AdminTestId { get; set; }
public virtual ICollection<AdminTestQuestion> AdminTestQuestions { get; set; }
}
The data returned in AdminTestQuestions is a collection containing this information:
public partial class AdminTestQuestion
{
public int AdminTestQuestionId { get; set; }
public int AdminTestId { get; set; }
public System.Guid QuestionUId { get; set; }
public virtual AdminTest AdminTest { get; set; }
// I would like to add subTopicId here
// I would like to add title here
}
Is there a way that I could return additional information into this collection? Firstly to do this would I need to add a couple of virtual parameters? Second how/can I do this with LINQ?
Specifically what I would like to do is to add an additional two pieces of data that are based on the QuestionUId. I would like to add "Title" (from the question table) and "SubTopicId" (from the problem table). These can be obtained by somehow joining to the question table and then to the problem table:
public class Question
{
public int QuestionId { get; set; }
public int ProblemId { get; set; }
public Guid QuestionUId { get; set; }
// I think I will need to add this code here but I did not do it yet
// public Question() {
// this.AdminTestQuestions = new HashSet<AdminTestQuestion>();
// }
// public virtual ICollection<AdmintestQuestion> AdminTestQuestions { get; set; }
}
public class Problem
{
public Problem()
{
this.Questions = new HashSet<Question>();
}
public int ProblemId { get; set; }
public int SubTopicId { get; set; }
public virtual SubTopic SubTopic { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
Simple, no you can't as you're working with a type that is defined at compile time. Your linq query runs at runtime and is bound to the types that were available at compile time.
Your only solution would be to create a new anonymous type but that's an entirely different solution.

Asp.Net MVC Reaching The Property Using Many To Many Relation With Mapping Table

You can see my previous question which related with many to many relation but with auto generated mapping table.
I have 2 model, HrTraining and HrPerson. Any people can be assigned to one or more Trainings. You can see my model as below
public class HrTraining
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
public class HrMapTrainingPerson
{
public int Id { get; set; }
public string Status { get; set; }
public int HrTrainingId { get; set; }
public int HrPersonId { get; set; }
public virtual HrTraining HrTraining { get; set; }
public virtual HrPerson HrPerson { get; set; }
}
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapTrainingPerson> HrMapTrainingPerson { get; set; }
}
How can i take all training objects which assingned to a person with efficient way.
So you want to find a person, and get all the trainings assigned to it? There are lot of ways.. but using your models, this could be something like this
var trPersons = dbContext.HrPerson.Find(idPerson).HrMapTrainingPerson.ToList();
foreach(var trPerson in trPersons) {
var training = trPerson.HrTraining;
//do what you want, here you can get trPerson.HrTraining.Name for instance
}

Categories