Include与ThenInclude
在这里面比较关注的是Include和ThenInclude语法
var student = await _context.Students.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
在这个例子中类似sql中的left join
先去查找Students的导航属性Enrollments,然后再去关联Enrollment的Course属性,进而获取到课程的相关信息
Include的用法,通过查看源码有以下几个用法
/// <summary>
/// Specifies related entities to include in the query results. The navigation property to be included is specified starting with the
/// type of entity being queried (<typeparamref name="TEntity" />). If you wish to include additional types based on the navigation
/// properties of the type being included, then chain a call to
/// <see cref="M:Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ThenInclude``3(Microsoft.EntityFrameworkCore.Query.IIncludableQueryable{``0,System.Collections.Generic.IEnumerable{``1}},System.Linq.Expressions.Expression{System.Func{``1,``2}})" />
/// after this call.
/// </summary>
/// <example>
/// <para>
/// The following query shows including a single level of related entities.
/// <code>
/// context.Blogs.Include(blog => blog.Posts);
/// </code>
/// </para>
/// <para>
/// The following query shows including two levels of entities on the same branch.
/// <code>
/// context.Blogs
/// .Include(blog => blog.Posts).ThenInclude(post => post.Tags);
/// </code>
/// </para>
/// <para>
/// The following query shows including multiple levels and branches of related data.
/// <code>
/// context.Blogs
/// .Include(blog => blog.Posts).ThenInclude(post => post.Tags).ThenInclude(tag => tag.TagInfo)
/// .Include(blog => blog.Contributors);
/// </code>
/// </para>
/// <para>
/// The following query shows including a single level of related entities on a derived type using casting.
/// <code>
/// context.Blogs.Include(blog => ((SpecialBlog)blog).SpecialPosts);
/// </code>
/// </para>
/// <para>
/// The following query shows including a single level of related entities on a derived type using 'as' operator.
/// <code>
/// context.Blogs.Include(blog => (blog as SpecialBlog).SpecialPosts);
/// </code>
/// </para>
/// </example>
/// <typeparam name="TEntity"> The type of entity being queried. </typeparam>
/// <typeparam name="TProperty"> The type of the related entity to be included. </typeparam>
/// <param name="source"> The source query. </param>
/// <param name="navigationPropertyPath">
/// A lambda expression representing the navigation property to be included (<c>t => t.Property1</c>).
/// </param>
/// <returns>A new query with the related data included.</returns>
public static IIncludableQueryable<TEntity, TProperty> Include<TEntity, TProperty>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, TProperty>> navigationPropertyPath)
where TEntity : class;
其中
/// <summary>
/// Specifies related entities to include in the query results. The navigation property to be included is specified starting with the
/// type of entity being queried (<typeparamref name="TEntity" />). If you wish to include additional types based on the navigation
/// properties of the type being included, then chain a call to
/// <see cref="M:Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ThenInclude``3(Microsoft.EntityFrameworkCore.Query.IIncludableQueryable{``0,System.Collections.Generic.IEnumerable{``1}},System.Linq.Expressions.Expression{System.Func{``1,``2}})" />
/// after this call.
/// </summary>
这个方法适用于在查询结果中关联一些其他的实体。通过使用Linq中的属性来查看导航属性。如果还想关联另外的属性信息,需要使用ThenInclude。
几个应用举例:
context.Blogs.Include(blog =>blog.Posts);
context.Blogs.Include(blog => blog.Posts).ThenInclude(post=>post.Tags);
context.Blogs.Include(blog => blog.Posts).ThenInclude(post => post.Tags).ThenInclude(tag =>tag.TagInfo)
.Include(blog =>blog.Contributors);
路由数据
3.x与2.x的路由表示还是不一样的
2.X:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
3.X:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
其他
ValidateAntiForgeryToken
特性帮助抵御跨网站请求伪造 (CSRF) 攻击。 令牌通过 FormTagHelper 自动注入到视图中,并在用户提交表单时包含该令牌。 令牌由 ValidateAntiForgeryToken
特性验证。
模型绑定
[Microsoft.AspNetCore.Mvc.NonAction]
public virtual System.Threading.Tasks.Task<bool> TryUpdateModelAsync (object model, Type modelType, string prefix);
以基于已发布表单数据中的用户输入更新已检索实体中的字段。 Entity Framework
的自动更改跟踪在由表单输入更改的字段上设置 Modified
标记。 调用 SaveChanges
方法时,Entity Framework
会创建 SQL 语句,以更新数据库行。 忽略并发冲突,并且仅在数据库中更新由用户更新的表列。
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
try
{
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
}
数据库连接关闭
数据库连接用完之后,需要进行关闭,释放数据库资源。ASP.NET Core内置依赖关系注入会完成此任务。
在依赖注入DbContext时默认的生存期是Scoped
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("defaultConnection")));
services.AddMvc();
}
查看解析生存期是ServiceLifetime.Scoped
public static IServiceCollection AddDbContext<TContext>(
this IServiceCollection serviceCollection,
Action<DbContextOptionsBuilder> optionsAction = null,
ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
ServiceLifetime optionsLifetime = ServiceLifetime.Scoped)
where TContext : DbContext;
这说明上下文生存期与Web请求生存期一致,并在web请求结束时将自动调用Dispose方法。
数据库事务
默认情况EF会自动隐式实现事务。