views:

60

answers:

3

i have this in my query:

var results = (from urls in _context.Urls
               join documents in _context.Documents on urls.UrlId equals documents.DocumentId
               let words = (from words in _context.Words
                            join hits in _context.Hits on words.WordId equals hits.WordId
                            where hits.DocumentId == documents.DocumentId
                            select words.Text).AsEnumerable<string>()

                where urls.ResolvedPath.Contains(breakedQuery, KeywordParts.Url, part) ||
                      documents.Title.Contains(breakedQuery, KeywordParts.Title, part) ||
                      documents.Keywords.Contains(breakedQuery, KeywordParts.Keywords, part) ||
                      documents.Description.Contains(breakedQuery, KeywordParts.Description, part) ||
                      words.Contains(breakedQuery, KeywordParts.Content, part) ...

and Contains extension method:

for strings

public static bool Contains(this string source, IEnumerable<string> values, KeywordParts valuePart, KeywordParts part)
    {
        if (!string.IsNullOrWhiteSpace(source))
            return source.Split(' ').AsEnumerable<string>().Contains(values, valuePart, part);
        return false;
    }

for enumerables (main method)

public static bool Contains(this IEnumerable<string> source, IEnumerable<string> values, KeywordParts valuePart, KeywordParts part)
    {
        if (source != null && source.Count() > 0 &&
            values != null && values.Count() > 0 &&
            (part == KeywordParts.Anywhere || valuePart == part))
        {
            foreach (var value in values)
            {
                var has = false;
                var none = (value.StartsWith("-"));
                string term = value.Replace("-", "");

                if (none)
                    has = source.Any(q => !q.Contains(value));
                else
                    has = source.Any(q => q.Contains(values));

                if (has)
                    return has;
            }
        }
        return false;
    }

and using Contains method throws exception NotSupportedException: Method 'Boolean Contains(String, IEnumerable`1[String], KeywordParts, KeywordParts)' has no supported translation to SQL.

actually i want to check each indexed document if have at lease one of specified conditions

+2  A: 

You can't just write your own methods and call them from your query expression - the query translator has no idea what that method's meant to do.

You could force the where clause to be executed in .NET after fetching the documents and words, potentially... although obviously that means fetching all the joined data from the database. Would that be okay?

To do that, you'd want something like:

var tmpQuery = (from urls in _context.Urls
                join documents in _context.Documents 
                on urls.UrlId equals documents.DocumentId
                let words = (from words in _context.Words
                             join hits in _context.Hits 
                             on words.WordId equals hits.WordId
                             where hits.DocumentId == documents.DocumentId
                             select words.Text)
                select new { urls, documents, words };

var query = from r in tmpQuery.AsEnumerable()
            let urls = r.urls.ToList()
            let words = r.words.ToList()
            let documents = r.documents.ToList()
            where urls.ResolvedPath.Contains(breakedQuery, 
                                             KeywordParts.Url, part) ||
               documents.Title.Contains(breakedQuery,
                                        KeywordParts.Title, part) ||
               documents.Keywords.Contains(breakedQuery,
                                           KeywordParts.Keywords, part) || 
               documents.Description.Contains(breakedQuery, 
                                              KeywordParts.Description, part) ||
               words.Contains(breakedQuery, KeywordParts.Content, part)
            select new { urls, words, documents };
Jon Skeet
very good, thank you. can you give me an approach to improve speed of executing this query. as you know i'm developing very basic web search engine and this is a part of search section of project. after i'm retrieved results i want to sort result by rate of each which calculated in select. i'll post rate section in seperate question.
Sadegh
here are my question http://stackoverflow.com/questions/3274897/simple-rating-algohritm-to-sorting-results-according-to-user-query
Sadegh
+2  A: 

My understanding and someone please correct me if I am wrong, the problem is that when using an extension method with Linq to SQL the extension method is not executed as .NET code like the extension methods you have in your question.

The Linq to SQL extension methods return expression trees, which the Linq to SQL engine then parses and generates the appropriate SQL query to satisfy the expression tree.

Chris Taylor
Yes, exactly. In this case the expression tree will refer to calls to `Contains`, which LINQ to SQL doesn't know about.
Jon Skeet
@Jon, thanks for the confirmation.
Chris Taylor
+1  A: 

Another way of implementing this is to write a scalar UDF in the database that implements this functionality. Then drag that UDF onto the LINQ-to-SQL designer, which will give you access to your UDF via the data-context. Then you can use things like:

where _context.MyContains(documents.Title, breakedQuery,
       KeywordParts.Title, part);

and which will call the UDF after translation (i.e. WHERE dbo.MyContains(...))

Marc Gravell