I'm summarizing data to build an alert system. This query counts how many points an employee has based on some criteria. The one below is working fine. However, the query can be longer depending on the User's location. For example, below I have only have 3 unions but for other Employees' locations more unions may be needed. For example in Location B they may also want to add an alert if a ReasonTypeID == is 3 and any entries in the last 90 days.
So I was thinking on building a table that I can add parameters on a location by location basis. I've read about dynamic linq library and I can use that for the WHERE statements but how would I add another query concatenation on the fly?
Here is what I have now.
public class TMAlert
{
public string EmpID { get; set; }
public string FullName { get; set; }
public decimal? PointSummary { get; set; }
public string WarningLabel { get; set; }
public bool AlertFlag { get; set; }
}
IEnumerable<TMAlert> tmAlert = (
from a in allEntries
where a.Date >= DateTime.Now.AddDays(-30) && a.ReasonTypeID == 1
group a by new
{
a.EmpID,
a.FullName,
a.ReasonTypeID
} into g
select new TMAlert
{
EmpID = g.Key.EmpID,
FullName = g.Key.FullName,
WarningLabel = "Last 30 Days",
PointSummary = g.Sum(a => a.Points),
AlertFlag = (g.Sum(a => a.Points) >= 4) ? true : false
}).Concat(from a in allEntries
where a.Date >= DateTime.Now.AddDays(-90) && a.ReasonTypeID == 1
group a by new
{
a.EmpID,
a.FullName,
a.ReasonTypeID
} into g
select new TMAlert
{
EmpID = g.Key.EmpID,
FullName = g.Key.FullName,
WarningLabel = "Last 90 Days",
PointSummary = g.Sum(a => a.Points),
AlertFlag = (g.Sum(a => a.Points) >= 9) ? true : false
}).Concat(from a in allEntries
where a.Date >= (
from o in allEntries
where o.EmpID == a.EmpID && a.WarningTypeID == 2
select (DateTime?)o.Date).Max()
group a by new
{
a.EmpID,
a.FullName,
a.ReasonTypeID
} into g
select new TMAlert
{
EmpID = g.Key.EmpID,
FullName = g.Key.FullName,
WarningLabel = "60 Since Warning type 2 ",
PointSummary = g.Sum(a => a.Points),
AlertFlag = (g.Sum(a => a.Points) >= 4) ? true : false
});
I was thinking to build a criteria table then build the queries on the fly based on the table.
for example
for each entry in criteriaTable
add dynamic linq entry.Param
.concat(....
next....
is this possible or are there better ways to do this?
There is a way to combine linq queries dynamically. I use it to add conditional filters to my where statements when showing a filtered list of data.
I initialize the lambda expression like this
Expression<Func<CatalogItem, bool>> Filter = CatalogItem => true;
then as I go I add to it.
if(!string.IsNullOrEmpty(searchVal)) {Filter = Filter.Compose(CatalogItem => CatalogItem.Title.ToLower().Contains(searchVal), Expression.And);}
I may have different if conditions that cause me to add more and more to my filter then finally I want to apply that filter against my collection.
IQueryable<CatalogItem> FilteredList = Items.AsQueryable().Where(Filter);
In this case Items is a list of CatalogItems that was pulled from the database earlier. I am using IQueryable instead of just List<> cause I need to act on the results afterwards.
List<CatalogItem> Items;//fill Items list here;
I know this is in-line Linq as opposed to the structure you use but I really can't get into the other form of linq querying as this is so quick and dirty for me. If you have to use the other linq form these rules may still apply.
Related
I know that Linq cannot handle ToString() and I've read a few work arounds, most seem to be doing the casting outside of the Linq query, but this is for the output where I am trying to shove it into a list and this is where it is blowing up.
As the code will show below I did some casting elsewhere already in order to make it fit in Linq, but the very last part has the tostring and this I need to rewrite too but I'm not sure how.
DateTime edate = DateTime.Parse(end);
DateTime sdate = DateTime.Parse(start);
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new GaugeData() { SiteID = siteid.site_name, Data_Time = rainfall.trend_data_time, Trend_Data = float.Parse(rainfall.trend_data_avg.ToString()) }).ToList();
Linq will handle it, however Linq2Entities will not since EF will want to relay that expression to the DbProvider which doesn't understand/translate all .Net methods.
When it comes to extracting data from entities, the path with the least pain is to have your entity definitions should use the compatible .Net types matching the SQL data types. Then when you want to load that data into view models / DTOs where you might want to perform formatting or data type translation, let the ViewModel/DTO handle that or pre-materialized your Linq2Entity query into an anonymous type list and then process the translations /w Linq2Object.
Without knowing the data type of your TrendDataAvg, an example with a value stored as a decimal, but you want to work with float:
Formatting in ViewModel example:
public class TrendData // Entity
{ // ...
public decimal trend_data_avg { get; set; }
// ...
}
public class GuageData // ViewModel
{
public decimal trend_data_avg { get; set; } // Raw value.
public float Trend_Data // Formatted value.
{
get { return Convert.ToSingle(trend_data_avg); }
}
}
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new GaugeData() { SiteID = siteid.site_name, Data_Time = rainfall.trend_data_time, trend_data_avg = rainfall.trend_data_avg }).ToList();
Anonymous Types Example:
public class GuageData // ViewModel
{
public float Trend_Data { get; set; }
}
var reading = (from rainfall in db.trend_data
join mid in db.measurements on rainfall.measurement_id equals mid.measurement_id
join siteid in db.sites on mid.site_id equals siteid.site_id
where siteid.site_name == insite && rainfall.trend_data_time >= sdate && rainfall.trend_data_time <= edate
select new
{
siteid.site_name,
rainfall.trend_data_time,
rainfall.trend_data_avg
}.ToList() // Materializes our Linq2Entity query to POCO anon type.
.Select( x=> new GaugeData
{
SiteID = site_name,
Data_Time = trend_data_time,
Trend_Data = Convert.ToSingle(trend_data_avg)
}).ToList();
Note: If you use the Anonymous Type method and want to utilize paging, additional filtering, etc. then be sure to do it before the initial .ToList() call so that it is processed by the Linq2EF. Otherwise you would be fetching a much larger set of data from EF than is necessary with potential performance and resource utilization issues.
Additionally, if you set up your navigation properties in your entities you can avoid all of the explicit join syntax. EF is designed to do the lifting when it comes to the relational DB, not just an alternate syntax to T-SQL.
// Given trend data has a single measurement referencing a single site.
var gaugeData = db.trend_data
.Where(x => x.trend_data_time >= sdate
&& x.trend_data_time <= edate
&& x.measurement.site.site_name == insite))
.Select(x => new
{
x.measurement.site.site_name,
x.trend_data_time,
x.trend_data_avg
}).ToList()
.Select( x=> new GaugeData
{
SiteID = site_name,
Data_Time = trend_data_time,
Trend_Data = Convert.ToSingle(trend_data_avg)
}).ToList();
You could use the Convert.ToSingle() method, float is an alias for system.single.
Trend_Data = Convert.ToSingle(rainfall.trend_data_avg)
I have to to a rather large request to a database to fetch a bunch of data, it's however taking a noticeable time to run. is there some way to increase the performance on this? preemptive apologies for the ugly code (I did have a version that segmented this into multiple smaller functions but that was even slower)
from contact in _database.OBJECTCONTACT
where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTID == id && o.OBJECTTYPE == type) && contact.ACTIVE >= 1 && CheckViewAccess(contact)
group contact by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.First().OBJECTPROJECT.PROJECTNAME : "General") into projectGroup
select new ProjectViewModel()
{
ProjectName = projectGroup.Key,
ContactGroups = (from g in _database.OBJECTGROUP
where g.GROUPTYPE == "CONTACT" && ContactsModule.CheckUserRole("View", g.OBJECTTYPE, g.GROUPNAME)
select new ContactGroupViewModel()
{
CanEdit = ContactsModule.CheckUserRole("Edit", g.OBJECTTYPE, g.GROUPNAME),
GroupId = g.OBJECTGROUPID,
GroupName = g.GROUPNAME,
Contacts = (from c in projectGroup
join l in _database.OBJECTCONTACTLOCATION on c.OBJECTCONTACTLOCATIONID equals l.OBJECTCONTACTLOCATIONID into lgrp from loc in lgrp.DefaultIfEmpty(null)
orderby c.NAME
select new ContactViewModel()
{
Id = (int)c.OBJECTCONTACTID,
Name = c.NAME,
Description = c.DESCRIPTION,
ContactInformation = CreateContactInfoViewmodels(c),
Owners = c.OBJECTCONTACTOWNER.Where(owner => owner.OBJECTTYPE == "AIRPORT")
.Select(owner => ContactOwnerViewModel.FromOwnerId(owner.OBJECTID, owner.OBJECTTYPE)).ToList(),
Projects = c.OBJECTCONTACTPROJECT.Select(proj => proj.OBJECTPROJECT).ToList(),
Typename = GetTypeName(c),
TypeId = c.OBJECTCONTACTTYPEID ?? 0,
ContactGroupId = c.OBJECTGROUPID,
ContactGroup = g.GROUPNAME,
Editable = CheckAccessBool("EDIT", c),
Location = loc != null ? new LocationViewModel()
{
Address = loc.ADDRESS,
GoogleMapLink = loc.GMAPADDRESS,
LocationId = loc.OBJECTCONTACTLOCATIONID,
LatLon = Tuple.Create(loc.LATITUDE, loc.LONGITUDE)
} : null,
}).ToList()
}).ToList()
}).ToList();
I think I should be able to use joins to move the entire DB fetch code to the top (theoretically improving perfomance) but I am having trouble finding the syntax which would suit my needs
Thanks everyone for coming with suggestions. I am in a situation where I'm not able to do much with the database itself so I'm making the best of what I have. my hands a bit tied in regards to the tools at my disposal (also fairly old codebase I think it's EF 5 or something like that)
this version moves the DB transaction to the top (so that is fewer fetches) and does a lot of data manipulation at the bottom.
// general object is created above
var res = (from contact in _database.OBJECTCONTACT.AsEnumerable() // as enumerable used to allow for defaultifempty in join (minor damage to performance)
join oGroup in _database.OBJECTGROUP on contact.OBJECTGROUPID equals oGroup.OBJECTGROUPID into og from objectGroup in og.DefaultIfEmpty(defaultValue: general)
where contact.OBJECTCONTACTOWNER.Any(o => o.OBJECTTYPE == type && o.OBJECTID == id)
// ReSharper disable once PossibleNullReferenceException (it's taken care of by check using .any() )
group new {contact, objectGroup } by (contact.OBJECTCONTACTPROJECT.Any() ? contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECT.PROJECTNAME : "General") into pGroup
orderby pGroup.Key == "General" ? pGroup.Key : "" descending
select new ProjectViewModel()
{
ProjectName = pGroup.Key,
ProjectId = pGroup.FirstOrDefault() != null ? (pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault() != null ? pGroup.FirstOrDefault().contact.OBJECTCONTACTPROJECT.FirstOrDefault().OBJECTPROJECTID : -1) : -1,
ContactGroups = (from c in pGroup
group c by c.objectGroup into grp
let canEdit = ContactsModule.CheckUserRole("EDIT", grp.Key.OBJECTTYPE, grp.Key.GROUPNAME)
orderby grp.Key.SORTORDER descending
select new ContactGroupViewModel()
{
GroupName = grp.Key.GROUPNAME,
GroupId = grp.Key.OBJECTGROUPID,
CanEdit = canEdit,
Contacts = grp.Select(item => new ContactViewModel()
{
Id = (int)item.contact.OBJECTCONTACTID,
Name = item.contact.NAME,
Description = item.contact.DESCRIPTION,
Editable = canEdit,
ContactInformation = item.contact.OBJECTCONTACTNUMBER.OrderByDescending(num => num.ISMAININFO).Select(num => new ContactInfoViewmodel()
{
Data = num.NUMBERDATA,
IsMain = num.ISMAININFO > 0,
Type = num.OBJECTCONTACTNUMBERTYPE.NAME
}).ToList()
}).ToList()
}).ToList()
}).ToList();
this seems to (on average) take about a 4th of the time the original query needed (still a noticeable time due to the size of database but within acceptable limits)
I have the following LINQ query:
//two different contexts, databases, tables...
NoteSet = lmCtx.LMNotes.AsEnumerable();
EmpSet = tessCtx.Employees.AsEnumerable();
var lmAccountNotes = (from lmnote in NoteSet
join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID
join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID
where lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1
select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList();
This works for queries on small tables, but NoteSet is a pretty big table and I'm reaching well over 1.5GB of used memory by the process before the framework just explodes and throw an OutOfMemory exception.
Is there any way to keep the lazy loading feature while executing something like this ?
So as to keep having a query that returns a NoteInfo object, I changed it to this:
//LMNotes is the actual huge database...
var m = lmCtx.LMNotes.Where(x => x.lnt_recordId == 5566).ToList();
var lmAccountNotes = (from lmnote in m
join createdby in EmpSet on lmnote.lnt_createdById equals createdby.EmployeeID
join modifiedby in EmpSet on lmnote.lnt_modifiedById equals modifiedby.EmployeeID
where lmnote.lnt_recordId == 566 && lmnote.lnt_tableId == 1
select new NoteInfo { Note = lmnote, CreatedBy = createdby, ModifiedBy = modifiedby }).ToList();
This is better
As explained in the comments, you cannot really run a single query across two different database, at least not without setting up some help construct (which would then live on either database, and actually who knows if that would improve the performance at all).
However, that does not mean that we cannot improve your query at all. If we can’t rely on the database engine to execute the query, we can do that ourselves. In this case, what you are doing is essentially just a query on the LMNotes entity and then you join employees from the Employees set.
So a naive solution could look like this:
var notes = lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
return new NoteInfo
{
Note = lmnote,
CreatedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_createdById),
ModifiedBy = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == lmnote.lnt_modifiedById)
};
})
.ToList();
Of course, while this runs a single query on LMNotes, this still runs two separate queries for each note in the result. So it’s not really better than what EF would have done up there.
What we can do however is add some lookup. I suspect that the set of employees is somewhat limited, so it would make sense to only fetch each employee once. Something like this:
private Dictionary<int, Employee> employees = new Dictionary<int, Employee>();
private Employee GetEmployee(int employeeId)
{
Employee employee;
if (!employees.TryGetValue(employeeId, out employee))
{
employee = tessCtx.Employees.FirstOrDefault(e => e.EmployeeId == employeeId);
employees[employeeId] = employee;
}
return employee;
}
public List<NoteInfo> GetNotes()
{
return lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
return new NoteInfo
{
Note = lmnote,
CreatedBy = GetEmployee(lmnote.lnt_createdById),
ModifiedBy = GetEmployee(lmnote.lnt_modifiedById)
};
})
.ToList();
}
This would only look up each employee once and then cache the employee object.
Alternatively, you could also make a second pass here and fetch all employees at once after reading the notes for the first time. Something like this:
public List<NoteInfo> GetNotes()
{
var employeeIds = new HashSet<int>();
var notes = lmCtx.LMNotes
.Where(lmnote => lmnote.lnt_recordId == 5566 && lmnote.lnt_tableId == 1)
.Select(lmnote =>
{
// remember the ids for later
employeeIds.Add(lmnote.lnt_createdById);
employeeIds.Add(lmnote.lnt_modifiedById);
return new NoteInfo
{
Note = lmnote,
CreatedBy = null,
ModifiedBy = null
};
})
.ToList();
var employees = tessCtx.Employees
.Where(e => employeeIds.Contains(e.EmployeeId))
.ToList()
.ToDictionary(e => e.EmployeeId);
foreach (var noteInfo in notes)
{
noteInfo.CreatedBy = employees[noteInfo.Note.lnt_createdById];
noteInfo.ModifiedBy = employees[noteInfo.Note.lnt_modifiedById];
}
return notes;
}
This would only run a single query against each database.
I have a model called ElectricityBillSiteExceeding that looks like this:
public class ElectricityBillSiteExceeding
{
public string GroupInvoiceNumber { get; set; }
public int ElectricityBillMainId { get; set; }
public string SiteNo { get; set; }
public decimal BillSiteTotal { get; set; }
public decimal MaximumAmount { get; set; }
}
I want to create a list of this type and use it to feed a grid on one of my pages, the purpose is to show which site has bills that exceed the max amount allowed.
I have written the SQL which will give me this dataset, it looks like this:
SELECT SUM(ElectricityBillSiteTotal),
ebs.ElectricityBillMainId,
SiteNo,
ebm.GroupInvoiceNumber,
es.MaximumAmount
FROM dbo.ElectricityBillSites ebs
LEFT JOIN dbo.ElectricityBillMains ebm
ON ebs.ElectricityBillMainId = ebm.ElectricityBillMainId
LEFT JOIN dbo.ElectricitySites es
ON ebs.SiteNo = es.SiteNumber
GROUP BY ebs.ElectricityBillMainId, SiteNo, ebm.GroupInvoiceNumber, es.MaximumAmount
HAVING SUM(ElectricityBillSiteTotal) <> 0 AND SUM(ElectricityBillSiteTotal) > es.MaximumAmount
I'm now in my repository trying to write the method which will go to the database and fetch this dataset so that I can power my grid for the user to see.
This is where I'm struggling. I have written a basic LINQ statement to select from a couple of tables, however I'm unsure how I can incorporate the group by and having clause from my SQL and also how I can then turn this IQueryable object into my List<ElectricityBillSiteExceeding> object.
What I have so far
public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
var groupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites
join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals
billMains.ElectricityBillMainId
join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber
//TODO: group by total, mainId, siteNo, GroupInv, MaxAmt and Having no total = 0 and total > max
select new
{
groupInv = billMains.GroupInvoiceNumber,
mainId = billMains.ElectricityBillMainId,
siteNo = billSites.SiteNo,
total = billSites.ElectricityBillSiteTotal,
max = sites.MaximumAmount
};
//TODO: Map the result set of the linq to my model and return
throw new NotImplementedException();
}
Can anyone point me in the right direction here?
The correct Linq query for your sql is the following. See Left Join to understand the DefaultIfEmpty and also the notes there about the use of ?. in the following group by.
(About the having - in linq you just provide a where after the group by)
var result = from ebs in ElectricityBillSites
join ebm in ElectricityBillMains on ebs.ElectricityBillMainId equals ebm.ElectricityBillMainId into ebmj
from ebm in ebmj.DefaultIfEmpty()
join es in ElectricitySites on ebs.SiteNo equals es.SiteNumber into esj
from es in esj.DefaultIfEmpty()
group new { ebs, ebm, es } by new { ebs.ElectricityBillMainId, ebs.SiteNo, ebm?.GroupInvoiceNumber, es?.MaximumAmount } into grouping
let sum = grouping.Sum(item => item.ebs.ElectricityBillSiteTotal)
where sum > 0 && sum > grouping.Key.MaximumAmount
orderby sum descending
select new ElectricityBillSiteExceeding
{
GroupInvoiceNumber = grouping.Key.GroupInvoiceNumber,
ElectricityBillMainId = grouping.Key.ElectricityBillMainId,
SiteNo = grouping.Key.SiteNo,
BillSiteTotal = sum,
MaximumAmount = grouping.Key.MaximumAmount
};
The error you get:
An expression tree lambda may not contain a null propagating operator
By reading this I conclude that you have an older versino of the provider and thus replace the group by code from the code above with the following:
let GroupInvoiceNumber = ebm == null ? null : ebm.GroupInvoiceNumber
let MaximumAmount = es == null ? 0 : es.MaximumAmount
group new { ebs, ebm, es } by new { ebs.ElectricityBillMainId, ebs.SiteNo, GroupInvoiceNumber, MaximumAmount } into grouping
Before getting into grouping , you need to be aware that the default join in LINQ is always an INNER JOIN. Take a look at the MSDN page How to: Perform Left Outer Joins. However, in the solution I'm presenting below, I'm using INNER JOINs since you are using fields from the other tables in your grouping and having clauses.
For reference on grouping using LINQ, check out How to: Group Query Results on MSDN.
A solution specific to your case is going to look something like:
public List<ElectricityBillSiteExceeding> GetAllElectricityBillSiteExceedings()
{
var qryGroupedBillSitesThatExceed = from billSites in _context.ElectricityBillSites
join billMains in _context.ElectricityBillMains on billSites.ElectricityBillMainId equals billMains.ElectricityBillMainId
join sites in _context.ElectricitySites on billSites.SiteNo equals sites.SiteNumber
where billSites.ElectricityBillSiteTotal != 0 && billSites.ElectricityBillSiteTotal > sites.MaximumAmount
group new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount }
by new { billMains.GroupInvoiceNumber, billMains.ElectricityBillMainId, billSites.SiteNo, billSites.ElectricityBillSiteTotal, sites.MaximumAmount } into eGroup
select eGroup.Key;
var inMemGroupedBillSitesThatExceed = qryGroupedBillSitesThatExceed.AsEnumerable();
var finalResult = inMemGroupedBillSitesThatExceed.Select(r => new ElectricityBillSiteExceeding()
{
BillSiteTotal = r.ElectricityBillSiteTotal,
ElectricityBillMainId = r.ElectricityBillMainId,
GroupInvoiceNumber = r.GroupInvoiceNumber,
MaximumAmount = r.MaximumAmount,
SiteNo = r.SiteNo,
});
return finalResult.ToList();
}
This probably will be enough. You could use AutoMapper. It will trivialize mapping to classes.
var resultList = groupedBillSitesThatExceed
.AsEnumerable() //Query will be completed here and loaded from sql to memory
// From here we could use any of our class or methods
.Select(x => new ElectricityBillSiteExceeding
{
//Map your properties here
})
.ToList(); //Only if you want List instead IEnumerable
return resultList;
I have two lists comprised of different complex-objects, and each one is from 2 separate data-sources. One list may-or-may-not contain records. When any records exist in the "optional" list I need the "normal" list to be further-filtered.
Unfortunately, I can only find very simple examples here and online, which is why I am asking this question.
The Pseudo-Logic Goes Like This:
When QuickFindMaterial records exist, get all DataSource records where query.Name is in the QuickFindMaterial.Material collection. If no QuickFindMaterial records exist do not affect the final result. Lastly, select all distinct DataSourcerecords.
The Classes Looks Like:
public class QuickFindMaterial
{
public string SiteId { get; set; }
public string Material { get; set; }
}
The Code Looks Like:
I have commented-out my failed WHERE logic below
var dataSource = DocumentCollectionService.ListQuickFind();
var quickFindMaterial = ListMaterialBySiteID(customerSiteId);
var distinct = (from query in dataSource
select new
{
ID = query.DocumentID,
Library = query.DocumentLibrary,
ModifiedDate = query.DocumentModifiedDate,
Name = query.DocumentName,
Title = query.DocumentTitle,
Type = query.DocumentType,
Url = query.DocumentUrl,
})
//.Where(x => x.Name.Contains(quickFindMaterial.SelectMany(q => q.Material)))
//.Where(x => quickFindMaterial.Contains(x.Name))
.Distinct();
I think this is what you want:
.Where(x => !quickFindMaterial.Any() || quickFindMaterial.Any(y => x.Name == y.Material))
You could join on Name -> Material
Example:
var distinct = (from query in dataSource
join foo in quickFindMaterial on query.Name equals foo.Material
select new
{
ID = query.DocumentID,
Library = query.DocumentLibrary,
ModifiedDate = query.DocumentModifiedDate,
Name = query.DocumentName,
Title = query.DocumentTitle,
Type = query.DocumentType,
Url = query.DocumentUrl,
}).Distinct();