Querying Child Collections 2 levels deep in LINQ - c#

i currently have a linq to entities model set up as follows
each Sample has a collection Of Tests
each Test has a collection of Results
Each Result has Status property valuing whether it is Available or Completed
how would i write a linq query that would:
get the samples that have available Results
retaining only the tests that have available results
and only the results in each test that are available
having trouble getting my head around this problem and help with getting this
written would really help alot
Classes:
public class Sample
{
public Sample()
{
Tests = new List<Test>();
}
public int Id { get; set; }
public string IdText { get; set; }
public DateTime SampleDate { get; set; }
public DateTime LoginDate { get; set; }
public string Container { get; set; }
public string Product { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual SamplePoint SamplingPoint { get; set; }
public virtual SampleTemplate SampleTemplate { get; set; }
public virtual Customer ForCustomer { get; set; }
public virtual ICollection<Test> Tests { get; set; }
public class Test
{
public Test()
{
Results = new List<Result>();
}
public string Id { get; set; }
public string Status { get; set; }
public string Analysis { get; set; }
public string ComponentList { get; set; }
public virtual Instrument InstrumentUsed { get; set; }
public virtual ICollection<Result> Results { get; set; }
public virtual Sample ForSample { get; set; }
}
public class Result
{
public string Id { get; set; }
public string TestNumber { get; set; }
public string Status { get; set; }
public string Analysis { get; set; }
public string ComponentName { get; set; }
public string Text { get; set; }
public string Units { get; set; }
public double Value { get; set; }
public int OutOfRange { get; set; }
public DateTime SampledDate { get; set; }
public DateTime SampleLoginDate { get; set; }
public string SamplePoint { get; set; }
public virtual Sample ForSample { get; set; }
public virtual Test ForTest { get; set; }
}

If I understand your table structure then it's fairly easy to query down to get the results that you're interested in.
I put together a simple set of classes to test the results.
public static class db
{
public static List<Sample> Samples = new List<Sample>();
}
public class Sample
{
public string Name;
public List<Test> Tests = new List<Test>();
}
public class Test
{
public string Name;
public List<Result> Results = new List<Result>();
}
public class Result
{
public string Name;
public string Status;
}
And I created this set of test data:
From here it is easy to query the data down to just available results:
var query =
from s in db.Samples
from t in s.Tests
from r in t.Results
where r.Status == "Available"
select new { Sample = s.Name, Test = t.Name, Result = r };
Which gives me this data:
But that doesn't group the data by Sample and Test properly.
One way to do it properly is to create new Sample & Test objects that contain only the available results, like so:
var query =
from s in db.Samples
from rt in (
from t in s.Tests
from r in t.Results
where r.Status == "Available"
group r by t into rts
select new Test()
{
Name = rts.Key.Name,
Results = rts.ToList()
})
group rt by s into srts
select new Sample()
{
Name = srts.Key.Name,
Tests = srts.ToList()
};
This produces this result:
However, it might not be possible, or desirable, to create new instance of objects that look like actual entities but are not actually from the database. It might be possible to accidentally persist one of these objects back to the database and wipe out correct records!
So, an alternative, which I think is the best, is to create a nested structure that contains the unmodified database entities and includes the available tests in an extra properly all while keeping the nested structure!!
Here's how:
var query =
from s in db.Samples
from rt in
(from t in s.Tests
from r in t.Results
where r.Status == "Available"
group r by t into rts
select new
{
Test = rts.Key,
AvailableResults = rts.ToArray()
})
group rt by s into srts
select new
{
Sample = srts.Key,
AvailableTests = srts.ToArray()
};
And this produces:
With these results you still have access to the unchanged Sample and Test objects, but all filtered by the available results.
Let me know if this helps.

Without seeing your actual class structure, I'm hoping this can help in some way:
var testsWithAvailableResults = from test in dbContext.Tests
select new {
Results = (from result in test.Results where result.Status == "Available")
};

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 ;)

Linq Result into Observable collection throws parsing exception

I have created an observable collection
public ObservableCollection<DatagGridCollection> combine_audit_final_collection { get; set; }
which I'm trying to populate via linq result
var d = (from p in Auditcollectiondata
from c in Finalcollectiondata
where c.sno == p.sno
select new
{
p.sno,
p.AuditID,
p.claimnumber,
p.QueryID,
p.DateWorked,
p.UserID,
p.Line,
p.Dos,
p.CPT,
p.Units,
p.amtBilled,
p.RecoupUnit,
p.reocupamts,
p.ScioNote,
p.Linelevelstatus_valuetext,
p.providerNote,
c.ID_finalstatus,
c.FinalStatus
});
The join works fine but when I try to insert the results into observable collection. I'm getting casting error.
combine_audit_final_collection = new ObservableCollection<DatagGridCollection>((ObservableCollection<DatagGridCollection>) d);
The combine_audit_final_collection will be binded into the datagrid. Though there is no compile error I'm getting parsing exception at runtime while execute.
Update : I try to join two observable collection using sno and inserting the result into another observable collection 'combine_audit_final_collection'. If my approach is wrong please let me know any other approach.
public class DatagGridCollection
{
public bool update { get; set; }
public int sno { get; set; }
public string AuditID { get; set; }
public string claimnumber { get; set; }
public string QueryID { get; set; }
public DateTime DateWorked { get; set; }
public string UserID { get; set; }
public string Line { get; set; }
public string Dos { get; set; }
public string CPT { get; set; }
public string Units { get; set; }
public string amtBilled { get; set; }
public string RecoupUnit { get; set; }
public string reocupamts { get; set; }
public string ScioNote { get; set; }
public string Linelevelstatus_valuetext { get; set; }
public string providerNote { get; set; }
public int final_status_sno { get; set; }
public string Finalstatus { get; set; }
}
Are you sure that your ObservableCollection is a collection of DataGridCollections? Is every element of your collection a DataGridCollection?
If not, but it is in fact a collection of MyType, change the word DataGridCollection below with MyType
Anyway, if you would check in your debugger the type of object d, you would notice that it is not an IEnumerable<DatagGridCollection>.
Just change your code to:
select new DataGridCollection()
{
p.sno,
...
If you want to detect the cause of this kind of errors in the future, my advice would be not to use the word var too much, and not to do too much statements at once.
IEnumerable<DataGridCollection> d = ...
Select new DataGridCollection
{
...
};
combine_audit_final_collection = new ObservableCollection<DatagGridCollection>(d);
It will be much easier to find your errors.

Correct way to set up a 1 to Many So I can Query Attributes on the Included table with Entity Framework 6

I currently have a 2 table set up. I can include the Associated table with ease but I would like to be able to use a condition in the Where Clause and not sure where I need to change it. The relationship is a People can have Many Address
public partial class People
{
public People()
{
Address = new HashSet<Address>();
}
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int id { get; set; }
public string State { get; set; }
public int PeopleId { get; set; }
public People People { get; set; }
}
I can currently do this and it includes the Associated Data, but this returns more data than I need.
using (DBContextdb = new DBContext())
{
var oooo = db.People.IncludeOptimized(x => x.Address).ToListAsync();
}
I would like to do something along these lines but I need to get my relations set up correctly. This Doesn't actually let me select the property of State on the Address class.
using (DBContext db = new DBContext())
{
var oooo = db.People.IncludeOptimized(x => x.Address).Where(x => x.Address.State == "Florida").ToListAsync();
}
You can't access the 'state' Property because 'x.Address' is a collection.
Also the State-Property is of the type int?. But you try to compare it with the string "Florida". So you should also change that.
So if you need all Addresses which have the State 'Florida' you can use something like this:
A simplified Model class:
public class Model1
{
public IQueryable<People> People { get; set; }
public IQueryable<Address> Addresses { get; set; }
public Model1()
{
People = new List<People>().AsQueryable() ;
Addresses = new List<Address>().AsQueryable();
}
}
The new Address/People classes:
public partial class People
{
public People()
{
Address = new HashSet<Address>();
}
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int Id { get; set; }
public string State { get; set; }
public int PeopleId { get; set; }
public People People { get; set; }
}
And then you can use the following code:
Model1 model = new Model1();
var queryResult = model.Addresses.Where(a => a.State == "Florida");
EDIT
Here is the query you are looking for:
IQueryable<People> queryResult = model.Addresses.Where(a => a.State == "Florida").Select(a => a.People);

Petapoco - Couldn't find split point

I have been struggling with peta poco and related classes and are getting the error "Couldn't find split point between PetaPocoProofOfConcept.Resource and PetaPocoProofOfConcept.BookingType".
My two classes are:
[TableName("Resource"), PrimaryKey("Id")]
public class Resource
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public long MinTimeSpan { get; set; }
public long MaxTimeSpan { get; set; }
public long FixedTimeSpan { get; set; }
public DateTime ActiveStart { get; set; }
public DateTime ActiveEnd { get; set; }
public bool Active { get; set; }
public BookingType BookingType { get; set; }
public int StatusId { get; set; }
}
[TableName("BookingType"), PrimaryKey("Id")]
public class BookingType
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
I get the error when executing this line of code:
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
var resources = new Database(connection).Query<Resource, BookingType>("SELECT * FROM [Resource]").ToList();
}
I have been reading some documentation but cant seem to find any answer of why this fails. Does anyone know?
Thanks :)
That's not the way Petapoco multi mapping works.
You can use that syntax in this way:
var posts = db.Fetch<post, author>(#"
SELECT * FROM posts
LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id
");
This gives you two list with Posts and Authors.
If you want to perform more complex mappings (like your example), you need to write a callback like this:
var posts = db.Fetch<post, author, post>(
(p,a)=> { p.author_obj = a; return p; },
#"SELECT * FROM posts
LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id
");
More info on Petapoco Multi mapping

Linq query to retrieve collection from one class to another class

I have 2 classes SiteConfig, SiteConfigView. One is tightly coupled with my EF and one class to expose it to View models. Both the classes holds to a collection of type 'Brands'
I struck at writing a linq query to fetch the records from db to view model.
As I am exposing a different class to view model, I have to get the records of type 'SiteConfigView'. So I am writing a linq query but I am bit confused how to get the collection from SiteConfig to SiteConfigView.
There are my classes
public partial class SiteConfig
{
public SiteConfig()
{
this.SiteBrands = new HashSet<SiteBrand>();
}
public int IdSiteConfig { get; set; }
public string Name { get; set; }
public byte[] SiteLogo { get; set; }
public string Brands { get; set; }
public string LinkColour { get; set; }
public virtual ICollection<SiteBrand> SiteBrands { get; set; }
}
public class SiteConfigView
{
public SiteConfigView()
{
}
public int IdSiteConfig { get; set; }
public string Name { get; set; }
public byte[] SiteLogo { get; set; }
public string Brands { get; set; }
public string LinkColour { get; set; }
public IEnumerable<SiteBrandView> SiteBrands { get; set; }
}
And this is the query I am trying
var db = new SampleMVCEntities();
IQueryable<SiteConfig> test = db.SiteConfigs.Select(a => new SiteConfigView{Name = a.Name,LinkColour = a.LinkColour,SiteLogo = a.SiteLogo});
Can comebody guide me how to get the collection from SiteConfig to SiteConfigView.
Thanks
You're going on the right direction tried like this
var siteConfigs = db.SiteConfigs.AsEnumerable().Select(a => new SiteConfigView()
{
Name = a.Name,
LinkColour = a.LinkColour,
SiteLogo = a.SiteLogo,
SiteBrands = a.SiteBrands.AsEnumerable().Select(a => new SiteBrandView()
{
//Do the projection
}).ToList()
}).ToList();

Categories