【问题标题】:Using repository patern when using async / await methods Asp.NET MVC5 EF使用异步/等待方法时使用存储库模式 Asp.NET MVC 5 EF
【发布时间】:2016-10-09 23:50:58
【问题描述】:

您能否解释一下在使用 async / await 方法时如何实现存储库模式,这里是没有 async 的示例:

型号:

public class Note
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
}

界面:

interface INoteRepository : IDisposable
{
    IEnumerable<Note> GetNotes();
    Note GetNoteById(Guid? id);
    void PostNote(Note note);
    void DeleteNote(Guid id);
    void PutNote(Note note);
    void Save();
}

存储库:

public class NoteRepository : INoteRepository, IDisposable
{
    private MyDbContext context;

    public NoteRepository(MyDbContext _context)
    {
        context = _context;
    }
    public void DeleteNote(Guid id)
    {
        Note note = context.Notes.Find(id);
        context.Notes.Remove(note);
    }

    public Note GetNoteById(Guid id)
    {
        return context.Notes.Find(id);
    }

    public IEnumerable<Note> GetNotes()
    {
        return context.Notes.ToList();
    }

    public void PostNote(Note note)
    {
        context.Notes.Add(note);
    }

    public void PutNote(Note note)
    {
        context.Entry(note).State = EntityState.Modified;
    }

    public void Save()
    {
        context.SaveChanges();
    }

    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
                context.Dispose();
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }

    // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
    // ~NoteRepository() {
    //   // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
    //   Dispose(false);
    // }

    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
        GC.SuppressFinalize(this);
    }

    public Note GetNoteById(Guid? id)
    {
        return context.Notes.Find(id);
    }
    #endregion

}

控制器:

public class NotesController : Controller
{
    //private MyDbContext db = new MyDbContext();

    private INoteRepository noterepository;

    //public NotesController(INoteRepository _noterepository)
    //{
    //    _noterepository = noterepository;
    //}

    public NotesController()
    {
        noterepository = new NoteRepository(new MyDbContext());
    }



    // GET: Notes
    public ActionResult Index()
    {
        return View(noterepository.GetNotes());
    }

    // GET: Notes/Details/5
    public ActionResult Details(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Note note = noterepository.GetNoteById(id);
        if (note == null)
        {
            return HttpNotFound();
        }
        return View(note);
    }

    // GET: Notes/Create
    public ActionResult Create()
    {
        return View();
    }

    // POST: Notes/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,Title,Body")] Note note)
    {
        if (ModelState.IsValid)
        {
            note.Id = Guid.NewGuid();
            noterepository.PostNote(note);
            noterepository.Save();
            return RedirectToAction("Index");
        }

        return View(note);
    }

    // GET: Notes/Edit/5
    public ActionResult Edit(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Note note = noterepository.GetNoteById(id);
        if (note == null)
        {
            return HttpNotFound();
        }
        return View(note);
    }

    // POST: Notes/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "Id,Title,Body")] Note note)
    {
        if (ModelState.IsValid)
        {
            noterepository.PutNote(note);
            noterepository.Save();
            return RedirectToAction("Index");
        }
        return View(note);
    }

    // GET: Notes/Delete/5
    public ActionResult Delete(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        Note note = noterepository.GetNoteById(id);
        if (note == null)
        {
            return HttpNotFound();
        }
        return View(note);
    }

    // POST: Notes/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(Guid id)
    {
        Note note = noterepository.GetNoteById(id);
        noterepository.DeleteNote(id);
        noterepository.Save();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            noterepository.Dispose();
        }
        base.Dispose(disposing);
    }
}

现在有一个问题: 我如何使用 acync / await 方法做到这一点,通过搜索我找到了一些示例,但我无法理解网络中没有完整的示例。 非常感谢。

【问题讨论】:

    标签: c# entity-framework asynchronous asp.net-mvc-5


    【解决方案1】:

    您的新异步存储库类:

    public class NoteRepository : INoteRepository, IDisposable
    {
        private MyDbContext context;
    
        public NoteRepository(MyDbContext _context)
        {
            context = _context;
        }
    
        public async Task DeleteNoteAsync(Guid id)
        {
            Note note = await context.Notes.FindAsync(id);
            context.Notes.Remove(note);
        }
    
        public async Task<Note> GetNoteByIdAsync(Guid id)
        {
            return await context.Notes.FindAsync(id);
        }
    
        public async Task<List<Note>> GetNotesAsync()
        {
            return await context.Notes.ToListAsync();
        }
    
        ...
    
        public async Task SaveAsync()
        {
            await context.SaveChangesAsync();
        }
    
        ...
    
        public async Task<Note> GetNoteById(Guid? id)
        {
            return await context.Notes.FindAsync(id);
        }
    
        #endregion
    }
    

    变化:

    • 将每个返回值 T 转换为 async Task&lt;T&gt;
    • 在内部使用数据库异步操作
    • 为方法名称添加“异步”后缀

    你的新控制器类:

    public class NotesController : Controller
    {
        //private MyDbContext db = new MyDbContext();
    
        private INoteRepository noterepository;
    
        //public NotesController(INoteRepository _noterepository)
        //{
        //    _noterepository = noterepository;
        //}
    
        public NotesController()
        {
            noterepository = new NoteRepository(new MyDbContext());
        }
    
    
    
        // GET: Notes
        public async Task<ActionResult> Index()
        {
            return await View(noterepository.GetNotesAsync());
        }
    
        // GET: Notes/Details/5
        public async Task<ActionResult> Details(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Note note = await noterepository.GetNoteByIdAsync(id);
            if (note == null)
            {
                return HttpNotFound();
            }
            return View(note);
        }
    
        // GET: Notes/Create
        public ActionResult Create()
        {
            return View();
        }
    
        // POST: Notes/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create([Bind(Include = "Id,Title,Body")] Note note)
        {
            if (ModelState.IsValid)
            {
                note.Id = Guid.NewGuid();
                await noterepository.PostNoteAsync(note);
                await noterepository.SaveAsync();
                return RedirectToAction("Index");
            }
    
            return View(note);
        }
    
        // GET: Notes/Edit/5
        public async Task<ActionResult> Edit(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Note note = await noterepository.GetNoteByIdAsync(id);
            if (note == null)
            {
                return HttpNotFound();
            }
            return View(note);
        }
    
        // POST: Notes/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit([Bind(Include = "Id,Title,Body")] Note note)
        {
            if (ModelState.IsValid)
            {
                await noterepository.PutNoteAsync(note);
                await noterepository.SaveAsync();
                return RedirectToAction("Index");
            }
            return View(note);
        }
    
        // GET: Notes/Delete/5
        public async Task<ActionResult> Delete(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Note note = await noterepository.GetNoteByIdAsync(id);
            if (note == null)
            {
                return HttpNotFound();
            }
            return View(note);
        }
    
        // POST: Notes/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(Guid id)
        {
            Note note = await noterepository.GetNoteByIdAsync(id);
            await noterepository.DeleteNoteAsync(id);
            await noterepository.SaveAsync();
            return RedirectToAction("Index");
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                noterepository.Dispose();
            }
            base.Dispose(disposing);
        }
    }
    

    变化:

    • 将每个返回值 T 转换为 async Task&lt;T&gt;
    • 在内部使用 noterepository 异步操作

    【讨论】:

    • 非常感谢 omikad,因为我认为某些操作没有理由使用存储库模式来执行它们,例如 Create。
    • 是的,没错,有些动作不需要数据库。而且,正如您所见,这种转换非常机械化。
    • 我希望数据访问层返回 IEnumerable,因为我可以在从 EF 生成的查询中堆叠 .where(...) 和其他子句;当您制作 .ToListAsync() 时,您将获取整个数据集,如果数据访问中的方法与 .where(...) 子句一起应用,则过滤将在 .NET 中进行。如果过滤发生在 MSSQL 内部,性能会更好。
    • @Nikola 有时返回 IEnumerable 不是一个好主意。如果您在 Linq 查询上使用 AsEnumerable,然后向其中添加新的 where,它实际上仍会从 db 检索整个数据集,然后在 .NET 端应用附加条件。但是返回 IQueryable 也不是一个好主意,因为这将是一个泄漏的抽象,将 EF 查询暴露给外界。理想情况下,存储库应尽可能独立,以返回业务运营所需的最终数据。这就是为什么通用存储库有时不受欢迎的原因。
    猜你喜欢
    • 2018-07-21
    • 2022-12-09
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 2016-09-22
    相关资源
    最近更新 更多