SlideShare uma empresa Scribd logo
1 de 48
Артур Дробинский
МЦЦ Томск
Entity Framework Core: tips
and tricks
About Us
MCC Tomsk
Cloud platform
for tele-medicine
Microservices
Message Broker
SOA
.Net Core
React/Redux
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command = new
SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
reader.Read();
Console.WriteLine("t{0}t{1}t{2}",
reader[0], reader[1], reader[2]);
}
public class Comment
{
public int Id { get; set; }
public string Text { get; set; }
public bool IsDeleted { get; set; }
public Post Post { get; set; }
}
var context = new MyContext(options);
var comment = context.Comments.First();
Console.WriteLine("t{0}t{1}", comment.Text,
comment.Id);
var context = new MyContext(options);
var comment = context.Comments
.Include(x => x.Post)
.Where(x => x.Id > 5)
.Where(x => !x.Post.IsDeleted)
.OrderBy(x => x.Text)
.First();
Console.WriteLine("t{0}t{1}",
comment.Post.Title, comment.Text);
var sql =
@"select * from #Posts p
left join #Users u on u.Id = p.OwnerId
Order by p.Id";
var data = connection.Query<Post, User,
Post>(sql, (post, user) => { post.Owner =
user; return post; });
var post = data.First();
Pitfalls
Entities
public class Post
{
public int Id { get; private set; }
public string Title { get; set; }
public Blog Blog { get; set; }
public bool IsDeleted { get; set; }
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
public class Blog
{
public int Id { get; private set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; }
private Blog() { }
public Blog(string name)
{
Name = name;
}
}
public class Comment
{
public int Id { get; set; }
public string Text { get; set; }
public Post Post { get; set; }
}
SELECT N+1
var posts = context.Posts.ToList();
//SELECT * FROM Posts
foreach (var post in posts)
{
var commentsCount = context.Comments
.Count(x => !x.IsDeleted && x.Post.Id == post.Id);
//SELECT COUNT(1) FROM Comments WHERE IsDeleted = 0 AND PostId = {0}
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
SELECT N+1
posts = context.Posts.Include(x => x.Comments).ToList();
//SELECT * FROM Posts WHERE IsDeleted = 0
//SELECT * FROM Comments
//INNER JOIN (SELECT Id FROM Posts WHERE IsDeleted = 0) AS T ON PostId = T.Id
foreach (var post in posts)
{
var commentsCount = post.Comments.Count();
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
SELECT N+1
var posts3 = context.Posts.Select(x => new
{
PostTitle = x.Title,
PostId = x.Id,
CommentsCount = x.Comments.Count(),
}).ToList();
//SELECT Title, Id, (SELECT COUNT(*)
//FROM Comments AS c WHERE [x].[Id] = [c].[PostId]) AS CommentsCount
//FROM Posts AS x
foreach (var post in posts3)
{
var commentsCount = post.CommentsCount;
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
SELECT N+1
var posts4 = context.Posts.Select(x => new
{
Post = x,
CommentsCount = x.Comments.Count(),
}).ToList();
//SELECT * FROM Posts
//SELECT Count(*) FROM Comments WHERE PostId = {0}
foreach (var post in posts3)
{
var commentsCount = post.CommentsCount;
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
SELECT N+1
var posts5 = context.Posts.Select(x => new
{
PostId = x.Id,
Blog = x.Blog,
CommentsCount = x.Comments.Count(),
}).ToList();
//SELECT [Blogs].*, (SELECT COUNT(*) FROM[Comments] AS[c]
//WHERE[x].[Id] = [c].[PostId] ) AS[CommentsCount]
//FROM[Posts] AS[x]
//LEFT JOIN[Blogs] AS[x.Blog] ON[x].[BlogId] = [x.Blog].[Id]
foreach (var post in posts3)
{
var commentsCount = post.CommentsCount;
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Change Tracker
context = _createDatabase();
var blog = context.Blogs.FirstOrDefault();
Assert.Null(blog.Posts);
context.Posts.Where(x => x.BlogId == blog.Id).ToList();
Assert.Null(blog.Posts);
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
ToList()
var blog = context.Blogs.Include(x => x.TimeRegion).ToList();
//SELECT * FROM Blogs
//LEFT JOIN TimeRegion ON TimeRegion.Id = Blogs.TimeRegionId
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
context.TimeRegions.ToList();
var blog = context.Blogs.ToList();
//SELECT * FROM TimeRegions
//SELECT * FROM Blogs
GroupBy
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
var result = context.Posts
.GroupBy(p => p.BlogId)
.Select(g => new { BlogId = g.Key, PostCount = g.Count() })
.ToList();
//SELECT[p].[BlogId] AS[BID], COUNT(*) AS[cnt]
//FROM[Posts] AS[p]
//GROUP BY[p].[BlogId]
GroupBy
var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
.ConfigureWarnings(x => x.Throw(RelationalEventId.QueryClientEvaluationWarning));
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
var result = context.Posts
.GroupBy(p => p.BlogId)
.Select(g => new { BID = g.Key, cnt = g.Count(x => !x.IsDeleted) })
.ToList();
var result = context.Posts
.GroupBy(p => p.Blog)
.Select(g => new { Url = g.Key.Name, Count = g.Count() })
.ToList();
Aggregates
var data = context.Blogs.Select(x => new
{
x.Id,
MaxPostId = x.Posts.Max(post => post.Id)
}).ToList();
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
InvalidOperationException: Sequence contains no elements.
Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.GetResult<TResult>(IEnumerable
<ValueBuffer> valueBuffers, bool throwOnNullResult)
Min/Max/Average
var data = context.Blogs.Select(x => new
{
x.Id,
MaxPostId = x.Posts.Max(post => (int?)post.Id)
}).ToList();
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Tired? 
Private constructors & Collection initializers
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
public class Blog
{
public int Id { get; private set; }
public string Name { get; set; }
private Blog() { }
public Blog(string name)
{
Name = name;
}
}
Private constructors & Collection initializers
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
public class Blog
{
public int Id { get; private set; }
public ICollection<Post> Posts { get; }
= new List<Post>(); //don't do that
}
var blog = context.Blogs.FirstOrDefault();
var postCount = blog.Posts.Count();
Lazy Loading
• Since 2.1
• No private constructors (but could be protected)
• Not recommended
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
.UseLazyLoadingProxies();
var blog = context.Blogs.FirstOrDefault();
//SELECT * FROM Blogs LIMIT 1
var postCount = blog.Posts.Count();
// SELECT * FROM Posts WHERE BlogId = 1
Target Multiple Databases
public abstract class MainContext : DbContext
{
public DbSet<Comment> Comments { get; set; }
protected MainContext(DbContextOptions<MainContext> options) : base(options) { }
}
public class PostgresContext : MainContext
{
public PostgresContext(DbContextOptions<MainContext> options) : base(options) { }
}
public class SqlServerContext : MainContext
{
public SqlServerContext(DbContextOptions<MainContext> options) : base(options) { }
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Migrations.Designer.cs (could be deleted)
[Migration("20181124143113_Initial")]
partial class Initial
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview3-35497")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("XUnitTestProject1.Blog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Name");
b.HasKey("Id");
b.ToTable("Blogs");
});
modelBuilder.Entity("XUnitTestProject1.Comment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<bool>("IsDeleted");
b.Property<int?>("PostId");
b.Property<string>("Text");
b.HasKey("Id");
b.HasIndex("PostId");
b.ToTable("Comments");
});
Value Convertors
• EnumToStringConverter
• DateTimeToTicksConverter
• Objects as JSON serialized string
(not built-in)
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Value Convertors (JSON serialized-objects)
public class Blog {
public int Id { get; private set; }
…
public BlogSettings Settings { get; set; }
}
public class BlogSettings {
public int BackgroundColor { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Blog>().Property(e => e.Settings)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<BlogSettings>(v));
}
Query Filter (Soft Delete)
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
var blogs = context.Posts.ToList();
//SELECT * FROM Posts WHERE IsDeleted = 0
public class Post
{
public int Id { get; private set; }
public string Title { get; set; }
public Blog Blog { get; set; }
public bool IsDeleted { get; set; }
}
Testing
In Memory
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
_createDatabase = () => new MyContext(options);
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
In Memory & transactions
var options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.ConfigureWarnings(x =>
x.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
In Memory & constraints??? SQLite!
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open(); //an open connection is required for database to exist
var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
.ConfigureWarnings(x =>
x.Throw(RelationalEventId.QueryClientEvaluationWarning))
.UseLazyLoadingProxies()
;
var options = optionsBuilder.UseSqlite(connection).Options;
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Migrations
[Fact]
public void Migrations_RunAll_MigrationsSuccessfullyApplied()
{
using (var connection = new SqliteConnection("DataSource=:memory:"))
{
connection.Open(); //an open connection is required for database to exist
var optionsBuilder = new DbContextOptionsBuilder<MyContext>();
var options = optionsBuilder.UseSqlite(connection).Options;
var context = new MyContext(options);
context.Database.Migrate();
}
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Migrations
var context = new MyContext(options);
context.Database.Migrate();
foreach (var entityType in context.Model.GetEntityTypes())
{
GetDbSet(context, entityType.ClrType).First();
}
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
System.InvalidOperationException : Error querying entity 'Post': SQLite Error 1: 'no
such column: p.Date'.
Integration
Z.EntityFramework.Plus.EFCore – Bulk Operations
_dbContext.Comments.Where(x => !x.IsDeleted)
.Update(x => new Comment()
{
IsDeleted = true,
});
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
UPDATE A
SET A.[IsDeleted] = @zzz_BatchUpdate_0
FROM[Comments] AS A
INNER JOIN(SELECT[x].[Id], [x].[IsDeleted],
[x].[PostId], [x].[Text]
FROM [Comments] AS [x]
WHERE [x].[IsDeleted] = 0
) AS B ON A.[Id] = B.[Id]
BulkUpdate does not work with InMemoryDatabase
Z.EntityFramework.Plus.EFCore – FromCache
var timeRegions = _dbContext.TimeRegions.FromCache();
_dbContext.AttachRange(timeRegions);
var blogs = _dbContext.Blogs.ToList();
Console.WriteLine(string.Join(", ", blogs.Select(blog => $"{blog.Name}:
{blog.TimeRegion.Offset}")));
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Dapper (EFSqlTranslator)
var query = context.Posts
.Select(p => new
{
BlogId = p.Blog.Id,
Title = p.Title
})
.GroupBy(x => x.BlogId)
.Select(x => new { Id = x.Key, Cnt = x.Count() });
var queryResult = context.Query(query,
new EFModelInfoProvider(context),
new SqliteObjectFactory(),
out var sql);
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Override SaveChanges
• Watch changes (e.g. cache invalidation)
• Log changes (e.g. audit)
• Inform about changes (send to 3rd party)
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Override SaveChanges & SignalR
FullText Search
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Summary
• SELECT N+1
• Group By & Aggregate
• ChangeTracker
• SaveChanges() override
• Testing & Migrations
E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
Спасибо. Вопросы?
Артур Дробинский
artur.drobinskiy@mcc-tomsk.de
http://arturdr.ru
До встречи 31 января!
TomskDotNet #2!
Точка Кипения
31 января, 18:30

Mais conteúdo relacionado

Mais procurados

HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 

Mais procurados (20)

Custom YUI Widgets
Custom YUI WidgetsCustom YUI Widgets
Custom YUI Widgets
 
Why Learn Python?
Why Learn Python?Why Learn Python?
Why Learn Python?
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
Xm lparsers
Xm lparsersXm lparsers
Xm lparsers
 
TDC2016POA | Trilha .NET - CQRS e ES na prática com RavenDB
TDC2016POA | Trilha .NET - CQRS e ES na prática com RavenDBTDC2016POA | Trilha .NET - CQRS e ES na prática com RavenDB
TDC2016POA | Trilha .NET - CQRS e ES na prática com RavenDB
 
Intro
IntroIntro
Intro
 
ESNext for humans - LvivJS 16 August 2014
ESNext for humans - LvivJS 16 August 2014ESNext for humans - LvivJS 16 August 2014
ESNext for humans - LvivJS 16 August 2014
 
Python 표준 라이브러리
Python 표준 라이브러리Python 표준 라이브러리
Python 표준 라이브러리
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, future
 
H base programming
H base programmingH base programming
H base programming
 
Symfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technologySymfony2 Building on Alpha / Beta technology
Symfony2 Building on Alpha / Beta technology
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 
PHP 5.4
PHP 5.4PHP 5.4
PHP 5.4
 
Why Sifu
Why SifuWhy Sifu
Why Sifu
 
5. Ввод-вывод, доступ к файловой системе
5. Ввод-вывод, доступ к файловой системе5. Ввод-вывод, доступ к файловой системе
5. Ввод-вывод, доступ к файловой системе
 
Using Fuzzy Code Search to Link Code Fragments in Discussions to Source Code
Using Fuzzy Code Search to Link Code Fragments in Discussions to Source CodeUsing Fuzzy Code Search to Link Code Fragments in Discussions to Source Code
Using Fuzzy Code Search to Link Code Fragments in Discussions to Source Code
 
Sam wd programs
Sam wd programsSam wd programs
Sam wd programs
 
DEF CON 27 -OMER GULL - select code execution from using sq lite
DEF CON 27 -OMER GULL - select code execution from using sq liteDEF CON 27 -OMER GULL - select code execution from using sq lite
DEF CON 27 -OMER GULL - select code execution from using sq lite
 
The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 

Semelhante a Entity Framework Core: tips and tricks

Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
celenarouzie
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
Kiev ALT.NET
 
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
NETFest
 
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdfSummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
ARORACOCKERY2111
 
Web весна 2013 лекция 6
Web весна 2013 лекция 6Web весна 2013 лекция 6
Web весна 2013 лекция 6
Technopark
 
C# 6.0 - April 2014 preview
C# 6.0 - April 2014 previewC# 6.0 - April 2014 preview
C# 6.0 - April 2014 preview
Paulo Morgado
 
Embedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for JavaEmbedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for Java
Jevgeni Kabanov
 

Semelhante a Entity Framework Core: tips and tricks (20)

greenDAO
greenDAOgreenDAO
greenDAO
 
Building a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to SpaceBuilding a friendly .NET SDK to connect to Space
Building a friendly .NET SDK to connect to Space
 
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docxWeb CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
Web CrawlersrcedusmulylecrawlerController.javaWeb Crawler.docx
 
C# labprograms
C# labprogramsC# labprograms
C# labprograms
 
Micro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicateMicro-ORM Introduction - Don't overcomplicate
Micro-ORM Introduction - Don't overcomplicate
 
3 database-jdbc(1)
3 database-jdbc(1)3 database-jdbc(1)
3 database-jdbc(1)
 
TechTalk - Dotnet
TechTalk - DotnetTechTalk - Dotnet
TechTalk - Dotnet
 
PostThis
PostThisPostThis
PostThis
 
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
 
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
.NET Fest 2017. Михаил Щербаков. Механизмы предотвращения атак в ASP.NET Core
 
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdfSummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
SummaryHW6 Account ManagementIn HW4, you kept track of multiple.pdf
 
Green dao
Green daoGreen dao
Green dao
 
Web весна 2013 лекция 6
Web весна 2013 лекция 6Web весна 2013 лекция 6
Web весна 2013 лекция 6
 
Clean Code
Clean CodeClean Code
Clean Code
 
srgoc
srgocsrgoc
srgoc
 
C# 6.0 - April 2014 preview
C# 6.0 - April 2014 previewC# 6.0 - April 2014 preview
C# 6.0 - April 2014 preview
 
#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기#살아있다 #자프링외길12년차 #코프링2개월생존기
#살아있다 #자프링외길12년차 #코프링2개월생존기
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#
 
Embedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for JavaEmbedded Typesafe Domain Specific Languages for Java
Embedded Typesafe Domain Specific Languages for Java
 
Clean code
Clean codeClean code
Clean code
 

Último

Salient Features of India constitution especially power and functions
Salient Features of India constitution especially power and functionsSalient Features of India constitution especially power and functions
Salient Features of India constitution especially power and functions
KarakKing
 

Último (20)

TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
TỔNG ÔN TẬP THI VÀO LỚP 10 MÔN TIẾNG ANH NĂM HỌC 2023 - 2024 CÓ ĐÁP ÁN (NGỮ Â...
 
Unit 3 Emotional Intelligence and Spiritual Intelligence.pdf
Unit 3 Emotional Intelligence and Spiritual Intelligence.pdfUnit 3 Emotional Intelligence and Spiritual Intelligence.pdf
Unit 3 Emotional Intelligence and Spiritual Intelligence.pdf
 
SKILL OF INTRODUCING THE LESSON MICRO SKILLS.pptx
SKILL OF INTRODUCING THE LESSON MICRO SKILLS.pptxSKILL OF INTRODUCING THE LESSON MICRO SKILLS.pptx
SKILL OF INTRODUCING THE LESSON MICRO SKILLS.pptx
 
Spatium Project Simulation student brief
Spatium Project Simulation student briefSpatium Project Simulation student brief
Spatium Project Simulation student brief
 
Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024Mehran University Newsletter Vol-X, Issue-I, 2024
Mehran University Newsletter Vol-X, Issue-I, 2024
 
Wellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptxWellbeing inclusion and digital dystopias.pptx
Wellbeing inclusion and digital dystopias.pptx
 
Sociology 101 Demonstration of Learning Exhibit
Sociology 101 Demonstration of Learning ExhibitSociology 101 Demonstration of Learning Exhibit
Sociology 101 Demonstration of Learning Exhibit
 
Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)Jamworks pilot and AI at Jisc (20/03/2024)
Jamworks pilot and AI at Jisc (20/03/2024)
 
Salient Features of India constitution especially power and functions
Salient Features of India constitution especially power and functionsSalient Features of India constitution especially power and functions
Salient Features of India constitution especially power and functions
 
Introduction to Nonprofit Accounting: The Basics
Introduction to Nonprofit Accounting: The BasicsIntroduction to Nonprofit Accounting: The Basics
Introduction to Nonprofit Accounting: The Basics
 
Application orientated numerical on hev.ppt
Application orientated numerical on hev.pptApplication orientated numerical on hev.ppt
Application orientated numerical on hev.ppt
 
ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.ICT role in 21st century education and it's challenges.
ICT role in 21st century education and it's challenges.
 
REMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptxREMIFENTANIL: An Ultra short acting opioid.pptx
REMIFENTANIL: An Ultra short acting opioid.pptx
 
Basic Civil Engineering first year Notes- Chapter 4 Building.pptx
Basic Civil Engineering first year Notes- Chapter 4 Building.pptxBasic Civil Engineering first year Notes- Chapter 4 Building.pptx
Basic Civil Engineering first year Notes- Chapter 4 Building.pptx
 
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
Sensory_Experience_and_Emotional_Resonance_in_Gabriel_Okaras_The_Piano_and_Th...
 
Single or Multiple melodic lines structure
Single or Multiple melodic lines structureSingle or Multiple melodic lines structure
Single or Multiple melodic lines structure
 
How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17How to Create and Manage Wizard in Odoo 17
How to Create and Manage Wizard in Odoo 17
 
How to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POSHow to Manage Global Discount in Odoo 17 POS
How to Manage Global Discount in Odoo 17 POS
 
Google Gemini An AI Revolution in Education.pptx
Google Gemini An AI Revolution in Education.pptxGoogle Gemini An AI Revolution in Education.pptx
Google Gemini An AI Revolution in Education.pptx
 
Holdier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdfHoldier Curriculum Vitae (April 2024).pdf
Holdier Curriculum Vitae (April 2024).pdf
 

Entity Framework Core: tips and tricks

  • 2. About Us MCC Tomsk Cloud platform for tele-medicine Microservices Message Broker SOA .Net Core React/Redux E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 3. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 4. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 5. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 6. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 7. using (SqlConnection connection = new SqlConnection(connectionString)) { SqlCommand command = new SqlCommand(queryString, connection); connection.Open(); SqlDataReader reader = command.ExecuteReader(); reader.Read(); Console.WriteLine("t{0}t{1}t{2}", reader[0], reader[1], reader[2]); } public class Comment { public int Id { get; set; } public string Text { get; set; } public bool IsDeleted { get; set; } public Post Post { get; set; } } var context = new MyContext(options); var comment = context.Comments.First(); Console.WriteLine("t{0}t{1}", comment.Text, comment.Id);
  • 8. var context = new MyContext(options); var comment = context.Comments .Include(x => x.Post) .Where(x => x.Id > 5) .Where(x => !x.Post.IsDeleted) .OrderBy(x => x.Text) .First(); Console.WriteLine("t{0}t{1}", comment.Post.Title, comment.Text); var sql = @"select * from #Posts p left join #Users u on u.Id = p.OwnerId Order by p.Id"; var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post; }); var post = data.First();
  • 10. Entities public class Post { public int Id { get; private set; } public string Title { get; set; } public Blog Blog { get; set; } public bool IsDeleted { get; set; } } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s public class Blog { public int Id { get; private set; } public string Name { get; set; } public ICollection<Post> Posts { get; } private Blog() { } public Blog(string name) { Name = name; } } public class Comment { public int Id { get; set; } public string Text { get; set; } public Post Post { get; set; } }
  • 11. SELECT N+1 var posts = context.Posts.ToList(); //SELECT * FROM Posts foreach (var post in posts) { var commentsCount = context.Comments .Count(x => !x.IsDeleted && x.Post.Id == post.Id); //SELECT COUNT(1) FROM Comments WHERE IsDeleted = 0 AND PostId = {0} } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 12. SELECT N+1 posts = context.Posts.Include(x => x.Comments).ToList(); //SELECT * FROM Posts WHERE IsDeleted = 0 //SELECT * FROM Comments //INNER JOIN (SELECT Id FROM Posts WHERE IsDeleted = 0) AS T ON PostId = T.Id foreach (var post in posts) { var commentsCount = post.Comments.Count(); } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 13. SELECT N+1 var posts3 = context.Posts.Select(x => new { PostTitle = x.Title, PostId = x.Id, CommentsCount = x.Comments.Count(), }).ToList(); //SELECT Title, Id, (SELECT COUNT(*) //FROM Comments AS c WHERE [x].[Id] = [c].[PostId]) AS CommentsCount //FROM Posts AS x foreach (var post in posts3) { var commentsCount = post.CommentsCount; } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 14. SELECT N+1 var posts4 = context.Posts.Select(x => new { Post = x, CommentsCount = x.Comments.Count(), }).ToList(); //SELECT * FROM Posts //SELECT Count(*) FROM Comments WHERE PostId = {0} foreach (var post in posts3) { var commentsCount = post.CommentsCount; } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 15. SELECT N+1 var posts5 = context.Posts.Select(x => new { PostId = x.Id, Blog = x.Blog, CommentsCount = x.Comments.Count(), }).ToList(); //SELECT [Blogs].*, (SELECT COUNT(*) FROM[Comments] AS[c] //WHERE[x].[Id] = [c].[PostId] ) AS[CommentsCount] //FROM[Posts] AS[x] //LEFT JOIN[Blogs] AS[x.Blog] ON[x].[BlogId] = [x.Blog].[Id] foreach (var post in posts3) { var commentsCount = post.CommentsCount; } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 16. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 17. Change Tracker context = _createDatabase(); var blog = context.Blogs.FirstOrDefault(); Assert.Null(blog.Posts); context.Posts.Where(x => x.BlogId == blog.Id).ToList(); Assert.Null(blog.Posts); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 18. E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 19. ToList() var blog = context.Blogs.Include(x => x.TimeRegion).ToList(); //SELECT * FROM Blogs //LEFT JOIN TimeRegion ON TimeRegion.Id = Blogs.TimeRegionId E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s context.TimeRegions.ToList(); var blog = context.Blogs.ToList(); //SELECT * FROM TimeRegions //SELECT * FROM Blogs
  • 20. GroupBy E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s var result = context.Posts .GroupBy(p => p.BlogId) .Select(g => new { BlogId = g.Key, PostCount = g.Count() }) .ToList(); //SELECT[p].[BlogId] AS[BID], COUNT(*) AS[cnt] //FROM[Posts] AS[p] //GROUP BY[p].[BlogId]
  • 21. GroupBy var optionsBuilder = new DbContextOptionsBuilder<MyContext>() .ConfigureWarnings(x => x.Throw(RelationalEventId.QueryClientEvaluationWarning)); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s var result = context.Posts .GroupBy(p => p.BlogId) .Select(g => new { BID = g.Key, cnt = g.Count(x => !x.IsDeleted) }) .ToList(); var result = context.Posts .GroupBy(p => p.Blog) .Select(g => new { Url = g.Key.Name, Count = g.Count() }) .ToList();
  • 22. Aggregates var data = context.Blogs.Select(x => new { x.Id, MaxPostId = x.Posts.Max(post => post.Id) }).ToList(); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s InvalidOperationException: Sequence contains no elements. Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.GetResult<TResult>(IEnumerable <ValueBuffer> valueBuffers, bool throwOnNullResult)
  • 23. Min/Max/Average var data = context.Blogs.Select(x => new { x.Id, MaxPostId = x.Posts.Max(post => (int?)post.Id) }).ToList(); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 25. Private constructors & Collection initializers E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s public class Blog { public int Id { get; private set; } public string Name { get; set; } private Blog() { } public Blog(string name) { Name = name; } }
  • 26. Private constructors & Collection initializers E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s public class Blog { public int Id { get; private set; } public ICollection<Post> Posts { get; } = new List<Post>(); //don't do that } var blog = context.Blogs.FirstOrDefault(); var postCount = blog.Posts.Count();
  • 27. Lazy Loading • Since 2.1 • No private constructors (but could be protected) • Not recommended E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s var optionsBuilder = new DbContextOptionsBuilder<MyContext>() .UseLazyLoadingProxies(); var blog = context.Blogs.FirstOrDefault(); //SELECT * FROM Blogs LIMIT 1 var postCount = blog.Posts.Count(); // SELECT * FROM Posts WHERE BlogId = 1
  • 28. Target Multiple Databases public abstract class MainContext : DbContext { public DbSet<Comment> Comments { get; set; } protected MainContext(DbContextOptions<MainContext> options) : base(options) { } } public class PostgresContext : MainContext { public PostgresContext(DbContextOptions<MainContext> options) : base(options) { } } public class SqlServerContext : MainContext { public SqlServerContext(DbContextOptions<MainContext> options) : base(options) { } } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 29. Migrations.Designer.cs (could be deleted) [Migration("20181124143113_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "2.2.0-preview3-35497") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("XUnitTestProject1.Blog", b => { b.Property<int>("Id") .ValueGeneratedOnAdd() .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property<string>("Name"); b.HasKey("Id"); b.ToTable("Blogs"); }); modelBuilder.Entity("XUnitTestProject1.Comment", b => { b.Property<int>("Id") .ValueGeneratedOnAdd() .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property<bool>("IsDeleted"); b.Property<int?>("PostId"); b.Property<string>("Text"); b.HasKey("Id"); b.HasIndex("PostId"); b.ToTable("Comments"); });
  • 30. Value Convertors • EnumToStringConverter • DateTimeToTicksConverter • Objects as JSON serialized string (not built-in) E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 31. Value Convertors (JSON serialized-objects) public class Blog { public int Id { get; private set; } … public BlogSettings Settings { get; set; } } public class BlogSettings { public int BackgroundColor { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>().Property(e => e.Settings) .HasConversion( v => JsonConvert.SerializeObject(v), v => JsonConvert.DeserializeObject<BlogSettings>(v)); }
  • 32. Query Filter (Soft Delete) protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted); } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s var blogs = context.Posts.ToList(); //SELECT * FROM Posts WHERE IsDeleted = 0 public class Post { public int Id { get; private set; } public string Title { get; set; } public Blog Blog { get; set; } public bool IsDeleted { get; set; } }
  • 34. In Memory var options = new DbContextOptionsBuilder<MyContext>() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .Options; _createDatabase = () => new MyContext(options); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 35. In Memory & transactions var options = new DbContextOptionsBuilder<MyContext>() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .ConfigureWarnings(x => x.Ignore(InMemoryEventId.TransactionIgnoredWarning)) .Options; E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 36. In Memory & constraints??? SQLite! var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); //an open connection is required for database to exist var optionsBuilder = new DbContextOptionsBuilder<MyContext>() .ConfigureWarnings(x => x.Throw(RelationalEventId.QueryClientEvaluationWarning)) .UseLazyLoadingProxies() ; var options = optionsBuilder.UseSqlite(connection).Options; E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 37. Migrations [Fact] public void Migrations_RunAll_MigrationsSuccessfullyApplied() { using (var connection = new SqliteConnection("DataSource=:memory:")) { connection.Open(); //an open connection is required for database to exist var optionsBuilder = new DbContextOptionsBuilder<MyContext>(); var options = optionsBuilder.UseSqlite(connection).Options; var context = new MyContext(options); context.Database.Migrate(); } } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 38. Migrations var context = new MyContext(options); context.Database.Migrate(); foreach (var entityType in context.Model.GetEntityTypes()) { GetDbSet(context, entityType.ClrType).First(); } E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s System.InvalidOperationException : Error querying entity 'Post': SQLite Error 1: 'no such column: p.Date'.
  • 40. Z.EntityFramework.Plus.EFCore – Bulk Operations _dbContext.Comments.Where(x => !x.IsDeleted) .Update(x => new Comment() { IsDeleted = true, }); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s UPDATE A SET A.[IsDeleted] = @zzz_BatchUpdate_0 FROM[Comments] AS A INNER JOIN(SELECT[x].[Id], [x].[IsDeleted], [x].[PostId], [x].[Text] FROM [Comments] AS [x] WHERE [x].[IsDeleted] = 0 ) AS B ON A.[Id] = B.[Id] BulkUpdate does not work with InMemoryDatabase
  • 41. Z.EntityFramework.Plus.EFCore – FromCache var timeRegions = _dbContext.TimeRegions.FromCache(); _dbContext.AttachRange(timeRegions); var blogs = _dbContext.Blogs.ToList(); Console.WriteLine(string.Join(", ", blogs.Select(blog => $"{blog.Name}: {blog.TimeRegion.Offset}"))); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 42. Dapper (EFSqlTranslator) var query = context.Posts .Select(p => new { BlogId = p.Blog.Id, Title = p.Title }) .GroupBy(x => x.BlogId) .Select(x => new { Id = x.Key, Cnt = x.Count() }); var queryResult = context.Query(query, new EFModelInfoProvider(context), new SqliteObjectFactory(), out var sql); E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 43. Override SaveChanges • Watch changes (e.g. cache invalidation) • Log changes (e.g. audit) • Inform about changes (send to 3rd party) E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 45. FullText Search E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 46. Summary • SELECT N+1 • Group By & Aggregate • ChangeTracker • SaveChanges() override • Testing & Migrations E n t i t y F r a m e w o r k C o r e : t i p s a n d t r i c k s
  • 48. До встречи 31 января! TomskDotNet #2! Точка Кипения 31 января, 18:30

Notas do Editor

  1. Сейчас я нарисую самую сложную архитектуру
  2. Сейчас я нарисую самую сложную архитектуру
  3. Сейчас я нарисую самую сложную архитектуру