Using repository patern when using async / await methods Asp.NET MVC5 EF

24,190

Your new async Repository class:

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
}

Changes:

  • Convert every return value T to async Task<T>
  • Use data base async operations inside
  • Add 'Async' suffix to method names

Your new Controller class:

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);
    }
}

Changes:

  • Convert every return value T to async Task<T>
  • Use noterepository async operations inside
Share:
24,190
Archil Labadze
Author by

Archil Labadze

Hi friends, My name Archil Labadze and I am engaged in the Arts, music, IT and programming mostly web programming, writing sites on Node.js, Python, Agular CLI, React. I think the programming of art, as if not to create things with the soul it's just a waste of resources and time. Thank you to all, have a good day!

Updated on June 10, 2020

Comments

  • Archil Labadze
    Archil Labadze almost 4 years

    Can you explane me how to implement repository patterns when using async / await methods, here is example without async:

    Model:

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

    Interface:

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

    Repository:

    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
    
    }
    

    Controller:

    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);
        }
    }
    

    Now here is a question: How can I do this with acync / await methods, from searching I found some examples but I cannon understand there is no full exaple in network. Lot of thanks.

  • Archil Labadze
    Archil Labadze almost 8 years
    Lot of thanks omikad, as I see there is no reason for some actons to do them with repository patern for example Create.
  • omikad
    omikad almost 8 years
    Yes, that's right, some actions works without database. And, as you can see, that conversion is pretty mechanical.
  • nkalfov
    nkalfov almost 7 years
    I would expect the data-access layer to return IEnumerable<T>, because I would be able to stack .where(...) and other clauses inside the query generated from EF; when you make .ToListAsync(), you would fetch the whole dataset and if the method from the data-access is applied with .where(...) clause, the filtering would happen in .NET. It is better for the performance if the filtering happens inside MSSQL.
  • JustAMartin
    JustAMartin almost 5 years
    @Nikola Sometimes IEnumerable is not a good idea to return. If you use AsEnumerable on a Linq Query and then add new where to it, it will actually still retrieve entire dataset from db and then apply the additional criteria on the .NET side. But returning IQueryable is also not a good idea because it would be a leaky abstraction exposing EF query to the outside world. Repositories ideally should be as self contained as possible to return the final data required for business operations. That's why generic repositories are sometimes frowned upon.