I have two IEnumerable<dynamic> of data retrieved from database tables, called FullSet and InScopeSubSet .
The second IEnumerable<dynamic> is a subset of the first set (both sets have ChildID as their unique ID)
I would like to create a new IEnumerable<dynamic> that contains only items from x that do not occur in y
I have tried the following but it will not compile. It says:
"extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax"
var sql = "SELECT ChildID, FirstName, Surname FROM Child ORDER BY ChildID ASC";
var FullSet = DB.Query(sql);
sql = "
SELECT UserScope.ChildID, Child.FirstName, Child.Surname
FROM UserScope
INNER JOIN Child ON UserScope.ChildID=Child.ChildID
WHERE UserAccountID = #0 ORDER BY ChildID ASC
";
var InScopeSubSet = DB.Query(sql, UserAccount.UserAccountID);
var OutScopeSubSet = FullSet .Except(InScopeSubSet );
To resolve the compiler error, use its second suggestion:
var OutScopeSubSet = Enumerable.Except(FullSet, InScopeSubSet);
The Enumerable one runs ok but returns the whole first set without taking out any items.
If that's the case, you are probably getting reference comparisons on objects that are not identical. You might be able to do it by implementing a custom IEqualityComparer. The call becomes
var OutScopeSubSet = Enumerable.Except(FullSet, InScopeSubSet, new DynamicChildIdComparer());
And DynamicChildIdComparer is:
class DynamicChildIdComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
return ((dynamic)x).ChildID.Equals(((dynamic)y).ChildID);
}
public int GetHashCode(object obj)
{
return ((dynamic)obj).ChildID.GetHashCode();
}
}
Are you looking for a "not in" SQL type operation
"Not in" in LINQ can be implemented like this :
var filterData = new List { "ListA", "ListB" };
var data= context.table.Select( c => c.id ).Except( filterData );
Related
I am trying to remove duplicate code throughout my project and I am at a standstill trying to figure this out. What I am trying to do is create a base linq query that will be reused to add things like Where, Take...etc in multiple different methods.
public IQueryable<Object> FooLinq(int id)
{
using (var ctx = new dbEntities())
{
var results =
(from account in ctx.account
join memberProducts in ctx.tblMemberProducts on account.Id equals memberProducts.AccountId
orderby account.date descending
select new{account,memberProducts}).ToList();
return results;
}
}
So that would be by base query above and I would have a seperate method that would reuse VioLinq but this time would use a where clause in it.
public List<IncomingViolations> Foo1(int id)
{
//Linq query FooLinq() where Name == "Bob"
}
You'll need to do two things:
Return the query prior to materializing it.
Make sure the context is still in scope when the final query is materialized.
These two requirements will play off each other somewhat, and there are a number of approaches you can take to meet them.
For example, you could make your method take the context as a parameter, forcing the caller to provide it and manage its lifecycle.
public IQueryable<AccountInfo> FooLinq(DbEntities ctx, int id)
{
return
from account in ctx.account
orderby account.date descending
select new AccountInfo()
{
Name = account.Name,
Mid = account.MemberID,
Date = account.Date,
Address = account.Address,
};
}
public List<IncomingViolations> Foo1(int id)
{
using(var ctx = new dbEntities())
{
//Linq query FooLinq() where Name == "Bob"
return FooLinq(ctx).Where(v => v.Name == "Bob").ToList();
}
}
You could alternatively inject the context as a constructor-injected dependency, and use a DI framework to manage the context's lifecycle.
You can do it as Queryable then add conditions to it.
For example:
public List<account> GetAccountsByName(string name, bool usePaging, int offset = 0, int take = 0) {
var query = GetMyQuery();
query = query.Where(x => x.Name == name);
query = query.OrderBy(x => x.Name);
if(usePaging) {
query = query.Take(take).Skip(offset);
}
query = PrepareSelectForAccount(query);
return query.ToList(); .
}
public IQueryable<account> GetMyQuery(){
return ctx.account.AsQueryable();
}
public IQueryable<account> PrepareSelectForAccount(IQueryAble<account> query){
return query.Select(select new AccountInfo()
{
Name = account.Name,
Mid = account.MemberID,
Date = account.Date,
Address = account.Address,
}
);
}
Sure, but don't call .ToList(), and return IQueryable<T> instead of List<T>. LINQ is based on the concept of deferred execution which means the query is not actually performed until the enumerable is iterated over. Until then, all you have done is built an object which knows how to do the query when the time comes.
By returning an IQueryable<T> from a function that sets up the "basic query," you are then free to tack on additional LINQ methods (such as .Where() or .Take()) to produce a modified query. At this point you are still simply setting up the query; it is actually performed only when you iterate over the enumerable, or call something like .ToList() which does that for you.
I've simple Linq2Sql query:
var result = from t in MyContext.MyItems
select new MyViewModelClass()
{
FirstProperty = t,
SecondProperty = new SomeLinq2SqlEntity()
}
The problem is that it seems that new SomeLinq2SqlEntity() is executed only once for the sequence, so all instances of MyViewModelClass in result of the query share the link to one object.
Update: Here is how I quickly check it:
result[0].SecondProperty.MyField = 10;
Using debugger I can check that MyField was set to 10 in all instances.
When I replace LINQ query with foreach, it works as expected:
var result = from t in MyContext.MyItems select t;
var list = new List<MyViewModelClass>();
foreach (var item in result)
{
list.add(new MyViewModelClass()
{
FirstProperty = item,
SecondProperty = new SomeLinq2SqlEntity()
});
}
I haven't found the root of the problem, but the post marked as asnwer provides good workaround. Check this asnwer for the detailed description: "new" inside concrete type projection is only called once
It probably has something to do with weird IQueryable implementation of your provider.
Aducci's answer extracts data from database with AsEnumerable() call and performs the query on that set, which is different from performing it through IQueryable.
For example IQueryable builds the ExpressionTree which it later parses according to the concrete provider (i.e. executing shared code once for optimization), while IEnumerable accepts Func and performs it as you would expect.
You can read more here:
http://msdn.microsoft.com/en-us/vcsharp/ff963710
Have you tried using adding the SomeLinq2SqlEntity object with linq to objects?
var result = (from t in MyContext.MyItems
select new
{
FirstProperty = t
})
.AsEnumerable()
.Select(t => new MyViewModelClass()
{
FirstProperty = t.FirstProperty ,
SecondProperty = new SomeLinq2SqlEntity();
});
I'm trying to construct a Where clause for a Linq statement which needs to determine whether the AccountNumber values retrieved as below exist in a List<string> collection.
I've thus far tried this:
private void FindAccountNumbers(List<string> AccountNumbers)
{
var query = from abc
select new
{
AccountNumber = abc.AccountNumber
};
query = query.Where(AccountNumbers.Contains(x => x.AccountNumber));
}
However I get the following build error:
The type arguments for method
'System.Linq.Queryable.Where(System.Linq.IQueryable,
System.Linq.Expressions.Expression>)' cannot
be inferred from the usage. Try specifying the type arguments
explicitly.
At runtime, query contains AccountNumber values, and I'm trying to pare this down based on matches found in the AccountNumbers collection (similar to an IN statement in TSQL). Should I be using Intersect instead of Contains? What am I doing wrong??
I think you want to have this:
query = query.Where(x => AccountNumbers.Contains(x.AccountNumber));
This doesn't work?
var query = from x in abc
where AccountNumbers.Contains(x.AccountNumber)
select new { x.AccountNumber };
That would give you back any AccountNumber in that list, unless AccountNumber isn't actually a string. That could be your problem.
Its because your syntax for from is wrong, I'm guessing that your collection is abc of items to match against is abc
The correct syntax would be (Version 1)
var query = from x in abc
select new { AccountNumber = x.AccountNumber };
query = query.Where(x=>AccountNumbers.Contains(x.AccountNumber));
you don't need to do an anonymous type either as you are just wanting the same field you could just do (Version 2)
var query = from x in abc select x.AccountNumber;
query = query.Where(x=>AccountNumbers.Contains(x));
However you could just slap the Where straight onto your original collection. (Version 3)
var query = abc.Where(x=>AccountNumbers.Contains(x.AccountNumber);
Or if you are just trying to find whether any exist in the collection (Version 4)
var query = abc.Any(x=>AccountNumbers.Countains(x.AccountNumber);
Version 1 will return IEnumerable<string>
Version 2 will return IEnumerable<string>
Version 3 will return IEnumerable<type of the items in abc>
Version 4 will return bool
Let me verify what you're trying to do.
You have a collection of objects abc. You want to pull out the AccountNumber from each member of that collection, compare it to the list of account numbers passed in, and determine... what? If there IS any overlap, or WHAT the overlap is?
If the AccountNumber field is a string, you could do this:
private IEnumerable<string> OverlappingAccountNumbers(IEnumerable<string> accountNumbers)
{
return abc.Select(x => x.AccountNumber)
.Intersect(accountNumbers);
}
Or for the boolean case:
private bool AnyOverlappingAccountNumbers(IEnumerable<string> accountNumbers)
{
return abc.Select(x => x.AccountNumber)
.Intersect(accountNumbers)
.Count() > 0;
}
I'd go with this:
private void FindAccountNumbers(List<string> AccountNumbers)
{
// Get a strongly-typed list, instead of an anonymous typed one...
var query = (from a in abc select a.AccountNumber).AsEnumerable();
// Grab a quick intersect
var matched = query.Intersect(AccountNumbers)
}
One liner?
var query = (from a in abc select a.AccountNumber).AsEnumerable().Intersect(AccountNumbers);
The last answer are wrong because did mention one important point and obviously didn't be tested, the first issue is, you can't mix between an sql query than not been execute and a string's list, YOU CAN'T MIX!!! the solution for this problem and tested is:
var AccountNumbers = select accountNumber from table where.... // is a entitie
private void FindAccountNumbers(IQueryable<acounts> AccountNumbers) //entitie object not string
{
var query = from abc
select new
{
AccountNumber = abc.AccountNumber
};
query = query.Join(AccountNumbers, abc => abc.AccountNumber, aco=> aco, (ac, coNum) => cpac);
}
It really works! is necessary to mention this solution is when you are working with linq and entities framework!
I know there are a lot of examples of this on the web, but I can't seem to get this to work.
Let me try to set this up, I have a list of custom objects that I need to have limited on a range of values.
I have a sort variable that changes based on some action on the UI, and I need to process the object differently based on that.
Here is my object:
MyObject.ID - Just an identifier
MyObject.Cost - The cost of the object.
MyObject.Name - The name of the object.
Now I need to filter this based on a range in the cost, so I will have something similar to this, considering that I could be limiting by Either of my bottom two properties.
var product = from mo in myobject
where mo.Cost <= 10000
or
var product = from mo in myobject
where mo.Name equals strName
Now I have the dynamic linq in my project, but I'm not figuring out how to get it to actually work, as when I do some of the examples I am only getting:
Func<Tsourse>bool> predicate
as an option.
Update:
I am trying to find a solution that helps me Objectify my code, as right now it is a lot of copy and paste for my linq queries.
Update 2:
Is there an obvious performance difference between:
var product = from mo in myobject
... a few joins ...
where mo.Cost <= 10000
and
var product = (from mo in myobject
... a few joins ...)
.AsQueryable()
.Where("Cost > 1000")
Maybe not directly answering your question, but DynamicQuery is unnecessary here. You can write this query as:
public IEnumerable<MyObject> GetMyObjects(int? maxCost, string name)
{
var query = context.MyObjects;
if (maxCost != null)
{
query = query.Where(mo => mo.Cost <= (int)maxCost);
}
if (!string.IsNullOrEmpty(name))
{
query = query.Where(mo => mo.Name == name);
}
return query;
}
If the conditions are mutually exclusive then just change the second if into an else if.
I use this pattern all the time. What "Dynamic Query" really means is combining pure SQL with Linq; it doesn't really help you that much with generating conditions on the fly.
using System.Linq;
var products = mo.Where(x => x.Name == "xyz");
var products = mo.Where(x => x.Cost <= 1000);
var products = mo.Where(x => x.Name == "xyz" || x.Cost <= 1000);
Read this great post on DLINQ by ScottGu
Dynamic LINQ (Part 1: Using the LINQ Dynamic Query Library)
You would need something like
var product = myobject.Where("Cost <= 10000");
var product = myobject.Where("Name = #0", strName);
If you downloaded the samples you need to find the Dynamic.cs file in the sample. You need to copy this file into your project and then add
using System.Linq.Dynamic; to the class you are trying to use Dynamic Linq in.
EDIT: To answer your edit. Yes, there is of course a performance difference. If you know the variations of filters beforehand then I would suggest writing them out without using DLINQ.
You can create your own Extension Method like so.
public static class FilterExtensions
{
public static IEnumerable<T> AddFilter<T,T1>(this IEnumerable<T> list, Func<T,T1, bool> filter, T1 argument )
{
return list.Where(foo => filter(foo, argument) );
}
}
Then create your filter methods.
public bool FilterById(Foo obj, int id)
{
return obj.id == id;
}
public bool FilterByName(Foo obj, string name)
{
return obj.name == name;
}
Now you can use this on an IEnumerable<Foo> very easily.
List<Foo> foos = new List<Foo>();
foos.Add(new Foo() { id = 1, name = "test" });
foos.Add(new Foo() { id = 1, name = "test1" });
foos.Add(new Foo() { id = 2, name = "test2" });
//Example 1
//get all Foos's by Id == 1
var list1 = foos.AddFilter(FilterById, 1);
//Example 2
//get all Foo's by name == "test1"
var list2 = foos.AddFilter(FilterByName, "test1");
//Example 3
//get all Foo's by Id and Name
var list1 = foos.AddFilter(FilterById, 1).AddFilter(FilterByName, "test1");
Ok, I have managed to get the following working
public IQueryable getTicketInformation(int ticketID)
{
var ticketDetails = from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new { tickets.ticket_id, tickets.title, tickets.care_of_email, file.filename };
return ticketDetails.AsQueryable();
}
I went ahead and created my own class (myObject) containing the primitives
ticket_id, title, care_of_email and filename. Which are the items I am returning in my linq statement.
I modified my statement to be
public IQueryable<myObject> getTicketInformation(int ticketID)
{
var ticketDetails = from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new { tickets.ticket_id, tickets.title, tickets.care_of_email, file.filename };
return ticketDetails.AsQueryable()<myObject>;
}
thinking that this would make it type safe with generics, but I get the error
"Cannot convert method group 'AsQueryable' to non-delegate type 'System.Linq.IQueryable'. Did you intend to invoke the method?"
Is what I am trying to do even possible?
Does the myObject class need to implement IEnumerable or IQueryable?
Or is it best to construct the object MyObject from the linq resultset and then just return from the function the object MyObject
public myObject getTicketInformation(int ticketID)
{
....linq statement....
myObject o = null;
foreach (obj in linqstatemt)
{
o = new myObject();
o.ticket_id = obj.ticket_id
.......
}
return o;
}
You mean:
select new MyObject { TicketId = tickets.ticket_id,
Title = tickets.title, ...};
(note I tweaked the names slightly to be more C#-idiomatic)
This is an "object initializer" that creates a new MyObject (per record) and assigns the properties from the source data. What you had was an "anonymous type" initializer, which isn't the same. Note that if you have a non-default constructor, you could also use something like:
select new MyObject(tickets.ticket_id, tickets.title);
which uses the specified constructor, passing in the supplied values from the source data.
This will then be IQueryable<MyObject>; you don't need to call .AsQueryable(). Note it would be better for your function to return the typed form (IQueryable<MyObject>) than the untyped IQueryable.
This line is syntactically incorrect:
return ticketDetails.AsQueryable()<myObject>;
and should read
return ticketDetails.AsQueryable<myObject>();
Also, you're creating anonymous objects with the select new {, but you want to create myObject instances. A correct implementation would look like this:
public IQueryable<myObject> getTicketInformation(int ticketID)
{
return from tickets in _context.tickets
join file in _context.file_objects on tickets.ticket_id equals file.source_id
where tickets.ticket_id == ticketID
select new myObject() {
ticket_id = tickets.ticket_id,
title = tickets.title,
care_of_email = tickets.care_of_email,
filename = file.filename
};
}
The new SomeClass() { Property = value, ... syntax creates a SomeClass instance and sets the properties to the given values. Alternatively you could implement a constructor on the myObject class and call it in the linq statement with select new myObject(...).
As Marc stated you're not constructing instances of myObject when your query is run. But additionally you don't need to cast it to an IQueryable<T>, a LINQ select statment will return an IQueryable<T> unless explicity cast to an IEnumerable<T>.
Also, be careful that your DataContext hasn't been disposed of before you try and access the data being returned. But I noticed your context is not constructed in the method, be careful that you're not maintaining a DataContext for too long, it's a unit-of-work object and not meant to be kept open for long periods of time.
Gents,
It all makes sense as long as you're only returning single table, but what if there's two or more to be returned???
RPDTDataContext smdt = new RPDTDataContext();
var projectedUsers = smdt.SM_Users.Join(
smdt.SM_CTSGroups, u => u.CtsGroupID, c => c.id,
(u, c) => new { CTSGroup = c.Name, UserName = u.Name, u.EmpID, u.Email });
return projectedUsers;