Как получить лучшие псевдонимы таблиц для операторов SQL, созданных Entity Framework?

Можно ли контролировать, какие имена псевдонимов таблиц Entity Framework использует для создаваемых инструкций SQL? Например, вместо

SELECT [Extent1].[Id] AS [Id]
FROM [dbo].[Orders] AS [Extend1]
INNER JOIN [dbo].[Customers] AS [Extend2] ON [Extend1].[CustomerId] = [Extend2].[Id]
WHERE [Extent2].[Name] = 'xyz'

я хотел бы получить

SELECT [order].[Id] AS [Id]
FROM [dbo].[Orders] AS [order]
INNER JOIN [dbo].[Customers] AS [customer] ON [order].[CustomerId] = [customer].[Id]
WHERE [customer].[Name] = 'xyz'

или что-то подобное, если мне не нужно помнить, какой номер расширения является какой таблицей. Конечно, в этом простом примере это не проблема, но в запросах, где у вас есть от [Extend1] до [Extend7], все становится сложнее.


person cremor    schedule 30.07.2014    source источник
comment
Если вы тратите много времени на просмотр запросов, сгенерированных машиной, значит, что-то очень не так, и применяются не имена.   -  person Damien_The_Unbeliever    schedule 30.07.2014
comment
@Damien_The_Unbeliever Я не провожу с ними много времени, но во время разработки мне нужно смотреть на них, чтобы знать, какие индексы мне нужно создать для своих таблиц. И иногда небольшие изменения в запросах Linq приводят к гораздо лучшим SQL-запросам, но чтобы знать, где оптимизировать, я должен видеть (и понимать) созданные запросы.   -  person cremor    schedule 30.07.2014
comment
+1 Мы профилируем все основные запросы, включая проверку сгенерированного sql. Было бы очень полезно иметь лучшие псевдонимы   -  person Matt Caton    schedule 30.07.2014


Ответы (1)


Вы можете использовать IDbCommandInterceptor и переопределить CommandText.

Я только что создал очень простую логику для замены псевдонима с помощью регулярного выражения.

Следующий код

var query = (from f1 in db.Foos
            where f1.ParentFooId == null
            join f2 in db.Foos on f1.FooId equals f2.ParentFooId
            select f2).ToArray();

по умолчанию станет чем-то вроде

SELECT 
    [Extent1].[FooId] AS [FooId], 
    [Extent2].[FooId] AS [FooId1], 
    [Extent2].[FooName] AS [FooName], 
    [Extent2].[ParentFooId] AS [ParentFooId], 
    [Extent2].[ParentFoo_FooId] AS [ParentFoo_FooId]
    FROM  [dbo].[Foos] AS [Extent1]
    INNER JOIN [dbo].[Foos] AS [Extent2] ON [Extent1].[FooId] = [Extent2].[ParentFooId]
    WHERE [Extent1].[ParentFooId] IS NULL
go

а потом я перехватил текст команды на ReaderExcecuting.

public void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
{
    var aliasLookup = new Dictionary<string, string>();
    var tableLookup = new Dictionary<string, int>();

    var text = command.CommandText;
    // Looking something like "[dbo].[Foos] AS [Extent1]"
    var pattern = @"\[dbo\]\.\[([\w\d]*)\] AS \[(Extent\d*)\]";
    var matches = Regex.Matches(text, pattern);
    foreach(Match m in matches)
    {
        if (m.Groups.Count == 3)
        {
            var tableName = m.Groups[1].Value;
            var aliasName = m.Groups[2].Value;

            var replacement = tableName;
            if (tableLookup.ContainsKey(tableName)) replacement += tableLookup[tableName]++;
            else tableLookup.Add(tableName, 2);

            aliasLookup.Add(aliasName, replacement);
        }
    });

    foreach(var pair in aliasLookup)
    {
        var oldAliasName = string.Format("[{0}]", pair.Key);
        var newAliasName = string.Format("[{0}]", pair.Value);
        command.CommandText = command.CommandText.Replace(oldAliasName, newAliasName);
    }
}

Результат будет выглядеть так

SELECT 
    [Foos].[FooId] AS [FooId], 
    [Foos2].[FooId] AS [FooId1], 
    [Foos2].[FooName] AS [FooName], 
    [Foos2].[ParentFooId] AS [ParentFooId], 
    [Foos2].[ParentFoo_FooId] AS [ParentFoo_FooId]
    FROM  [dbo].[Foos] AS [Foos]
    INNER JOIN [dbo].[Foos] AS [Foos2] ON [Foos].[FooId] = [Foos2].[ParentFooId]
    WHERE [Foos].[ParentFooId] IS NULL
go

Это просто дает вам представление о том, как это сделать, логика не тестировалась для сложных запросов, и ее следует использовать только для отладки.

person Yuliam Chandra    schedule 03.08.2014
comment
Хорошо, спасибо! Но есть ли причина, по которой вы используете regex.Replace, фактически ничего не заменяя, вместо того, чтобы перебирать regex.Matches? - person cremor; 04.08.2014
comment
@cremor, я знаю только, как использовать regex.Replace, если он работает с regex.Matches, не стесняйтесь редактировать мой ответ .. - person Yuliam Chandra; 04.08.2014
comment
как реализовать и переопределить этот метод структуры сущности? - person Jeferson Tenorio; 06.10.2016