Linq - EntityFramework NotSupportedException - c#

I have a query that looks like this:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new Case {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue ? new Notifier { Name = x.Notifier.Name } : null // This line throws exception
}).ToList();
A Case class can have 0..1 Notifier
The query above will result in the following System.NotSupportedException:
Unable to create a null constant value of type 'Models.Notifier'. Only entity types, enumeration types or primitive types are supported
in this context.
At the moment the only workaround I found is to loop the query result afterwards and manually populate Notifierlike this:
foreach (var c in caseList.Where(x => x.NotifierId.HasValue)
{
c.Notifier = (from x in context.Notifiers
where x.CaseId == c.CaseId
select new Notifier {
Name = x.Name
}).FirstOrDefault();
}
But I really don't want to do this because in my actual scenario it would generate hundreds of additional queries.
Is there any possible solution for a situation like this?.

I think you need to do that in two steps. First you can fetch only the data what you need with an anonymous type in a single query:
var caseList = (from x in context.Cases
where allowedCaseIds.Contains(x => x.CaseId)
select new {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
NotifierName = x.Notifier.Name
}).ToList();
After that, you can work in memory:
List<Case> cases = new List<Case>();
foreach (var c in caseList)
{
var case = new Case();
case.CaseId = c.CaseId;
case.NotifierId = c.NotifierId;
case.NotifierName = c.NotifierId.HasValue ? c.NotifierName : null;
cases.Add(case);
}

You could try writing your query as a chain of function calls rather than a query expression, then put an .AsEnumerable() in between:
var caseList = context.Clases
.Where(x => allowedCaseIds.Contains(x.CaseId))
.AsEnumerable() // Switch context
.Select(x => new Case() {
CaseId = x.CaseId,
NotifierId = x.NotifierId,
Notifier = x.NotifierId.HasValue
? new Notifier() { Name = x.Notifier.Name }
: null
})
.ToList();
This will cause EF to generate an SQL query only up to the point where you put the .AsEnumerable(), further down the road, LINQ to Objects will do all the work. This has the advantage that you can use code that cannot be translated to SQL and should not require a lot of changes to your existing code base (unless you're using a lot of let expressions...)

Related

Entity Framework - Using Linq to Invoke Field Name

I am using net 4.5, C# and Entity Framework. I want to be able to invoke a Field at runtime for a linq query.
using var( context = new SomeDataEntities())
{
var abc = from b in context.someTable
where b.SomeField == 1
select b.AnotherField;
}
However I am want to invoke b.SomeField based on a input string parameter.
my current code is
using var( context = new SomeDataEntities())
{
var abc = from b in context.someTable
where b.GetType().GetProperty("SomeField").GetValue(b, null).ToString() == "test"
select b.AnotherField;
}
If you want to dynamically create a where clause, the best way to do this is the method chaining instead of the linq format. For instance:
using (var context = new SomeDataEntities())
{
var query = context.Set<SomeTable>();
if (field1.HasValue)
{
query = query.Where(e => e.Field1 == field1.Value);
}
if (field2.HasValue)
{
query = query.Where(e => e.Field2 == field2.Value);
}
var abc = query.Select(b => b.AnotherField);
}
If you have a lot of fields or an unknown number, SQL generation might be your best strategy and can be accessed with the DbContext.Database.SqlQuery() methods.

The LINQ expression contains references to queries that are associated with different contexts

Here's my code:
var myStrings = (from x in db1.MyStrings.Where(x => homeStrings.Contains(x.Content))
join y in db2.MyStaticStringTranslations on x.Id equals y.id
select new MyStringModel()
{
Id = x.Id,
Original = x.Content,
Translation = y.translation
}).ToList();
And I get the error that the specified LINQ expression contains references to queries that are associated with different contexts. I know that the problem is that I try to access tables from both db1 and db2, but how do I fix this?
MyStrings is a small table
Load filtered MyStrings in memory, then join with MyStaticStringTranslations using LINQ:
// Read the small table into memory, and make a dictionary from it.
// The last step will use this dictionary for joining.
var byId = db1.MyStrings
.Where(x => homeStrings.Contains(x.Content))
.ToDictionary(s => s.Id);
// Extract the keys. We will need them to filter the big table
var ids = byId.Keys.ToList();
// Bring in only the relevant records
var myStrings = db2.MyStaticStringTranslations
.Where(y => ids.Contains(y.id))
.AsEnumerable() // Make sure the joining is done in memory
.Select(y => new {
Id = y.id
// Use y.id to look up the content from the dictionary
, Original = byId[y.id].Content
, Translation = y.translation
});
You are right that db1 and db2 can't be used in the same Linq expression. x and y have to be joined in this process and not by a Linq provider. Try this:
var x = db1.MyStrings.Where(xx => homeStrings.Contains(xx.Content)).ToEnumerable();
var y = db2.MyStaticStringTranslations.ToEnumerable();
var myStrings = (from a in x
join b in y on x.Id equals y.id
select new MyStringModel()
{
Id = x.Id,
Original = x.Content,
Translation = y.translation
}).ToList();
Refer to this answer for more details: The specified LINQ expression contains references to queries that are associated with different contexts
dasblinkenlight's answer has a better overall approach than this. In this answer I'm trying to minimize the diff against your original code.
I also faced the same problem:
"The specified LINQ expression contains references to queries that are associated with different contexts."
This is because it's not able to connect to two context at a time so i find the solution as below.
Here in this example I want to list the lottery cards with the owner name but the Table having the owner name is in another Database.So I made two context DB1Context and DB2Context.and write the code as follows:
var query = from lc in db1.LotteryCardMaster
from om in db2.OwnerMaster
where lc.IsActive == 1
select new
{
lc.CashCardID,
lc.CashCardNO,
om.PersonnelName,
lc.Status
};
AB.LottryList = new List<LotteryCardMaster>();
foreach (var result in query)
{
AB.LottryList.Add(new LotteryCardMaster()
{
CashCardID = result.CashCardID,
CashCardNO = result.CashCardNO,
PersonnelName =result.PersonnelName,
Status = result.Status
});
}
but this gives me the above error so i found the other way to perform joining on two tables from diffrent database.and that way is as below.
var query = from lc in db1.LotteryCardMaster
where lc.IsActive == 1
select new
{
lc.CashCardID,
lc.CashCardNO,
om.PersonnelName,
lc.Status
};
AB.LottryList = new List<LotteryCardMaster>();
foreach (var result in query)
{
AB.LottryList.Add(new LotteryCardMaster()
{
CashCardID = result.CashCardID,
CashCardNO = result.CashCardNO,
PersonnelName =db2.OwnerMaster.FirstOrDefault(x=>x.OwnerID== result.OwnerID).OwnerName,
Status = result.Status
});
}

Nested query in entity framework

I am getting the following exception:
The nested query is not supported. Operation1='Case' Operation2='Collect'
with this query
var Games = context.Games.Select(a => new GameModel
{
Members = (a.Type == 1 ? (a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
{
ID = c.UserID,
email = c.UserInfo.EmailAddress,
screenName = c.UserInfo.ScreenName
})) :
(a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
{
ID = d.UserID,
email = d.UserInfo.EmailAddress,
screenName = d.UserInfo.ScreenName
)))
})
when I don't include the condition in selecting Members, the query works fine. Is there a way I can do the conditional inside the query?
You're overestimating the power of LINQ translation to SQL. Not everything is translatable and there is no compiler warning for that due to the way LINQ works.
Nested collections are usually either a) not supported or b) end up in horrible SELECT N+1 queries. What you ask EF to do is to return an object tree. SQL does not support tree like results so you run into the object-relational impedance mismatch and it hurts.
I advise you to fetch the nested collection data as a second, completely separate query. That allows you more control and is guaranteed to work.
As a non-essential side-note, you will probably not be able to convince EF to use the ?: operator over sequences. That is very hard to translate. Think how you would write this as SQL - very hard and convoluted.
It looks like Linq to EF doesn't support the following
context.Games.Select(g => new
{
Field = g.IsX? queryable1 : queryable2
});
But, here's a hack you can use to get it to work:
context.Games.Select(g => new
{
Field = queryable1.Where(q => g.IsX)
.Concat(queryable2.Where(q => !g.IsX))
});
I faced the same problem. The solution was to load both results and determine what to use after the query (I know it has performance downside), but at least you can do it temporarily if deadline attacks you:
At the LINQ side
var Games = context.Games.Select(a => new GameModel
{
// carries type1 results
Members = a.UsersInGames.Where(b => b.GameID == a.ID && b.StatusID == 1).Select(c => new Member
{
ID = c.UserID,
email = c.UserInfo.EmailAddress,
screenName = c.UserInfo.ScreenName
})),
//You need to create this temporary carrier to carry type 2 results
MembersOfType2 = a.Teams.Where(b => b.GameID == a.ID).SelectMany(b => b.UsersInTeams.Where(c => c.StatusID == 1)).Select(d => new Member
{
ID = d.UserID,
email = d.UserInfo.EmailAddress,
screenName = d.UserInfo.ScreenName
})))
})
}
After that you may loop Gamesand make the assignment Members = MembersOfType2 if Type == 1 for a certain game.
I had this error too. I had code like this:
var Games = context.Games.Select(a => new GameModel
{
Members = (!filters.GetDatailedDataToo ? null : new List<MemberModel>())
};
This error occurs when null is used in ? : operation.
This is not that case, written up here, but I've wasted lot of time, I think anyone uses this case, who searches this error text..

linq query from two database

I have a linq query from two database, however, each time the program stops at the query point. I don't know how to debug linq using VS. Can someone help me figure it out what's wrong here? Thank you.
public List<Promotion> GetBroder(string source)
{
string _connString = ConfigurationManager.AppSettings["DB1"];
PromotionDataContext dc = new PromotionDataContext(_connString);
string connString = ConfigurationManager.AppSettings["DB2"];
ReachDirectDataContext RDdc = new ReachDirectDataContext(connString);
return (from b in RDdc.BrokerNos
from p in dc.Promotions
where p.Source == source && p.Broker == b.BrokerNo1
select new Promotion() {Code=p.Code,BrokerName=b.Name}).ToList<Promotion>();
}
Your linq statement looks fine. To aid in debugging, I find it helpful to assign the linq query to a local variable, then return the local variable. You can then set a breakpoint on the return statement, and when the debugger stops at the breakpoint, you can inspect the query local variable to see what's in it, interactively. You can use the Locals window in VS, or the Immediate Window to surf around inside your app's variables and see what's going on.
In particular, double check that the inputs into your linq query are actually providing data. Verify that RDdc.Brokernos is non-empty, and dc.Promotions, etc. If these are empty, the result will be empty. Track your bug "upstream".
Minor point: You don't need to specify the type parameter on the .ToList() call in the select. The compiler will infer the type automagically.
You can use the following to display the generated SQL for the Linq statement.
ReachDirectDataContext RDdc = new ReachDirectDataContext(connString);
RDdc.Log = Console.Out;
return (from b in RDdc.BrokerNos
from p in dc.Promotions
where p.Source == source && p.Broker == b.BrokerNo1
select new Promotion() {Code=p.Code,BrokerName=b.Name}).ToList<Promotion>();
You can try the following to separate out the queries.
var promotions = from p in dc.Promotions
where p.Source == source
select p;
var brokers = from o in promotions
join b in RDdc.BrokerNos on o.Broker equals b.BrokerNo1
select new Promotion
{
Code = o.Code,
BrokerName = b.Name
};
return brokers.ToList();
The double from looks suspicious to me.
(This is not the equivalent to SQL's JOIN statement, if that is what you were aiming for.)
If you can combine the contexts:
Try creating relationship between BrokerNos and Promotions in edmx and using navigation property in query.
For example:
var result = dc.Promotions.Where(p => p.Source == source).
Select(p => new Promotion() {
Code = p.Code,
BrokerName = p.Broker.Name, // use new navigation property here
});
If not (intersection will be done in memory, not on DB!!!:
var result1 = dc.Promotions.Where(p => p.Source == source).
Select(p => new Promotion() {
Code = p.Code,
BrokerId = p.BrokerId, // add id property for intermediate results
}).ToList();
var result2 = RDdc.Brokers.ToList();
var finalResult = result1.Where(p => result2.Contains(b => b.BrokerId == p.BrokerId)).Select(p => new Promotion{
Code = p.Code,
BrokerName = result2.Single(b => b.BrokerId == p.BrokerId).Name,
});

String.Split in a Linq-To-SQL Query?

I have a database table that contains an nvarchar column like this:
1|12.6|18|19
I have a Business Object that has a Decimal[] property.
My LINQ Query looks like this:
var temp = from r in db.SomeTable select new BusinessObject {
// Other BusinessObject Properties snipped as they are straight 1:1
MeterValues = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray()
};
var result = temp.ToArray();
This throws an NotSupportedException: Method 'System.String[] Split(Char[])' has no supported translation to SQL.
That kinda sucks :) Is there any way I can do this without having to add a string property to the business object or selecting an anonymous type and then iterating through it?
My current "solution" is:
var temp = from r in db.SomeTable select new {
mv = r.MeterValues,
bo = new BusinessObject { // all the other fields }
};
var result = new List<BusinessObject>();
foreach(var t in temp) {
var bo = t.bo;
bo.MeterValues = t.mv.Split('|').Select(Decimal.Parse).ToArray();
result.Add(bo);
}
return result.ToArray(); // The Method returns BusinessObject[]
That's kinda ugly though, with that temporary list.
I've tried adding a let mv = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray() but that essentially leads to the same NotSupportedException.
This is .net 3.5SP1 if that matters.
You need to force the select clause to run on the client by calling .AsEnumerable() first:
var result = db.SomeTable.AsEnumerable().Select(r => new BusinessObject {
...
MeterValues = r.MeterValues.Split('|').Select(Decimal.Parse).ToArray()
}).ToList();
You can't use split, but in this scenario you can do the following:
// Database value is 1|12.6|18|19
string valueToFind = "19";
var temp = from r in db.SomeTable.Where(r => ("|" + r.MeterValues + "|").Contains("|" + valueToFind + "|"));
This code adds outer pipes (|) to the database value on the fly inside the query so you can do start, middle, and end value matches on the string.
For example, the above code looks for "|19|" inside "|1|12.6|18|19|", which is found and valid. This will work for any other valueToFind.
You don't need to use a temporary list:
var query = from r in db.SomeTable
select new
{
r.Id,
r.Name,
r.MeterValues,
...
};
var temp = from x in query.AsEnumerable()
select new BusinessObject
{
Id = x.Id,
Name = x.Name,
MeterValues = x.mv.Split('|').Select(Decimal.Parse).ToArray(),
...
};
return temp.ToArray();
Unfortunately its the IQueryable you are using (Linq to SQL) that is not supporting the Split function.
You are really only left with the IEnumerable (Linq to Objects) support for it in this case. You second code snippet is what you need to do, or something like...
var temp = (from r in db.SomeTable select new {
mv = r.MeterValues,
bo = new BusinessObject { // all the other fields }
}).AsEnumerable().Select(blah, blah) ;

Categories