Классический пример
использования byte типов данных в
Entity Model (SQL Type - tinyint). Классическая ошибка,
которую мы допустили в проекте, заключалась в следующем коде:
const byte inProgress = (byte)SessionStatus.InProgress;
using (var context = new SystemDatabaseContext())
return context.Entity.Session
.Where(s => s.Status ==
inProgress).ToList()
.Select(s => new Tuple<long, long>(s.Id, s.MessageId));
Основная ошибка этого
кода в том, что код на выходе будет такой:
SELECT [Extent1].[Id] AS [Id],
[Extent1].[MessageId] AS [MessageId],
[Extent1].[SystemId] AS [SystemId],
[Extent1].[Status] AS [Status],
[Extent1].[CreateDate] AS [CreateDate],
[Extent1].[ChangeDate] AS [ChangeDate],
[Extent1].[TransferredSize] AS [TransferredSize],
[Extent1].[ErrorCode] AS [ErrorCode],
[Extent1].[ErrorText] AS [ErrorText]
FROM
[dbo].[Session] AS [Extent1]
WHERE 0 = CAST([Extent1].[Status]
AS int)
Имеем лишнее
преобразование byte в int. Решается эта проблема следующим образом:
const byte
inProgress = (byte) SessionStatus.InProgress;
var
tinyintComparison = new List<byte> { inProgress };
using (var context
= new SystemDatabaseContext())
{
return
context.Entity.Session
.Where(s => tinyintComparison.Contains(s.Status)).ToList()
.Select(s => new Tuple<long, long>(s.Id, s.MessageId));
}
На выходе имеем такой
результат:
SELECT [Extent1].[Id] AS [Id],
[Extent1].[MessageId] AS [MessageId],
[Extent1].[SystemId] AS [SystemId],
[Extent1].[Status] AS [Status],
[Extent1].[CreateDate] AS [CreateDate],
[Extent1].[ChangeDate] AS [ChangeDate],
[Extent1].[TransferredSize] AS [TransferredSize],
[Extent1].[ErrorCode] AS [ErrorCode],
[Extent1].[ErrorText] AS [ErrorText]
FROM
[dbo].[Session] AS [Extent1]
WHERE 0
= [Extent1].[Status]
Данный код работает в
несколько раз быстрее, чем предыдущая версия. Очень ощутимые результаты, когда
данных много и поле индексированное.
Индексация получается в первом примере не участвует . Во втором можно
увидеть преимущество выборки, когда используем индексацию. Не используется
лишнее преобразование типов, и выборка данных происходит в 10 раз быстрее. http://blog.hompus.nl/2013/01/21/filtering-on-a-tinyint-with-entity-framework/
Вторая ошибка была скрыта в приведенном коде:
var
selectData = (from organization in Entity.Organization
join
executive in Entity.SystemExecutive
on
organization.Id equals executive.OrgId
where !executive.Disabled
select new {
organization.Id, organization.Edrpou })
.ToList();.
По времени выполнение
данного запроса занимает 4ms. Из-за того что запрос приводится к такому виду:
SELECT [Extent1].[Id] AS [Id],
[Extent1].[Edrpou] AS [Edrpou]
FROM
[dbo].[Organization] AS [Extent1]
INNER JOIN [dbo].[SystemExecutive] AS [Extent2]
ON [Extent1].[Id] = [Extent2].[OrgId]
WHERE
[Extent2].[Disabled] <> cast(1 as bit)
Заменяем знак != на == и вот что имеем:
var
selectData = (from organization in Entity.Organization
join
executive in Entity.SystemExecutive
on
organization.Id equals executive.OrgId
where
executive.Disabled == false
select new {
organization.Id, organization.Edrpou })
.ToList();
Результат выполнения 2ms. Данные не совсем корректны, потому используемая нами таблица была небольшой. На больших таблицах прирост
производительности будет существеннее. Ниже представлен результат
преобразования приведенного выше кода в SQL.
SELECT [Extent1].[Id] AS [Id],
[Extent1].[Edrpou] AS [Edrpou]
FROM
[dbo].[Organization] AS [Extent1]
INNER JOIN [dbo].[SystemExecutive] AS [Extent2]
ON [Extent1].[Id] = [Extent2].[OrgId]
WHERE 0
= [Extent2].[Disabled]
Следующая проблема была в
использовании N+1 антипаттерна.
Select N+1
Select N + 1 is a data
access anti-pattern where the database is accessed in a suboptimal way. Take a
look at this code sample, then we'll discuss what is going on. Say you want to
show the user all comments from all posts so that they can delete all of the
nasty comments. The naive implementation would be something like:
var posts = Dao.GetRecentPosts();
foreach (var post in posts)
{
post.Comments = Dao.GetCommentsForPost(post);
}
public IEnumerable<Post> GetRecentPosts()
{
using(var ctx= new MyBlogContext())
{
return (from post in ctx.Posts
orderby post.PublishedDate descending
select post)
.Take(50)
.ToList();
}
}
public IEnumerable<Comment> GetCommentsForPost(Post post)
{
using(var ctx= new MyBlogContext())
{
return (from comment in ctx.Comments
where comment.Post.Id = post.Id
select comment)
.ToList();
}
}
Пример решения может
выглядеть следующим образом:
//
SELECT * FROM Posts JOIN Comments ...
var postsQuery = (from post in blogDataContext.Posts.Include("Comments")
select post);
foreach (Post post in postsQuery)
{
// no
lazy loading of comments list causes
foreach (Comment comment in post.Comments)
{
//print
comment...
}
}
Мы указываем в запросе, какие
части объектной модели мы хотим включить в первоначальный запрос. Более
подробно об решении этой проблемы можно посмотреть здесь: http://www.hibernatingrhinos.com/products/efprof/learn/alert/SelectNPlusOne
No comments:
Post a Comment