0

I'm currently working, with my team, on a project where we have a large SQL Server 2014 database with a lot of data. To increase performances, we chose to use FullTextIndex with EntityFramework by following this tutorial. We now have a problem for the table Customer because of inheritance (we think so, it may be something else). Here is the model (simplified) :

public abstract class Person // In db : dbo.People
{
    public int Id { get; set }
    public string LastName { get; set; }
    // Other properties
}

public class Customer : Person // In db : dbo.People_Customer
{
    // Inherited properties from Person
}

public class Mission // In db : dbo.Missions
{
    public int CustomerIdPerson { get; set; }
    public virtual Customer Customer { get; set; }
}

When I try to search missions through customer via the following query :

context.Missions.Where(m => m.Customer.LastName.Contains("foo")).ToList();

I have this error (raised by SQL Server) : Cannot use a CONTAINS or FREETEXT predicate on column 'LastName' because it is not full-text indexed.

Here is the SQL generated by EF (I just replaced the variable by the actual value of the SQL parameter).

SELECT 
[Limit3].[IdJourney] AS [IdJourney]
FROM ( SELECT TOP (2000) 
    [Project4].[IdJourney] AS [IdJourney]
    FROM    (SELECT 
        [Project2].[IdJourney] AS [IdJourney], 
        [Project2].[SuppressionDate] AS [SuppressionDate], 
        [Project2].[State] AS [State], 
        [Project2].[TripIdTrip] AS [TripIdTrip], 
        [Project2].[IdTrip] AS [IdTrip], 
        [Project2].[SuppressionDate1] AS [SuppressionDate1], 
        [Project2].[TripSetIdTripSet] AS [TripSetIdTripSet], 
        [Project2].[C1] AS [C1], 
        (SELECT TOP (1) 
            [Extent6].[PlannedDate] AS [PlannedDate]
            FROM  [dbo].[PlannedElements_PlannedBusinessFleetElement] AS [Extent5]
            INNER JOIN [dbo].[PlannedElements] AS [Extent6] ON [Extent5].[IdPlannedElement] = [Extent6].[IdPlannedElement]
            WHERE (0 = [Extent5].[TypeOfBusinessFleetElement]) AND ([Project2].[IdJourney] = [Extent5].[JourneyIdJourney])) AS [C2]
        FROM ( SELECT 
            [Extent1].[IdJourney] AS [IdJourney], 
            [Extent1].[SuppressionDate] AS [SuppressionDate], 
            [Extent1].[State] AS [State], 
            [Extent1].[TripIdTrip] AS [TripIdTrip], 
            [Extent2].[IdTrip] AS [IdTrip], 
            [Extent2].[SuppressionDate] AS [SuppressionDate1], 
            [Extent2].[TripSetIdTripSet] AS [TripSetIdTripSet], 
            (SELECT TOP (1) 
                [Extent4].[PlannedDate] AS [PlannedDate]
                FROM  [dbo].[PlannedElements_PlannedBusinessFleetElement] AS [Extent3]
                INNER JOIN [dbo].[PlannedElements] AS [Extent4] ON [Extent3].[IdPlannedElement] = [Extent4].[IdPlannedElement]
                WHERE (0 = [Extent3].[TypeOfBusinessFleetElement]) AND ([Extent1].[IdJourney] = [Extent3].[JourneyIdJourney])) AS [C1]
            FROM  [dbo].[Journeys] AS [Extent1]
            INNER JOIN [dbo].[Trips] AS [Extent2] ON [Extent1].[TripIdTrip] = [Extent2].[IdTrip]
        )  AS [Project2] ) AS [Project4]
    LEFT OUTER JOIN [dbo].[TripSets] AS [Extent7] ON [Project4].[TripSetIdTripSet] = [Extent7].[IdTripSet]
    LEFT OUTER JOIN  (SELECT [Extent8].[IdPerson] AS [IdPerson1], [Extent8].[LastName] AS [LastName]
        FROM  [dbo].[Persons] AS [Extent8]
        INNER JOIN [dbo].[Persons_Customer] AS [Extent9] ON [Extent8].[IdPerson] = [Extent9].[IdPerson] ) AS [Join5] ON [Extent7].[CustomerIdPerson] = [Join5].[IdPerson1]
    WHERE (CONTAINS([Join5].[LastName], '"foo*"'))
)  AS [Limit3]

I'm sure the column is full-text indexed. I've rebuild multiple times the calalog and the index. For other tables that are not inherited, the FullTextSearch works fine...

I'm out of ideas... Thanks for your help. :)

Atlasmaybe
  • 1,511
  • 11
  • 26
  • I immagine that the error "Cannot use a CONTAINS or FREETEXT predicate on column 'LastName' because it is not full-text indexed." is raised from SQL Server. At first instance you could check what exactly is the query you are trying to run (using SQL profiler or stopping execution in RewriteFullTextQuery) and try to run it directly in SQL Server. I'm also sure that you are using the right connection and the right schema (dbo) but check it :) – bubi May 26 '15 at 13:51
  • @bubi, thanks for your help. I checked the SQL query generated by EF and I saw that EF is trying to do a `CONTAINS` on a column through an alias, like this : `CONTAINS([myAlias].[LastName], '"foo*"')`. Without the contains, it works fine but otherwise, it crashes. I think it's due to the sql generated by EF but I've no clue how to resolve it without rewriting all my queries... – Atlasmaybe May 26 '15 at 14:08
  • Actually CONTAINS support aliases... Queries like SELECT * FROM MyTable myalias where CONTAINS(myalias.MyField, 'meee*') works fine. Could you send the Whole query that EF sends to SQL Server? – bubi May 26 '15 at 15:04
  • @bubi I just updated my question, please go see it for the SQL query. Thanks for your help :) – Atlasmaybe May 27 '15 at 07:37
  • I answer down to add a little bit of code – bubi May 28 '15 at 08:47

1 Answers1

1

Probably the problem is related to the fact that CONTAINS access to a Join and not to a table so SQL Server does not like it. I usually avoid to inherit on classes written on the DB (scared about EF behaviour :) ).

Anyway you could avoid to add the [myAlias] in the piece of
CONTAINS([myAlias].[LastName], '"foo*"')
query. Probably sometimes you need to insert [myAlias] so you could change the code in the FtsInterceptor class. You could add 1 more "tag"
private const string FullTextPrefixWithoutAlias = "-FTSPREFIXNOALIAS-";
and then call a different RewriteFullTextQuery if you specify the FTSPREFIXNOALIAS (or better add a parameter to RewriteFullTextQuery to specify the behaviour). The only difference in RewriteFullTextQuery is that in the case you don't want the alias you need to use
string.Format(@"contains([$2], @{0})",parameter.ParameterName));
instead of
string.Format(@"contains([$1].[$2], @{0})",parameter.ParameterName));

In the same way you can also add more control to the FtsInterceptor adding more "tags"...

bubi
  • 6,414
  • 3
  • 28
  • 45
  • You put me on the right way and now it's working. :) – Atlasmaybe May 28 '15 at 12:27
  • In fact not that much, I spoke too fast. It's almost working. With the query I posted above, I still need to tell to SQL Server where to check the column (and I can't specify one tag for every table that I've)... – Atlasmaybe May 28 '15 at 12:34
  • There are some consideration about the solution. You are using an IDbCommandInterceptor so is quite hard to understand on wich table you are working (you should parse the query) and understand (actually I don't know how) if you need to insert the alias or not. If you determine the table how can you determine if use the alias or not? Probably it depends on the query... So probably the best way is to avoid to insert the alias always but it works only if you have the column name you are working on only in one table. (continue) – bubi Jun 02 '15 at 10:11
  • can you please share the working code @Atlasmaybe? I am having the same issue. – akd Jan 25 '17 at 17:43
  • Do you mean the code of contains? If you need it you can find it here http://www.entityframework.info/Home/FullTextSearch – bubi Jan 26 '17 at 07:11
  • No I mean I have followed the link you sent. But I get the same error as @Atlasmaybe. he says that "you put him on the right direction and it is working now". I wonder what is that fix? – akd Jan 26 '17 at 10:05
  • I am having problem when I have multiple fts search fields and trying to add multiple interceptions for 2 columns – akd Jan 26 '17 at 10:20
  • I'm sorry but I just sent him the suggestions you can see. Atlasmaybe then made the fixes. You could probably ask to him. – bubi Jan 26 '17 at 15:06
  • Hello @akd, I'm sorry but I can't help you. I'm no longer in the same company as before, so I don't have access to the code. But, in my memories, we finally dropped this idea and we chose to write our own SQL Stored Procedure, which is easier than playing with entity framework... – Atlasmaybe Jan 26 '17 at 19:25
  • Thanks for coming back to me @Atlasmaybe. I think going towards stored procedure is right thing to do. But the complexity is that handling all null checks and implementing sorting through stored procedures is also complex when there are over 10 filters. Thank you – akd Jan 27 '17 at 09:17