LINQ-to-SQL select filter - linq

Is there a way to ensure a particular conditional clause is added to the expression tree on each select from a particular table?
For example, a table with a field with the date a record was deleted should never come back or be included in any kind of statement.
Rather than include a where clause each time, is there a way, without creating a view, to add a conditional to each select?
--- Edit for clarity below ---
I'm looking for a function I can partial, much like the Insert/Update/Delete functions, but for Selecting. I want to apply a blanket filter to all queries against a table.
Furthermore, if I get a collection of items from a parent, I want that set to be filtered as well.
Something like:
Private Function BaseItems() As IQueryable(Of Item)
Return (From mi In dataContext.Items Where mi.DeletedAt Is Nothing Select mi)
End Function
Public Function GetItems() as list(of Item)
Return (From mi in BaseItems() select mi).ToList()
End Function
works for functions I write and call. ITEMS, can be a child of MASTER, for example.
'assume TheMaster is a LinqToSQL data class which has a one to many child of Items
TheMaster.Items.Count '<-- will bring back all Items.
How do I always filter what populates by data classes?

You should be able to do this my putting the items into a list and then use lambda expressions to filter the list?
MyListObject.Where(x => x == x.Date);

I'm not sure I understand the context of your question. However you can add more where conditionals on a linq query. If you return an IQueryable, you'll only return a runnable query and you can chain on it other LINQ queries. Afaik it won't execute until you start making it to an IEnumerable or List to iterate through.
Example on LINQ-to-SQL:
MyDataContext context = new MyDataContext();
public IQueryable<MyTable> GetTable() {
return from record in context.Records
where record.Date > DateTime.Now
select record;
}
public IEnumerable<MyTable> GetTableWithinWeek() {
return from record in GetTable()
where record.Date < DateTime.Now.AddDays(7);
select record;
}
I hope my answer makes sense.

AssociateWith in the DataLoadOptions of the DataContext seems to do what I'm needing.

Related

Linq to Entities - Filter on any item in one list belonging to another list

Here is my scenario. I have a Document class. The document is associated with a DocumentClasses table through a many to many relationship, so a document can have one or more classes. When running a search, the user can choose to filter documents by class. So, I need to be able to append a where clause to my query if the user chooses to select any classes. The logic is that if the document is assigned to any of the classes in the classes the user selected, the document should be returned. So the basic query needed in pseudo code. So basically, if any number in list A belongs to list B, then return the record.
I have tried this (RestrictByClasses is just a List(Of Integer)):
query = query.where(Function(resultItem) RestrictByClasses.Contains(resultItem.DocumentClassIds.Any())
But I get the following exception:
The nested query is not supported. Operation1='Case' Operation2='Collect'
Is there any way to get linq to filter records out like this?
Thanks!
UPDATE:
After doing a little more debugging, I think that it is more to do with how I am projecting onto the object in order to load it with values that can be used to filter. Here is how I am doing the projection:
Dim query = From document In dbContext.Documents
Select New FeeAndReceptionReportIntermediateItem With
{
.BookTypeId = If(restrictByBookTypes AndAlso document.DocumentInstruments.Any(), document.DocumentInstruments.FirstOrDefault().Instrument.BookTypeId, Nothing),
.CustomerId = document.CustomerId,
.DocumentClassIds = If(restrictByDocumentClasses, document.DocumentClasses.Select(Function(group) group.ClassId), Nothing),
.DocumentId = document.DocumentId,
.DocumentNumber = document.DocumentNumber,
.DepartmentId = document.DepartmentId,
.InstrumentGroupIds = If(restrictByInstrumentGroup, document.DocumentInstruments.FirstOrDefault().Instrument.InstrumentGroups.Select(Function(group) group.InstrumentGroupId), Nothing),
.RecordDateTime = document.RecordDateTime,
.RestrictedInstrument = (includeRestrictedDocuments AndAlso document.DocumentInstruments.Any() AndAlso document.DocumentInstruments.FirstOrDefault().Instrument.Restricted)
}
I think it is complaining about how the .DocumentClassIds and .InstrumentGroupId's are being loaded into the POCO object (FeeAndReceptionReportIntermediateItem). I would really like to load these up in the initial query, before a .ToList() has been called and I would really like to not even do the join if the user did not pass in the restrictions that require me to create the join, that's why I am using the navigation properties and an if statement when loading these collecctions, because I am assuming if "restrictByDocumentClasses" is false, the navigation property won't be accessed and the join won't be included.
This works as a general pattern. The first line gets an IQueryable<> from the DbSet<>. The select does this for us, so that we can continue reusing query to hold our query as we build it up. Then just keep adding on If...Then...query=query.Where(...)...Endif to continue whittling down the resultset.
var query=db.MyTable.Select(x=>x);
if (RestrictByClasses.Any())
query=query.Where(r =>
r.DocumentClasses.Select(x=>x.ClassId)
.Intersect(RestrictByClasses)
.Any());
if (RestrictBySomethingElse)
query=query.Where(x=>SomethingElse)
I think this is the equivalent in VB.NET:
Dim query = db.MyTable.[Select](Function(x) x)
If RestrictByClasses.Any() Then
query = query.Where(Function(r) r.DocumentClasses.Select(Function(group) group.ClassId).Intersect(RestrictByClasses).Any())
End If
'Repeat as necessary
If RestrictBySomethingElse Then
query = query.Where(Function(x) SomethingElse)
End If
'End repeat
' Rest here is pseudo code
' Sort
SELECT/SWITCH sortonfield
CASE 'name': query=query.OrderBy(Function(x) x.name)
CASE 'dob': query=query.OrderBy(Function(x) x.dob)
DEFAULT: query=query.OrderBy(Function(x) x.id)
END CASE
'Paginate
query=query.Skip((pagenumber-1)*pagesize).Take(pagesize)
'Project
Dim finalresult=query.Select(Function(x) new something {
name=x.Name,
id=x.id,
things=x.things
});
Once all your filters have been put in place (and optionally a sort, and pagination), then project your resultset into whatever you need.

How to combine collection of linq queries into a single sql request

Thanks for checking this out.
My situation is that I have a system where the user can create custom filtered views which I build into a linq query on the request. On the interface they want to see the counts of all the views they have created; pretty straight forward. I'm familiar with combining multiple queries into a single call but in this case I don't know how many queries I have initially.
Does anyone know of a technique where this loop combines the count queries into a single query that I can then execute with a ToList() or FirstOrDefault()?
//TODO Performance this isn't good...
foreach (IMeetingViewDetail view in currentViews)
{
view.RecordCount = GetViewSpecificQuery(view.CustomFilters).Count();
}
Here is an example of multiple queries combined as I'm referring to. This is two queries which I then combine into an anonymous projection resulting in a single request to the sql server.
IQueryable<EventType> eventTypes = _eventTypeService.GetRecords().AreActive<EventType>();
IQueryable<EventPreferredSetup> preferredSetupTypes = _eventPreferredSetupService.GetRecords().AreActive<EventPreferredSetup>();
var options = someBaseQuery.Select(x => new
{
EventTypes = eventTypes.AsEnumerable(),
PreferredSetupTypes = preferredSetupTypes.AsEnumerable()
}).FirstOrDefault();
Well, for performance considerations, I would change the interface from IEnumerable<T> to a collection that has a Count property. Both IList<T> and ICollection<T> have a count property.
This way, the collection object is keeping track of its size and you just need to read it.
If you really wanted to avoid the loop, you could redefine the RecordCount to be a lazy loaded integer that calls GetViewSpecificQuery to get the count once.
private int? _recordCount = null;
public int RecordCount
{
get
{
if (_recordCount == null)
_recordCount = GetViewSpecificQuery(view.CustomFilters).Count;
return _recordCount.Value;
}
}

NHibernate IQueryable doesn't seem to delay execution

I'm using NHibernate 3.2 and I have a repository method that looks like:
public IEnumerable<MyModel> GetActiveMyModel()
{
return from m in Session.Query<MyModel>()
where m.Active == true
select m;
}
Which works as expected. However, sometimes when I use this method I want to filter it further:
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.ID < 100
select new { m.Name };
Which produces the same SQL as the first one and the second filter and select must be done after the fact. I thought the whole point in LINQ is that it formed an expression tree that was unravelled when it's needed and therefore the correct SQL for the job could be created, saving my database requests.
If not, it means all of my repository methods have to return exactly what is needed and I can't make use of LINQ further down the chain without taking a penalty.
Have I got this wrong?
Updated
In response to the comment below: I omitted the line where I iterate over the results, which causes the initial SQL to be run (WHERE Active = 1) and the second filter (ID < 100) is obviously done in .NET.
Also, If I replace the second chunk of code with
var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
where m.Items.Count > 0
select new { m.Name };
It generates the initial SQL to retrieve the active records and then runs a separate SQL statement for each record to find out how many Items it has, rather than writing something like I'd expect:
SELECT Name
FROM MyModel m
WHERE Active = 1
AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0
You are returning IEnumerable<MyModel> from the method, which will cause in-memory evaluation from that point on, even if the underlying sequence is IQueryable<MyModel>.
If you want to allow code after GetActiveMyModel to add to the SQL query, return IQueryable<MyModel> instead.
You're running IEnumerable's extension method "Where" instead of IQueryable's. It will still evaluate lazily and give the same output, however it evaluates the IQueryable on entry and you're filtering the collection in memory instead of against the database.
When you later add an extra condition on another table (the count), it has to lazily fetch each and every one of the Items collections from the database since it has already evaluated the IQueryable before it knew about the condition.
(Yes, I would also like to be the extensive extension methods on IEnumerable to instead be virtual members, but, alas, they're not)

What Linq to Entities statement tells me if a particular child entity exists?

Here is a foreach statement I'd like to express in Linq to Entities. It loops through child entities (attachments) of a parent entity (currentFactSheet) to see if an attachment exists with a particular FileName. How can I condense this procedural code into a Linq to Entites statement?
FactSheet currentFactSheet = mainWindow.GetCurrentFactSheet();
bool attachmentExists = false;
foreach (var thisAttachment in currentFactSheet.AttachmentsNav)
{
if (thisAttachment.FileName == nameOfAttachedFile)
{
attachmentExists = true;
}
}
This is a partial image showing FactSheet (left) and the Attachment entity associated via a navigation property named AttachmentsNav:
I want to query in memory entities to avoid a round trip to the database. I've found examples like this that search only the parent level. I've made many attempts, but they never bring up intellisense with the field names on my child entity (specifically Attachment.FileName).
Thanks in advance.
Try this:
bool attachmentExists = currentFactSheet.AttachmentsNav.Any(a => a.FileName == nameOfAttachedFile);

How do I perform a dynamic select in Linq?

I am trying to figure out how to dynamically specify the properties for my select clause in a linq query.
Lets say I have a collection of employee objects. At run time, the end user will be specifying which properties they would like to see for those employees, so I need to be able to dynamically construct my Linq select clause.
I have used the dynamic Linq library, but I prefer not to use that, because it requires me to build a string to pass to the select method. I'd like to understand how to do this via Expressions.
This looks like something that fits more with your requirements of not using dynamic linq.
Use Reflection to get the dynamic Column Values
//columns variable has column name as comma separated String which you
can save in DB //example string columns ="Name,Id,Age";
var strColumns =columns.split(,);
foreach(var myObject in MyObjectcollection)
{
for(int index =0;index<strColumns.count();index++)
{
//Create a collection of objects
mycollection.add(myObject.GetType().GetProperty(strColumns[index]).GetValue(myObject, null));
}
}

Resources