Base solution for your next web application
Open Closed

Porting existing MVC app to Abp & reusing existing code #2881


User avatar
0
OutdoorEd created

I have an existing MVC 5 application that has been using standard ASP.NET Identity for Authentication/Role Authorization. I am porting it over to Abp.

I already have a populated database in my current app. I used EF Database First to generate the Models from the database and then EF MVC Scaffolding to build all of the Controllers and Views for basic CRUD operations. Since that code all works I want to see if I can reuse it. My thought is to take my current Models, Controllers, Views and place then in a new /Area in .Web. My database tables already have all the necessary Abp fields: TenantId, IsDeleted, DeletedUserId, LastModificationTime, LastModifiedUserId, CreationTime, CreatorUserId so no migrations are necessary.

From looking at the sample implementation implementing CRUD on one database table requires about 30 different code files in the various layers spread across .Application, .Core, .EntityFramework and .Web. With over 90 tables in the existing database I am trying to explore alternatives to throwing out all my existing code and rebuilding the entire application from scratch.

My questions: I need to authenticate via Abp Login and pass the TenantId over to my existing Controllers to filter all the records by TenantId and to be able to use the TenantId and UserId in Create and Update methods. Is it possible to inherit the necessary elements from Abp to use my existing Models and scaffolded Controllers without needing to recreate all of the layers in .Application, .Core, .EntityFramework?

If it is possible, what would I need to do to get my existing code to work with TenantId? Things like a second DbContext that inherits from AbpZeroDbContext, and Models that inherit IMustHaveTenant? Or are there any shortcuts to accessing the TenantId that would allow me to use my current code?

Thanks for any advice you can give me.


4 Answer(s)
  • User Avatar
    0
    OutdoorEd created

    Is there any way to use EF Scaffolding to generate CRUD operations in controllers that include TenantId for CRUD and filtering <ins>directly</ins>from the Entity without using Dtos, etc.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Sorry for the late reply. Yes, you can inherit your entities from IMayHaveTenant or IMustHaveTenant and add your entities to original db context or you can create a second dbContext which inherits from AbpDbContext.

    In that way, ABP will filter and set according to current AbpSession.TenantId.

    You also need to derive your existing controllers from *ControllerBase.

    Thanks.

  • User Avatar
    0
    OutdoorEd created

    Thanks. I tried setting this up with both the Default DbContent and separate DBContent but have not been able to get it to work.

    My Entity class inherits IMustHaveTenant

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity.Spatial;
    using Abp.Domain.Entities;
    
    [Table("idb_IncidentDamage")]
    public partial class IncidentDamage : IMustHaveTenant
    {
        public Guid Id { get; set; }
    

    My Second DbContext inherits AbpDbContext

    using System.Data.Entity; using OE_Tenant.Incidents.Entity; using Abp.EntityFramework;

    namespace OE_Tenant.EntityFramework { public class IDBDbContext: AbpDbContext { public IDBDbContext() : base("SecondDefault")

    I used the EF6 Scaffolding Wizard to generate the Controllers and Views for CRUD operations straight from the Entities and added the necessary references to Abp and inherited from the ControllerBase

    When looking at breakpoints in the Controller for the AbpSession.TenantId the correct TenantId <ins>is</ins> in the session variable however, there is no database filtering taking place. All records are shown from all Tenants. There must be something that is missing/needs to be added to the Scaffolded Controller to include the TenantId in filtering and CRUD operations.

    Controller using System; using System.Data.Entity; using System.Linq; using System.Net; using System.Web.Mvc; using Abp.Authorization; using OE_Tenant.EntityFramework; using OE_Tenant.Incidents.Entity; using OE_Tenant.Web.Controllers;

    namespace OE_Tenant.Web.Areas.Incidents.Controllers { [AbpAuthorize] public class IncidentDamageController : OE_TenantControllerBase //: Controller { private IDBDbContext db = new IDBDbContext();

        // GET: Incidents/IncidentDamage
        public ActionResult Index()
        {
            var tenantId = AbpSession.TenantId;
            var IncidentDamage = db.IncidentDamage.Include(i => i.Incident).Include(i => i.LkpDamageSeverity).Include(i => i.LkpDamageType);
            return View(IncidentDamage.ToList());
        }
    

    This is the full controller generated by EF

    using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Threading.Tasks; using System.Net; using System.Web; using System.Web.Mvc; using Abp.Authorization; using OE_Tenant.EntityFramework; using OE_Tenant.Incidents.Entity; using OE_Tenant.Web.Controllers;

    namespace OE_Tenant.Web.Areas.Incidents.Controllers { [AbpAuthorize] public class IncidentDamageController : OE_TenantControllerBase //: Controller { private IDBDbContext db = new IDBDbContext();

        // GET: Incidents/IncidentDamage
        public ActionResult Index()
        {
            var tenantId = AbpSession.TenantId;
            var IncidentDamage = db.IncidentDamage.Include(i => i.Incident).Include(i => i.LkpDamageSeverity).Include(i => i.LkpDamageType);
            return View(IncidentDamage.ToList());
        }
    
        // GET: Incidents/IncidentDamage/Details/5
        public ActionResult Details(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            IncidentDamage IncidentDamage = db.IncidentDamage.Find(id);
            if (IncidentDamage == null)
            {
                return HttpNotFound();
            }
            return View(IncidentDamage);
        }
    
        // GET: Incidents/IncidentDamage/Create
        public ActionResult Create()
        {
            ViewBag.IncidentId = new SelectList(db.Incident, "Id", "IncidentEvent");
            ViewBag.DamageSeverityId = new SelectList(db.LkpDamageSeverity, "Id", "DamageSeverity");
            ViewBag.DamageTypeId = new SelectList(db.LkpDamageType, "Id", "DamageType");
            return View();
        }
    
        // POST: Incidents/IncidentDamage/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see &lt;a class=&quot;postlink&quot; href=&quot;http://go.microsoft.com/fwlink/?LinkId=317598&quot;&gt;http://go.microsoft.com/fwlink/?LinkId=317598&lt;/a&gt;.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(Include = "Id,IncidentId,DamageTypeId,DamageDescription,DamageSeverityId,DamageAmount,IndexIdentity,TenantId,IsDeleted,DeletedUserId,DeletionTime,LastModificationTime,LastModifiedUserId,CreationTime,CreatorUserId")] IncidentDamage IncidentDamage)
        {
            if (ModelState.IsValid)
            {
                IncidentDamage.Id = Guid.NewGuid();
                db.IncidentDamage.Add(IncidentDamage);
                db.SaveChanges();
                return RedirectToAction("Index");
            }
    
            ViewBag.IncidentId = new SelectList(db.Incident, "Id", "Activity", IncidentDamage.IncidentId);
            ViewBag.DamageSeverityId = new SelectList(db.LkpDamageSeverity, "Id", "DamageSeverity", IncidentDamage.DamageSeverityId);
            ViewBag.DamageTypeId = new SelectList(db.LkpDamageType, "Id", "DamageType", IncidentDamage.DamageTypeId);
            return View(IncidentDamage);
        }
    
        // GET: Incidents/IncidentDamage/Edit/5
        public ActionResult Edit(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            IncidentDamage IncidentDamage = db.IncidentDamage.Find(id);
            if (IncidentDamage == null)
            {
                return HttpNotFound();
            }
            ViewBag.IncidentId = new SelectList(db.Incident, "Id", "Activity", IncidentDamage.IncidentId);
            ViewBag.DamageSeverityId = new SelectList(db.LkpDamageSeverity, "Id", "DamageSeverity", IncidentDamage.DamageSeverityId);
            ViewBag.DamageTypeId = new SelectList(db.LkpDamageType, "Id", "DamageType", IncidentDamage.DamageTypeId);
            return View(IncidentDamage);
        }
    
        // POST: Incidents/IncidentDamage/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see &lt;a class=&quot;postlink&quot; href=&quot;http://go.microsoft.com/fwlink/?LinkId=317598&quot;&gt;http://go.microsoft.com/fwlink/?LinkId=317598&lt;/a&gt;.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,IncidentId,DamageTypeId,DamageDescription,DamageSeverityId,DamageAmount,IndexIdentity,TenantId,IsDeleted,DeletedUserId,DeletionTime,LastModificationTime,LastModifiedUserId,CreationTime,CreatorUserId")] IncidentDamage IncidentDamage)
        {
            if (ModelState.IsValid)
            {
                db.Entry(IncidentDamage).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.IncidentId = new SelectList(db.Incident, "Id", "Activity", IncidentDamage.IncidentId);
            ViewBag.DamageSeverityId = new SelectList(db.LkpDamageSeverity, "Id", "DamageSeverity", IncidentDamage.DamageSeverityId);
            ViewBag.DamageTypeId = new SelectList(db.LkpDamageType, "Id", "DamageType", IncidentDamage.DamageTypeId);
            return View(IncidentDamage);
        }
    
        // GET: Incidents/IncidentDamage/Delete/5
        public ActionResult Delete(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            IncidentDamage IncidentDamage = db.IncidentDamage.Find(id);
            if (IncidentDamage == null)
            {
                return HttpNotFound();
            }
            return View(IncidentDamage);
        }
    
        // POST: Incidents/IncidentDamage/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(Guid id)
        {
            IncidentDamage IncidentDamage = db.IncidentDamage.Find(id);
            db.IncidentDamage.Remove(IncidentDamage);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
    

    }

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    In order to use data filters you should not create dbContext by yourself. Instead you should inject a repository, for example IRepository<YourEntity>, and then you need to use this repository to get data.

    If you want to continue in this way, you can check the library we used for data filters here <a class="postlink" href="https://github.com/jcachat/EntityFramework.DynamicFilters">https://github.com/jcachat/EntityFramew ... micFilters</a> but I suggest you to use IRepository.

    Thanks.