临近期末了,老师想要我们完成一个期末项目以对本学期的学习进行一个汇总考核。在期末项目中,我使用了前端框架为vue全家桶(vue + vuex / pinia + vue-router + axios),ui框架有Element UI、Vant、Vuetify,以及一些各种能使开发更加方便快捷也更接近实际生产需求的npm包;后端使用了.net core 6版本的 asp.net core WebApi 搭配上furion实现后端接口,orm框架有EntityFramework Core、Dapper,SignalR作为通信框架,以及automapper等适用于生产环境的包;数据库使用了微软的Sql Server ,使用redis 作为一个缓存中间件以及消息队列的模拟,以及使用了mongodb作为文件管理。本篇文章我们着重讲解一下在使用EntityFramework Core中可能会遇到的问题以及解决方案。
首先介绍一下EntityFramework Core(EF Core):
Entity Framework Core是微软推出的一种开源的ORM(对象关系映射)框架,它可以帮助我们轻松地将数据库中的数据转换成对象,同时也可以将对象保存回数据库中。
相比于传统的SQL查询,使用Entity Framework Core可以更加方便地进行数据操作,而且它的学习曲线也比较平缓,适合我们进行学习和使用。
在Entity Framework Core中,我们可以通过定义实体类来映射数据库中的表,通过定义实体类的属性来映射表中的列。我们可以通过查询语句来获取数据,并且可以使用Lambda表达式来进行条件筛选和排序。此外,Entity Framework Core还支持多线程和并发操作,可以更好地满足大规模数据操作的需求。
总的来说,Entity Framework Core是一种易学、高效的数据访问框架,可以帮助我们更加方便地进行数据库操作。
在写期末项目时遇到了一些小问题,其中比较值得说一下的就是EFcore模糊查询和多表查询时遇到的两个问题,下面我们来复现一下这个问题:
查询的表结构如下:
当我要进行多表连接模糊查询时,代码如下:
public async Task<IActionResult> Get(string key)
{
try
{
int page = 1;
int limit = 15;
var ef = new CmsContext();
int offset = (page - 1) * limit;
var li =
await (from a in ef.TxtArticles
join b in ef.TxtArticleTags on a.Id equals b.ArticleId into t1
from b in t1.DefaultIfEmpty()
join c in ef.TxtTags on b.TagId equals c.Id into t2
from c in t2.DefaultIfEmpty()
join d in ef.AdUsers on a.UserId equals d.Id into t3
from d in t3.DefaultIfEmpty()
join e in ef.AdUserImgs on d.Id equals e.UserId into t4
from e in t4.DefaultIfEmpty()
join f in ef.TxtArticleContents on a.Id equals f.ArticleId into t5
from f in t5.DefaultIfEmpty()
join g in ef.TxtTypes on a.TypeId equals g.Id into t6
from g in t6.DefaultIfEmpty()
where f.Content.Contains(key)
orderby a.PubTime descending
select new
{
id = a.Id,
writer = d.Name,
uid = d.Id,
title = a.Title,
content = f.Content,
pub_time = a.PubTime,
view_num = a.ViewNum,
like_num = a.LikeNum,
front = a.Front,
type_id = a.TypeId,
type_name = g.TypeName
})
.Skip(offset).Take(limit).ToListAsync();
return Json(li);
}catch(Exception ex)
{
return Json(ex.Message);
}
出现了以下问题:
"Argument data type text is invalid for argument 1 of charindex function."
让我们百度翻译一下,看看出现了什么问题
总之大概意思就是contains不支持text类型的字段,网上有的解决方案就是将字段的类型设置为varchar(max),那么如何在不改变数据库结构的情况下解决呢?
只需要将查询语句换成:
EF.Functions.Like(f.Content, "%" + key + "%")
使用EFcore的原生like查询进行条件查询就可以了,代码如下:
public async Task<IActionResult> Get(string key)
{
try
{
int page = 1;
int limit = 15;
var ef = new CmsContext();
int offset = (page - 1) * limit;
var li =
await (from a in ef.TxtArticles
join b in ef.TxtArticleTags on a.Id equals b.ArticleId into t1
from b in t1.DefaultIfEmpty()
join c in ef.TxtTags on b.TagId equals c.Id into t2
from c in t2.DefaultIfEmpty()
join d in ef.AdUsers on a.UserId equals d.Id into t3
from d in t3.DefaultIfEmpty()
join e in ef.AdUserImgs on d.Id equals e.UserId into t4
from e in t4.DefaultIfEmpty()
join f in ef.TxtArticleContents on a.Id equals f.ArticleId into t5
from f in t5.DefaultIfEmpty()
join g in ef.TxtTypes on a.TypeId equals g.Id into t6
from g in t6.DefaultIfEmpty()
where EF.Functions.Like(f.Content, "%" + key + "%")
orderby a.PubTime descending
select new
{
id = a.Id,
writer = d.Name,
uid = d.Id,
title = a.Title,
content = f.Content,
pub_time = a.PubTime,
view_num = a.ViewNum,
like_num = a.LikeNum,
front = a.Front,
type_id = a.TypeId,
type_name = g.TypeName
})
.Skip(offset).Take(limit).ToListAsync();
return Json(li);
}catch(Exception ex)
{
return Json(ex.Message);
}
}
执行一下看下效果
确实可以查出数据了
那如果多字段都用同一个条件呢?代码如下:
where EF.Functions.Like(a.Title.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(c.TagName.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(g.TypeName.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(f.Content, "%" + key + "%")
|| EF.Functions.Like(d.Name.ToLower(), "%" + key.ToLower() + "%")
让我们看看结果:
查询是查询出结果了,但是出现了重复的数据。我们想要去重的话一般会用到efcore的distinct()方法,代码如下:
public async Task<IActionResult> Get(string key)
{
try
{
int page = 1;
int limit = 30;
var ef = new CmsContext();
int offset = (page - 1) * limit;
var li =
await (from a in ef.TxtArticles
join b in ef.TxtArticleTags on a.Id equals b.ArticleId into t1
from b in t1.DefaultIfEmpty()
join c in ef.TxtTags on b.TagId equals c.Id into t2
from c in t2.DefaultIfEmpty()
join d in ef.AdUsers on a.UserId equals d.Id into t3
from d in t3.DefaultIfEmpty()
join e in ef.AdUserImgs on d.Id equals e.UserId into t4
from e in t4.DefaultIfEmpty()
join f in ef.TxtArticleContents on a.Id equals f.ArticleId into t5
from f in t5.DefaultIfEmpty()
join g in ef.TxtTypes on a.TypeId equals g.Id into t6
from g in t6.DefaultIfEmpty()
where EF.Functions.Like(a.Title.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(c.TagName.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(g.TypeName.ToLower(), "%" + key.ToLower() + "%")
|| EF.Functions.Like(f.Content, "%" + key + "%")
|| EF.Functions.Like(d.Name.ToLower(), "%" + key.ToLower() + "%")
orderby a.PubTime descending
select new
{
id = a.Id,
writer = d.Name,
uid = d.Id,
title = a.Title,
//content = f.Content,
pub_time = a.PubTime,
view_num = a.ViewNum,
like_num = a.LikeNum,
front = a.Front,
type_id = a.TypeId,
type_name = g.TypeName
})
.Distinct()
.Skip(offset).Take(limit).ToListAsync();
return Json(li);
}catch(Exception ex)
{
return Json(ex.Message);
}
}
这样子确实好像也可以了,但是如果查询的字段中有text类型的话,我们把下面的注释取消后,就出现了以下报错:
"The text data type cannot be selected as DISTINCT because it is not comparable."
大致意思就是distinct不能用于text类型,那么如何在不改变数据库结构的条件下完成我们接口的编写呢?这就不得不提到C#的集合类型之一HashSet了,这就是一个集合,有一些基础的小伙伴们都知道集合是不可重复的,所以我们只要把查询出来的结果转换成HashSet形式就可以解决问题了。
HashSet<dynamic> hs = new HashSet<dynamic>();
hs = li.ToHashSet<dynamic>();
return Json(hs);
在代码的最后将我们查询的结果给ToHashSet一下就可以解决问题了。
查询的结果如下:
确实是去重了,这里的dynamic是C#的动态类型,虽然这让C#这个强语言变得更加灵活,但这样的 写法其实是很脏的,作为一门面向对象的高级程序设计语言,建议还是使用实体类进行传递。当然dynamic类型也是一大进步,有很多良好的特性,这里就不多加赘述了,各位感兴趣的小伙伴可以自行了解。
也许上述方法对性能容易产生影响,但确实也是解决方案之一,也考察了我们对数据库的理解,
对EFCore的使用、对C#类型的实际应用。尽管绕了一大圈,但结果总算是对了,也希望大家在学习或工作中对拥抱新事物,热爱代码,做到学以致用、学有所成。
最后向大家介绍一下在开发中我使用的后端框架----Fruion,让 .NET 开发更简单,更通用,更流行。官网地址如下:
让 .NET 开发更简单,更通用,更流行。 Furion | Furion
是一个非常不错的框架,上手快速,对新手也很友好,官方文档也很详细,使.net开发更加方便快捷。