我第一次使用Entity框架,想知道我是否在使用最佳实践。
我在业务逻辑中创建了一个单独的类来处理实体上下文。我遇到的问题是,在我看到的所有视频中,他们通常将上下文包装在using语句中以确保其关闭,但显然我不能在我的业务逻辑中执行此操作,因为上下文将在我实际关闭之前关闭用它?
这就是我正在做的事情吗?几个例子:
public IEnumerable<Article> GetLatestArticles(bool Authorised)
{
var ctx = new ArticleNetEntities();
return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
{
var ctx = new ArticleNetEntities();
return ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
我只是想确保当很多人使用它时,我不会构建一些会死的东西?
答案 0 :(得分:64)
这实际上取决于如何公开您的存储库/数据存储。
不确定你的意思是“上下文将被关闭,因此我不能做业务逻辑”。在您的业务逻辑里面使用using语句。或者,如果您的业务逻辑属于不同的类,那么让我们继续。 :)
有些人从他们的Repository返回具体的集合,在这种情况下你可以在using语句中包含上下文:
public class ArticleRepository
{
public List<Article> GetArticles()
{
List<Article> articles = null;
using (var db = new ArticleNetEntities())
{
articles = db.Articles.Where(something).Take(some).ToList();
}
}
}
这样做的好处是满足了连接的良好实践 - 尽可能晚地打开,并尽早关闭。
您可以将所有业务逻辑封装在using语句中。
缺点 - 您的存储库会发现我个人不喜欢的业务逻辑,并且您最终会针对每个特定方案使用不同的方法。
第二个选项 - 在Repository中新建一个上下文,并使其实现IDisposable。
public class ArticleRepository : IDisposable
{
ArticleNetEntities db;
public ArticleRepository()
{
db = new ArticleNetEntities();
}
public List<Article> GetArticles()
{
List<Article> articles = null;
db.Articles.Where(something).Take(some).ToList();
}
public void Dispose()
{
db.Dispose();
}
}
然后:
using (var repository = new ArticleRepository())
{
var articles = repository.GetArticles();
}
或第三选项(我最喜欢的),使用依赖注入。将所有上下文工作与存储库分离,并让DI容器处理资源:
public class ArticleRepository
{
private IObjectContext _ctx;
public ArticleRepository(IObjectContext ctx)
{
_ctx = ctx;
}
public IQueryable<Article> Find()
{
return _ctx.Articles;
}
}
您选择的DI容器会将具体的ObjectContext注入到Repository的实例化中,并使用已配置的生命周期(Singleton,HttpContext,ThreadLocal等),并根据该配置对其进行处理。
我进行了设置,因此每个HTTP请求都会获得一个新的Context。请求完成后,我的DI容器将自动处理上下文。
我还在这里使用工作单元模式,允许多个存储库使用一个对象上下文。
您可能也注意到我更喜欢从我的存储库返回IQueryable(而不是具体的List)。更强大(但风险很大,如果你不理解其含义)。我的服务层在IQueryable上执行业务逻辑,然后将具体集合返回给UI。
这是我最强大的选择,因为它允许简单的存储库,工作单元管理上下文,服务层管理业务逻辑,DI容器处理资源/对象的生命周期/处置
如果您想了解更多信息,请告诉我 - 因为它有很多内容,甚至超过这个令人惊讶的长答案。 :)
答案 1 :(得分:3)
我会将ctx作为每个类中的私有变量,然后每次创建一个新的实例,然后在完成时进行处理。
public class ArticleService
{
private ArticleEntities _ctx;
public ArticleService()
{
_ctx = new ArticleEntities();
}
public IEnumerable<Article> GetLatestArticles(bool Authorised)
{
return _ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
{
return _ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
}
public void Dispose()
{
_ctx.Dispose();
_ctx = null;
}
}
然后调用它。
ArticleService articleService = new ArticleService();
IEnumerable<Article> article = articleService.GetLatestArticles(true);
articleService.Dispose(); // killing the connection
这样您还可以在同一个上下文中添加/更新其他对象,并调用一个save方法,该方法通过Entity保存对db的任何更改。
答案 2 :(得分:3)
根据我的经验,这段代码并不好,因为你失去了通过导航属性导航关系的能力。
public List<Articles> getArticles( ){
using (var db = new ArticleNetEntities())
{
articles = db.Articles.Where(something).ToList();
}
}
使用此方法,您无法使用以下代码,因为a.Members始终为null(db context is close并且无法自动获取数据)。
var articles = Data.getArticles();
foreach( var a in articles ) {
if( a.Members.any(p=>p.Name=="miki") ) {
...
}
else {
...
}
}
}
仅使用全局数据库上下文是一个坏主意,因为您必须使用删除更改功能
在您的应用程序中,您可以执行此操作,但不保存更改并关闭窗口
var article= globalcontext.getArticleByID(10);
article.Approved=true;
然后在另一个应用程序中,你做了一些操作并保存
//..... something
globalcontext.saveChanges();
在这种情况下,先前文章批准的属性设置为由实体框架修改。当你保存,批准设置为真!
对我来说,最好的方法是每班使用1个上下文 如果需要,可以将上下文传递给另一个外部方法
class EditArticle {
private DbEntities de;
private currentAricle;
public EditArticle() {
de = new DbEntities; //inizialize on new istance
}
loadArticleToEdit(Articele a){
// a is from another context
currentArticle= de.Article.Single(p=>p.IdArticle==a.IdArticle){
}
private saveChanges(){
...
pe.saveChanges();
}
}
答案 3 :(得分:0)
您还可以做的是将您的背景存储在更高的级别。
例如,您可以拥有一个存储当前上下文的静态类:
class ContextManager
{
[ThreadStatic]
public static ArticleEntities CurrentContext;
}
然后,在你外面的某个地方做这样的事情:
using (ContextManager.CurrentContext = new ArticleEntities())
{
IEnumerable<Article> article = articleService.GetLatestArticles(true);
}
然后,在GetLastestArticles中,您只需使用相同的ContextManager.CurrentContext。
当然,这只是基本的想法。通过使用服务提供商,IoC等,您可以使其更加可行。
答案 4 :(得分:0)
您可以通过为所有必需的Entity Framework功能创建通用存储库类,从数据访问层开始准备Entity Framework。然后,您可以在业务层(封装)
中使用它以下是我在数据,业务和UI层中用于Entity Framework的最佳实践
用于此练习的技巧: