EF4 - Linq to SQLite and List.Contains strange behaviour - c#

I have some issue querying my local sqlite-database. Below is the current code:
public static Dictionary<Guid, string> GetHashedComanyMembers(List<Guid> IdsToSearch)
{
using (var dbContext = new DbEntities(GetConnectionString()))
{
var test = dbContext.<myTable>.Where(cm => IdsToSearch.Contains(cm.IdToFind));
}
}
The variable test contains an emtpt dataset. And now to my question.
If is set a variable
var firstGuid = IdsToSearch[0]
and i change the next line to
var test = dbContext.<myTable>.Where(cm => cm.IdToFind == firstGuid);
test contains the two sets i want to have.
If i let me show the (first) generated statement and run it on the database directly it also fails to find something. But the (second) statement succeeds.
So does anyone have any idea where i'm going wrong or could point out the error?
Thanks in advance, Steve
The first version:
SELECT
[Extent1].[ValueId] AS [ValueId],
[Extent1].[ValueFirstName] AS [ValueFirstName],
[Extent1].[ValueLastName] AS [ValueLastName],
[Extent1].[ValueLastChanged] AS [ValueLastChanged],
[Extent1].[ValueLastChangedBy] AS [ValueLastChangedBy]
FROM [tblValue] AS [Extent1]
WHERE '28826de0-27ea-42ab-9104-234ee289de16' = [Extent1].[ValueId]
Second:
SELECT
[Extent1].[ValueId] AS [ValueId],
[Extent1].[ValueFirstName] AS [ValueFirstName],
[Extent1].[ValueLastName] AS [ValueLastName],
[Extent1].[ValueLastChanged] AS [ValueLastChanged],
[Extent1].[ValueLastChangedBy] AS [ValueLastChangedBy]
FROM [tblValue] AS [Extent1]
WHERE [Extent1].[ValueId] = #p__linq__0
Edit
If i call th ToList() method on my table before the Where() everything is fine because the whole table-content will be enumerated an i call the Contains on this list. But that can't be the solution to Select the whole table first.
So i think the problem is the conversation done by Linq because the generated statements checks agains the Guid as string but it is internally stored as Blob.
In the second statement the guid is not set directly. There is an placeholder that will be replaced by DBMS? And this query is alright.
So any ideas where to start?

Related

MySQL Linq using .Contains(variable)

Setup info:
VS2013 / C#
EF6
MySQL database
.Net Connector 6.9.5
I'm trying to create a method that returns a collection of Account records using a partial name as the search criteria. If I hard code a string value using the IQueryable .Contains() extension method, it returns data. However, when I attempt to use a variable no data is returned.
Public class Test() {
MyEntities db = new MyEntities();
//Works....but the search criteria is hard coded.
public IQueryable<Account> WorksButValueHardCoded() {
return (from a in db.Accounts
where a.accountname.Contains("Test")
select a);
}
//Does not return anything
public IQueryable<Account> DoesNotReturnAnyData() {
//Obviously I would use a parameter, but even this test fails
string searchText = "Test";
return (from a in db.Accounts
where a.accountname.Contains(searchText)
select a);
}
}
I can see in the LINQ generated SQL used the LIKE operator, but I don't understand how the variable is injected as it reads:
SELECT
`Extent1`.`accountid`,
`Extent1`.`accountname`
FROM `account` AS `Extent1`
WHERE `Extent1`.`accountname` LIKE '%p__linq__0%'
So...why does it work with the hard coded value and not a string variable?
I ran into the same problem and followed the error step by step with Glimpse (nice tool to inspect what the server is doing). It turned out that the SQL-Statement is built correctly, because I got results by executing it on the database.
The problem could be the replacement of the string variables in the statement. I guess LINQ isn't replacing just the string you pass but fills the variable with spaces to the VARCHAR limit so your query looks like
SELECT`Extent1`.`accountid`, `Extent1`.`accountname`
FROM `account` AS `Extent1`
WHERE `Extent1`.`accountname` LIKE '%Test ... %'
Add
.Trim()
to your string and it works.
public IQueryable<Account> DoesNotReturnAnyData() {
string searchText = "Test";
// Use Trim() here
return (from a in db.Accounts
where a.accountname.Contains(searchText.Trim())
select a);
}
There is nothing wrong in theory i can see
Update
This is working fine for me on sqlServer
public IQueryable<Account> DoesNotReturnAnyData(MyEntities db,string searchText) {
return (from a in db.Accounts
where a.accountname.Contains(searchText )
select a)
}
This is a reported bug with MySQL Entity Framework 6.9.5
Bug #74918 : Incorrect query result with Entity Framework 6:
https://bugs.mysql.com/bug.php?id=74918
It has been fixed in MySQL Connector/Net 6.7.7 / 6.8.5 / 6.9.6 releases.
Changelog:
With Entity Framework 6, passing in a string reference to the "StartWith"
clause would return incorrect results.
Alternatively, a workaround is to use .Substring(0) which forces Entity not to use LIKE (might affect performance).
return (from a in db.Accounts
where a.accountname.Contains(searchText.Substring(0))

Custom Method in LINQ Query

I sum myself to the hapless lot that fumbles with custom methods in LINQ to EF queries. I've skimmed the web trying to detect a pattern to what makes a custom method LINQ-friendly, and while every source says that the method must be translatable into a T-SQL query, the applications seem very diverse. So, I'll post my code here and hopefully a generous SO denizen can tell me what I'm doing wrong and why.
The Code
public IEnumerable<WordIndexModel> GetWordIndex(int transid)
{
return (from trindex in context.transIndexes
let trueWord = IsWord(trindex)
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
}
public string IsWord(transIndex trindex)
{
Match m = Regex.Match(trindex.word, #"^[a-z]+(\w*[-]*)*",
RegexOptions.IgnoreCase);
return m.Value;
}
With the above code I access a table, transIndex that is essentially a word index of culled from various user documents. The problem is that not all entries are actually words. Nubers, and even underscore lines, such as, ___________,, are saved as well.
The Problem
I'd like to keep only the words that my custom method IsWord returns (at the present time I have not actually developed the parsing mechanism). But as the IsWord function shows it will return a string.
So, using let I introduce my custom method into the query and use it as a grouping parameter, the is selectable into my object. Upon execution I get the omninous:
LINQ to Entities does not recognize the method
'System.String IsWord(transIndex)' method, and this
method cannot be translated into a store expression."
I also need to make sure that only records that match the IsWord condition are returned.
Any ideas?
It is saying it does not understand your IsWord method in terms of how to translate it to SQL.
Frankly it does not do much anyway, why not replace it with
return (from trindex in context.transIndexes
let trueWord = trindex.word
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
What methods can EF translate into SQL, i can't give you a list, but it can never translate a straight forward method you have written. But their are some built in ones that it understands, like MyArray.Contains(x) for example, it can turn this into something like
...
WHERE Field IN (ArrItem1,ArrItem2,ArrItem3)
If you want to write a linq compatible method then you need to create an expresion tree that EF can understand and turn into SQL.
This is where things star to bend my mind a little but this article may help http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx.
If the percentage of bad records in return is not large, you could consider enumerate the result set first, and then apply the processing / filtering?
var query = (from trindex in context.transIndexes
...
select new WordIndexModel
{
Word,
Instances = grouped.Select(test => test.trans).Distinct()
});
var result = query.ToList().Where(word => IsTrueWord(word));
return result;
If the number of records is too high to enumerate, consider doing the check in a view or stored procedure. That will help with speed and keep the code clean.
But of course, using stored procedures has disadvatages of reusability and maintainbility (because of no refactoring tools).
Also, check out another answer which seems to be similar to this one: https://stackoverflow.com/a/10485624/3481183

Using variables to build a LinQ query?

I don't think is possible but wanted to ask to make sure. I am currently debugging some software someone else wrote and its a bit unfinished.
One part of the software is a search function which searches by different fields in the database and the person who wrote the software wrote a great big case statement with 21 cases in it 1 for each field the user may want to search by.
Is it possible to reduce this down using a case statement within the Linq or a variable I can set with a case statement before the Linq statement?
Example of 1 of the Linq queries: (Only the Where is changing in each query)
var list = (from data in dc.MemberDetails
where data.JoinDate.ToString() == searchField
select new
{
data.MemberID,
data.FirstName,
data.Surname,
data.Street,
data.City,
data.County,
data.Postcode,
data.MembershipCategory,
data.Paid,
data.ToPay
}
).ToList();
Update / Edit:
This is what comes before the case statement:
string searchField = txt1stSearchTerm.Text;
string searchColumn = cmbFirstColumn.Text;
switch (cmbFirstColumn.SelectedIndex + 1)
{
The cases are then done by the index of the combo box which holds the list of field names.
Given that where takes a predicate, you can pass any method or function which takes MemberDetail as a parameter and returns a boolean, then migrate the switch statement inside.
private bool IsMatch(MemberDetail detail)
{
// The comparison goes here.
}
var list = (from data in dc.MemberDetails
where data => this.IsMatch(data)
select new
{
data.MemberID,
data.FirstName,
data.Surname,
data.Street,
data.City,
data.County,
data.Postcode,
data.MembershipCategory,
data.Paid,
data.ToPay
}
).ToList();
Note that:
You may look for a more object-oriented way to do the comparison, rather than using a huge switch block.
An anonymous type with ten properties that you use in your select is kinda weird. Can't you return an instance of MemberDetail? Or an instance of its base class?
How are the different where statements handled, are they mutually excluside or do they all limit the query somehow?
Here is how you can have one or more filters for a same query and materialized after all filters have been applied.
var query = (from data in dc.MemberDetails
select ....);
if (!String.IsNullOrEmpty(searchField))
query = query.Where(pr => pr.JoinDate.ToString() == searchField);
if (!String.IsNullOrEmpty(otherField))
query = query.Where(....);
return query.ToList();

First SQL to Linq Conversion

I am new to LINQ, can somebody guide me that how can I convert the following SQL Query in Linq.
Select tblIns.InsID, tblProg.ProgName
from tblIns, tblProg
Where tblIns.InsID = tblProg.InsID
I am working on MVC2 Project, I have dataContext and Reposetories , please find below the code where I need this query:
public IQueryable<tblInstitute> InsRepeater()
{
return from Inst in _db.tblInstitutes
from Progs in _db.tblPrograms
Where Inst.InstituteID = Progs.InstituteID
Select Inst.InstituteID, Progs.ProgramName
}
The first thing you need is a data context which emulates your database. Here is an example of how to do this with linq to sql. You can also do it with entity framework (EF) or any other provider.
Once you have the tables created, the query then translates pretty straight forward:
var results = from insEntity in tablIns
from progEntity in tablProg
where insEntity.InsID equals progEntity.InsID
select new { insEntity.InsID, progEntity.ProgName };
With the question you have asked, this is as much as I think will be useful. In the future it's best to write questions explaining what you are trying to do, what you have tried, and then where you are stuck. The question should be specific enough to get you just over the next hump.
Per your edit: The query you have needs to have lowercase where and select and it needs to end the statement with a semi-colon (assuming it is c#). Then you select statement needs to select a new object. The results would look something like this:
public IQueryable<tblInstitute> InsRepeater()
{
return from Inst in _db.tblInstitutes
from Progs in _db.tblPrograms
where Inst.InstituteID equals Progs.InstituteID
select Inst; // for the current method header
//select new { Inst.InstituteID, Progs.ProgramName }; // to use this one you'll have to create a new type with the properties you want to return
}
First you need to
1. Create entity classes.
2. The data context.
3. Define relationships
4. Query
Reference
from I in tblIns
from P in tblProg
Where I.InsID = P.InsID
Select I.InsID, P.ProgName
Assuming you have your foreign keys set up properly in the database you can just do this, there is no need to to the joins yourself:
from x in db.tblProgs
select x.tblIns.id, x.Progname

LINQ-SQL Updating Multiple Rows in a single transaction

I need help re-factoring this legacy LINQ-SQL code which is generating around 100 update statements.
I'll keep playing around with the best solution, but would appreciate some ideas/past experience with this issue.
Here's my code:
List<Foo> foos;
int userId = 123;
using (DataClassesDataContext db = new FooDatabase())
{
foos = (from f in db.FooBars
where f.UserId = userId
select f).ToList();
foreach (FooBar fooBar in foos)
{
fooBar.IsFoo = false;
}
db.SubmitChanges()
}
Essentially i want to update the IsFoo field to false for all records that have a particular UserId value.
Whats happening is the .ToList() is firing off a query to get all the FooBars for a particular user, then for each Foo object, its executing an UPDATE statement updating the IsFoo property.
Can the above code be re-factored to one single UPDATE statement?
Ideally, the only SQL i want fired is the below:
UPDATE FooBars
SET IsFoo = FALSE
WHERE UserId = 123
EDIT
Ok so looks like it cant be done without using db.ExecuteCommand.
Grr...!
What i'll probably end up doing is creating another extension method for the DLINQ namespace. Still require some hardcoding (ie writing "WHERE" and "UPDATE"), but at least it hides most of the implementation details away from the actual LINQ query syntax.
Check DataClassesDataContext.ExecuteCommand...

Categories