I have a sequence of objects, whose type has many properties and I would like to join it with another sequence and set a value taking from the second sequence. But because there are many properties I do not prefer to create a new anonymous type by doing all those property assignments. Is there a better way to do this in a single linq query (having been trying un-successfully with let)
var x = from lt in legalTerms
join le in legalEntities on lt.LegalEntityCode equals le.Code
select new {a = lt.a, b = lt.b, c = le.c, d = lt.d .... z=lt.z} // don't like
I need a sequence like legalTerms where the items have that one property updated from legalEntities
Wish there was somehow a way to clone le and set that one property c = le.c without using reflection etc
Basically this is what I wonder is possible in a linq statement
foreach (var lt in legalTerms)
{
foreach (var le in legalEntities)
{
if (le.Code == lt.LegalEntityCode)
{
lt.LegalEntity = le.Name;
}
}
}
how about this
var map = new Func<LegalTerm, LegalEntity, LegalTerm>((term, entity) =>
{
term.LegalEntity = entity.Name;
return term;
});
var query = from lt in legalTerms
join le in legalEntities
on lt.Code equals le.LegalEntityCode
select map(lt, le);
knowing that you have some sort of structures similar to those bellow
public class LegalTerm
{
public string Code { get; set; }
public string LegalEntity { get; set; }
public string a { get; set; }
public string b { get; set; }
public string c { get; set; }
public string d { get; set; }
}
public class LegalEntity
{
public string LegalEntityCode { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Related
looks like I got stuck with a nested Linq Query. I have 4 Tables which I want to join. Basically a journal has one Recipient and multiple Readers. I'd like to show the Journal with it's Recipient and all it's Readers. Here's the EF Query
var myJournals = (
from s in db.Journals
where !s.Blacklist
join recToJournals in db.RecipientsToJournals on s.JournalID equals recToJournals.JournalID
join recipients in db.Recipients on recToJournals.RecipientID equals recipients.RecipientID
join reaToJournals in db.ReadersToJournals on s.JournalID equals reaToJournals.JournalID
join readers in db.Readers on reaToJournals.ReaderID equals readers.ReaderID
select new AnalysisViewModel
{
JournalID = s.JournalID,
Title = s.Title,
RecipientName = recipients.FullName,
ReaderList = readers.FullName.ToList()
});
return View(myJournals);
Here's the ViewModel:
public class AnalysisViewModel
{
public int JournalID { get; set; }
public string Title { get; set; }
public List<Char> ReaderList { get; set; }
public string ReaderName { get; set; }
public string RecipientName { get; set; }
}
Here I'll get an Exception System.NotSupportedException: The method 'ToList' is not supported when called on an instance of type 'String'.
If I use ReaderName = readers.FullName it works, but I get a List with multiple Journals and their Readers.
How can I show only one Journal with it's Recipient and all it's Readers?
This does not make sense. Why are you calling ToList on a string? Why would you even want List<char>? Change your model so the type is of string and remove ToString
Change 1 - in your linq statement
ReaderList = readers.FullName // remove .ToList
Change 2 - in your model
public string ReaderList { get; set; }
Although it is not technically wrong it is not best practice to name a property of type string (or any non collection type for that mater) with the suffix List. A more suitable name would be ReaderName.
First of all change your ViewModel
public class AnalysisViewModel
{
public int JournalID { get; set; }
public string Title { get; set; }
public string RecipientFullName { get; set; }
public List<string> ReadersFullNames { get; set; }
}
You need GroupBy
var myJournals = from s in db.Journals
where !s.Blacklist
join recToJournals in db.RecipientsToJournals
on s.JournalID equals recToJournals.JournalID
join recipients in db.Recipients
on recToJournals.RecipientID equals recipients.RecipientID
join reaToJournals in db.ReadersToJournals
on s.JournalID equals reaToJournals.JournalID
join readers in db.Readers
on reaToJournals.ReaderID equals readers.ReaderID
select new
{
JournalID = s.JournalID,
Title = s.Title,
RecipientFullName = recipients.FullName,
ReaderFullName = readers.FullName
};
var result = myJournals.GroupBy(j => new { j.JournalID, j.Title, j.RecipientFullName })
.Select(g => new AnalysisViewModel
{
JournalID = g.Key.JournalID,
Title = g.Key.Title,
RecipientFullName = g.Key.RecipientFullName,
ReadersFullNames = g.Select(r => r.ReaderFullName).ToList(),
}
)
.ToList();
I have Medals class, I call a service that return all Medal class fields except for the two fields ArabicName and ISOCode; Which I have to bring them from another table class "CountrysTBLs" , I made this join code:
The Medal class:
public class Medals {
public int bronze_count { get; set; }
public string country_name { get; set; }
public int gold_count { get; set; }
public string id { get; set; }
public int place { get; set; }
public int silver_count { get; set; }
public int total_count { get; set; }
public string ArabicName { get; set; } // this field not return by service
public string ISOCode { get; set; } // this field not return by service
}
The code:
var cntrs = db.CountrysTBLs.ToList();
List<Medals> objs = call_Service_Of_Medals_Without_ISOCode();
IEnumerable<Medals> query = from obj in objs
join cntry in cntrs on obj.country_name equals '%' + cntry.CommonName + '%'
select new Medals
{
ArabicName = cntry.ArabicName,
ISOCode = cntry.ISOCode,
country_name = obj.country_name,
place = obj.place,
gold_count = obj.gold_count,
silver_count = obj.silver_count,
bronze_count = obj.bronze_count,
total_count = obj.total_count
};
I get no result?!
How to fix that? Is there is any way to bring the two fields (ISOCode, ArabicName) without even use the inner join, and in same time best performance?
You want something like this to achieve LIKE functionality
List<Medals> medals = new List<Medals>();
var list = medals.Select(x => new
{
obj = x,
country = countries.FirstOrDefault(c => c.CommonName.Contains(x.country_name))
});
or something like this (if you want to just enrich each medal)
foreach (var medal in medals)
{
var country = countries.FirstOrDefault(c => c.CommonName.Contains(x.country_name));
medal.ISOCode = country.ISOCode;
medal.ArabicName = country.ArabicName;
}
Do note that this is not as performant as a Dictionary<string,Coutnry> of countries where the key is the country name, but as you need a LIKE comparison you would need to bring in a custom data structure such as Lucene index for fast lookups. But check first, if the lists are small enough, it probably won't be a problem. Otherwise, why not make the Medal.Country_Name and Country.Name the same? So you can do quick Dictionary (hashtable) lookups
Inside the "Distributions" variable is a key called "Deadline" which contains a date.
I would like to add "RealDeadline = i.Deadline,". All the other lines works fine, but I just cant find a way to add the last thing.
The match has to be made on AssignmentId which is the key for the whole combine. basically if the HandInData.Where(...) could just add the value of "Deadline" from "Distributions", that would do the trick..
var HandInData = db.Handins.ToList();
var Distributions = db.Distributes.ToList();
var AssignNames = HandInData.Where(a => Distributions.Any(x => x.AssignmentId == a.AssignmentId));
var StudentsHandedInDataFeed = AssignNames.Select(i => new {
*RealDeadline = i.Deadline, (this is not working..)*
Help = i.NeedHelp,
Done = i.Done,
AssName = i.Assignment.AssignmentName,
Student = i.Student.StudentName,
DeadlineInTimeformat = i.Assignment.AssignmentDeadline,
HandedInInTimeformat = i.HandedInDate,
Deadline = i.Assignment.AssignmentDeadline.ToString(),
HandedIn = i.HandedInDate.ToString()
});
public class Handin {
public int HandinId { get; set; }
public int StudentId { get; set; }
public int AssignmentId { get; set; }
public bool? Done { get; set; }
public bool? NeedHelp { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime? HandedInDate { get; set; }
public virtual Student Student { get; set; }
public virtual Assignment Assignment { get; set; }
}
You need to join the two lists. You can do it with LINQ syntax like this:
var StudentsHandedInDatFeed =
from h in HandInData
join d in Distributions on h.AssignmentId equals d.AssignmentId
select new {
RealDeadline = d.Deadline,
Help = h.NeedHelp,
// etc
};
The join will only include values from HandInData where there is a matching value in Distributions, so this takes care of your Where(a => Distributions.Any(... code as well.
I have a class
public class AmenityShowtime
{
public String AmenityKey { get; set; }
public String AmenityIcon { get; set; }
public String shTimes { get; set; }
}
Ultimately, I want to have a structure that is comprised of these nested classes:
public class AmenityShowtime
{
public String AmenityKey { get; set; }
public String AmenityIcon { get; set; }
public String shTimes { get; set; }
}
// Movie Class
public class theMovie
{
public String Movie_title { get; set; }
public String Rating { get; set; }
public String RunTime { get; set; }
public List<theAmenities> amens { get; set; }
}
public class theAmenities
{
public String AmenityName { get; set; }
public String AmenityIcon { get; set; }
public List<theTimes> times { get; set; }
}
public class theTimes
{
public String timepref { get; set; }
}
I needed to group by AmenityKey and shtimes ... I used the following code:
IEnumerable<IGrouping<string, string>> query = amShow.GroupBy(ams => ams.AmenityKey, ams => ams.shTimes);
List<theAmenities> thisMoviesList = new List<theAmenities>();
foreach (IGrouping<string, string> grp in query)
{
theAmenities thisMovieAmenities = new theAmenities();
thisMovieAmenities.AmenityName = grp.Key;
List<theTimes> thisMovieTimes = new List<theTimes>();
foreach (string stimes in grp)
{
theTimes thisShowtime = new theTimes();
thisShowtime.timepref = stimes;
thisMovieTimes.Add(thisShowtime);
}
thisMovieAmenities.times = thisMovieTimes;
thisMoviesList.Add(thisMovieAmenities);
}
works great, with one exception ... how do I get access to the field: AmenityIcon in the
foreach (IGrouping<string, string> grp in query)
{
theAmenities thisMovieAmenities = new theAmenities();
thisMovieAmenities.AmenityName = grp.Key;
I want to be able to do the following:
thisMovieAmenites.AmenityIcon = AmenityIcon
I must be missing something, thank you in advance
If you want to include the amenityIcon in the grouping, then one way is this:
var query = amShow.GroupBy(ams => new {ams.AmenityKey, ams.AmenityIcon},
ams => ams.shTimes);
Note that you do need the var keyword now, since this is an IGrouping<Anonymous-type,String>
(This will group based on key and icon, I am assuming that the icon is the same if the key is the same, so this is essentially the same as grouping only by key)
Now you can do
foreach (var grp in query)
{
theAmenities thisMovieAmenities = new theAmenities();
thisMovieAmenities.AmenityName = grp.Key.AmenityKey;
thisMovieAmenities.AmenityIcon = grp.Key.AmenityIcon;
...
If you want to avoid anonymous types, you could also create your class theAmenities right in the grouping:
IEnumerable<IGrouping<theAmenities,string>> query = amShow.GroupBy(
ams => new theAmenities(){AmenityKey = ams.AmenityKey, AmenityIcon = ams.AmenityIcon},
ams => ams.shTimes);
but that requires that your class theAmenities implements the IEquatable<T> interface, to allow the GroupBy operator to recognize all theAmenities objects with the same key as equal.
If you use an anonymous type instead, this will work automatically. But this approach has the advantage that you could let your IEquatable ignore the AmenityIcon, if indeed it is possible that there are multiple items with the same key but different icons
I have two views in my model.
I basically need to do an INNER JOIN on them based on three columns:
dataSource
ShowID
EpisodeID
The first thing I don't know how to do is add the SQL "AND" operator to the LINQ expression.
The second thing is, I don't know how to SELECT the JOINED table.
Can someone give me a hand?
var query = (from s in db.TVData_VW_ShowList
from z in db.TVData_VW_Schedule
where s.dataSource = z.dataSource
&& s.ShowID = z.ShowID
&& s.EpisodeId = z.EpisodeId select ...
You can use anonymous types to your advantage here, both to join across multiple columns, and to project into a new type containing data from both sides of the join. Here's a working example using Linq to objects:
namespace LinqExample
{
class Program
{
static void Main()
{
var Shows = new List<ShowData> { new ShowData { dataSource = "foo", EpisodeID = "foo", ShowID = "foo", SomeShowProperty = "showFoo" }};
var Schedules = new List<ScheduleData> { new ScheduleData { dataSource = "foo", EpisodeID = "foo", ShowID = "foo", SomeScheduleProperty = "scheduleFoo" } };
var results =
from show in Shows
join schedule in Schedules
on new { show.dataSource, show.ShowID, show.EpisodeID }
equals new { schedule.dataSource, schedule.ShowID, schedule.EpisodeID }
select new { show.SomeShowProperty, schedule.SomeScheduleProperty };
foreach (var result in results)
{
Console.WriteLine(result.SomeShowProperty + result.SomeScheduleProperty); //prints "showFoo scheduleFoo"
}
Console.ReadKey();
}
}
public class ShowData
{
public string dataSource { get; set; }
public string ShowID { get; set; }
public string EpisodeID { get; set; }
public string SomeShowProperty { get; set; }
}
public class ScheduleData
{
public string dataSource { get; set; }
public string ShowID { get; set; }
public string EpisodeID { get; set; }
public string SomeScheduleProperty { get; set; }
}
}
So to join you can use the join keyword then use on to specify the conditions. && (the logical and operator in C#) will be translated to the SQL AND keyword.
Also, in EF they have what are known as "implicit joins" meaning if I have TableA with a foreign key to TableB, call it fKey.
Doing where TableA.fKey == TableB.pKey will cause the provider to put a join there. To select you simply need to do;
select new { prop1 = TableA.Prop1, prop2 = TableB.Prop1 }
this will create a new anonymous which selects values from both tables.
Below is a more complete example of the join syntax. I think it uses all of the things you asked about;
var result = from a in TableA
join b in TableB on a.fKey equals b.pKey && b.Status equals 1
select new { a.Prop1, a.Prop2, b.Prop1 };
First you need to create an auxiliar class that contains the columns of both views, something like:
public class viewItem
{
public int ShowID { get; set; }
public int EpisodeID { get; set; }
public int dataSource { get; set; }
...
}
then your linq query would be:
var query = (from s in db.TVData_VW_ShowList
join z in db.TVData_VW_Schedule
on s.dataSource equals z.dataSource
where s.ShowID == z.ShowID
&& s.EpisodeID == z.EpisodeID
select new viewItem {
ShowID = s.ShowID,
EpisodeID = s.EpisodeID,
dataSource = s.dataSource,
...
}