Base solution for your next web application
Starts in:
01 DAYS
01 HRS
01 MIN
01 SEC

Activities of "OutdoorEd"

I would, respectfully, disagree with the assertion that ANZ is not a professional level product. What they sell is source code plain and simple and they are up front about what they are selling. Having built a lot of software over the past 20 years and paid other developers a lot of money to write code for me, there is no way that I could get the breadth of what the ANZ folks have built by paying an outside developer just $2500. From scratch would be in the Tens of Thousands of dollars US range. I am happy for what I have paid for since it was much less expensive than anything else I could have paid a developer to code. The quality of the code is also very good. I've had multiple top developers work with it and they have been impressed with the code quality.

I too wish that upgrading was something that was an option because when they release a improvement, I am left in the dust which is disappointing. But that does not mean that ANZ is not a professional grade product. In moving from the MVC Jquery version to the Net Core Jquery version I spent a lot of time documenting where my code fit in with theirs to make an upgrade process more feasible. I believe that there are some things that they could do with their code that would make it easier to upgrade:

  • Things like making certain classes partial so that we can derive our own separate class files. Case in point - the DbContext.cs. I could have all my entity code in a separate class and simply overwrite the ANZ DbContext file from a new version if I didn't have to put all my stuff in their DbContext.cs (Of course I could do this myself but then I'd still have to go in and edit their new DbContext each time to be a partial every time).
  • Publishing a list of code changed files for each version number would make it a lot easier to know where to look than dnard82's BeyondCompare check of every single file.
  • Some things are in Nuget packages, and knowing what version of each Nuget package is associated with a new release would also be helpful.
  • Finally, publishing any SQL Migration Scripts for backend database changes (like when they went from datetime to datetime2 would really be useful for me to be able to keep my database tables in line with the FullyAuditedEntity. (Yes that is in the .Migrator, but it should be part of the documentation for each version)

There are several things to understand about ASPNETZERO. It is built as a framework to create your Web app on top off and as an upgradable product. They've done a ton of the heavy lifting for you with authentication, authorization, etc. They don't say that there is an upgrade path (although there are some ways to branch that you can find in this forum).

In the case of 6.9 to 7.0 it is a very big UI change since they updated the underlying Metronic theme system (www.keenthemes.com) from the 5.x version to 6.0. That means you would need to change lots in the layout.cshtml files across the entire application.

Answer

Very helpful post. One thing I would add for Telerik ASP.NET Core Jquery with Netzero Core 2.2

I found that the Telerik js files need to be in the header rather than at the bottom of the page to work so on the NetZero /Area/Views/Layouts/__Layout.cshtml

I moved the script code up into the header AND there is a Netzero RenderScript flag in the footer which is set to false by default, change this to true when moving the Script Code to the Header

@RenderSection("Scripts", true)

Here is the Code I moved to the header

`

<!-- Dynamic scripts of ABP system (They are created on runtime and can not be bundled) -->
<script src="@(ApplicationPath)AbpServiceProxies/GetAll?v=@(AppTimes.StartupTime.Ticks)" type="text/javascript"></script>
<script src="@(ApplicationPath)AbpScripts/GetScripts?v=@(AppTimes.StartupTime.Ticks)" type="text/javascript"></script>

<script type="text/javascript">
    abp.localization.currentCulture = $.extend({}, abp.localization.currentCulture, { displayNameEnglish: '@CultureInfo.CurrentUICulture.EnglishName' });
    moment.locale('@(CultureHelper.UsingLunarCalendar ? "en": CultureInfo.CurrentUICulture.Name )'); //Localizing moment.js
</script>

<script src="@(ApplicationPath)view-resources/Areas/Admin/Views/_Bundles/signalr.bundle.min.js" asp-append-version="true"></script>

<script abp-src="/view-resources/Areas/Admin/Views/_Bundles/common-scripts.js" asp-append-version="true"></script>
<script abp-src="/view-resources/Areas/Admin/Views/_Bundles/app-common-scripts.js" asp-append-version="true"></script>
<script abp-src="/view-resources/Areas/Admin/Views/Layout/_Header.js" asp-append-version="true"></script>
<script src="@(ApplicationPath)view-resources/Areas/Admin/Views/Layout/_ThemeSelectionPanel.js" asp-append-version="true"></script>

@if (isChatEnabled)
{
    <script src="@(ApplicationPath)view-resources/Areas/Admin/Views/Layout/_ChatBar.js" asp-append-version="true"></script>
    <script src="@(ApplicationPath)Common/Scripts/Chat/chat.signalr.js" asp-append-version="true"></script>
}

<script src="[email protected]_Validation_Localization" asp-append-version="true"></script>
<script src="[email protected]_Select_Localization" asp-append-version="true"></script>
<script src="[email protected]_Timeago_Localization" asp-append-version="true"></script>
<script src="[email protected]_Localization" asp-append-version="true"></script>

<!--end::Base Scripts -->
<!--begin::Telerik Scripts -->
<!-- NOTE Jquery already loaded as part of NetZero Bundles above-->

** ** `

I appreciate all the fantastic work that the ASP.NET Zero Team has put into this framework. CSS Theme development is it's own specialty which requires experience in graphic design and UI. I've built dozens of Web apps and while I'm a good programmer, I'm not a graphic designer and I rely on commercial Theme packages to deliver a really good looking final package which I could never build myself. With a framework this complex and robust, I look to the ASP.NET Zero Team to deliver excellent NET Code and understand the need to utilize CSS Themes.

I had this same issue while developing locally. I spent 8 hours on Saturday coding, building and testing and 6 hours on Sunday - absolutely no problems. Then after one build it threw this same error. I had never touched any code related to the friend service. I ended up rebooting my computer (which showed a memory error on shutdown). When I restarted and built my solution again, the problem was gone. Three more hours of building and testing - no problem. Memory leak?

Answer

It is working now. Thanks.

Answer

Thanks for the reply. In addition to the aspx Page_Load code

protected void Page_Load(object sender, EventArgs e)
        {
            if (!IocManager.Instance.IocContainer.Resolve<IPermissionChecker>().IsGranted("Pages.Routestore.Routes.Routelist"))
            {
                Response.Redirect("/Views/Error/NotAuthorized.html");
            }
              //Show report otherwise
        }
    }

I added these in as well and it now "respects" Role Permissions.

<ins>AppPermissions.cs</ins>

public const string Pages_Routestore_Routes_Routelist = "Pages.Routestore.Routes.Routelist";

<ins>AppAuthorizationProvider.cs</ins>

pages.CreateChildPermission(AppPermissions.Pages_Routestore_Routes_Routelist, L("Routelist"));

<ins>Tenant.xml</ins>

<text name="Routelist" value="Route List" />

I do see the Permissions Checkbox for this role and when I select it I see that reflected in the database Abp_AppPermissions table

5	Pages.Routestore.Routes.Routelist	True	2018-08-13 04:17:59.800	2	NULL	3	UserPermissionSetting	1
Answer

This approach works for me to prevent anonymous users from logging in. I need to be able to control access via ABP Roles and can't see how to control that from the PageLoad on the ASPX page.

public partial class RouteList : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IocManager.Instance.IocContainer.Resolve<IPermissionChecker>().IsGranted("Pages.Routestore.Routes.Routelist"))
            {
                Response.Redirect("/Views/Error/NotAuthorized.html");
            }
              //Show report otherwise
        }
    }

In working with an existing database for the MVC Jquery version I used Simon Hughes' EntityFramework Reverse POCO Generator in a separate project just to create the POCOs. One of the nice things in Simon's template is that you can control things like the namespace so even though it was in a separate project, I could generate the correct namespace for my project.

I worked with a wonderful outside developer who laid out the architecture for this (just to admit that I am not smart enough to come up with this myself). Here is how one class was created for my Incident database application. It uses Repositories and separate Configuration files.

I am only showing one database field from the table all the way through and don't show you any of the relationships to other tables to shorten the code. I used generic MVC scaffolding to create the skeleton of the Controller and all the CRUD Views and then replaced things in the Controller with the Repositories.


FOLDER: OE_Tenant.Core\Incidents\Entity\IncidentFile.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;

namespace OE_Tenant.Incidents.Entity { [Table("idb_IncidentFile")] public partial class IncidentFile : FullAuditedEntity<Guid>, IMustHaveTenant { public int FileTypeId { get; set; } } }

FOLDER: OE_Tenant.Core\Incident\Repos\IIncidentFileRepository.cs

using Abp.Domain.Repositories; using OE_Tenant.Incidents.Entity; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

namespace OE_Tenant.Incidents.Repos { public interface IIncidentFileRepository : IRepository<IncidentFile, Guid> { } }

FOLDER: OE_Tenant.EntityFramework\EntityConfigurations\IncidentFileConfiguration.cs

using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; using OE_Tenant.Incidents.Entity;

namespace OE_Tenant.EntityConfigurations { public class IncidentFileConfiguration : EntityTypeConfiguration<IncidentFile> { public IncidentFileConfiguration() { // Primary Key this.HasKey(t => t.Id);

        // Table & Column Configuration
        this.ToTable("idb_IncidentFile");
        this.Property(t => t.Id).HasColumnName("Id");
        this.Property(t => t.FileTypeId).HasColumnName("FileTypeId");
        this.Property(t => t.TenantId).HasColumnName("TenantId");
    }
}

}


FOLDER: OE_Tenant.EntityFramework\Repos\IncidentFileRepository.cs

using OE_Tenant.EntityFramework.Repositories; using OE_Tenant.Incidents.Entity; using OE_Tenant.Incidents.Repos; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Abp.EntityFramework; using OE_Tenant.EntityFramework;

namespace OE_Tenant.Repos { public class IncidentFileRepository : OE_TenantRepositoryBase<IncidentFile, Guid>, IIncidentFileRepository { public IncidentFileRepository(IDbContextProvider<OE_TenantDbContext> dbContextProvider) : base(dbContextProvider) { } } }


FOLDER: OE_Tenant.EntityFramework\OE_TenantDbContext.cs

using System.Data.Common; using System.Data.Entity; using Abp.Zero.EntityFramework; using OE_Tenant.Authorization.Roles; using OE_Tenant.Authorization.Users; using OE_Tenant.Chat; using OE_Tenant.Friendships; using OE_Tenant.MultiTenancy; using OE_Tenant.Storage; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; using EntityFramework.Functions; using OE_Tenant.Incidents.Entity; //Added for External Configuration Files using OE_Tenant.EntityConfigurations; //Added for Dynamic Filter using EntityFramework.DynamicFilters; using Abp.Domain.Entities;

namespace OE_Tenant.EntityFramework { public class OE_TenantDbContext : AbpZeroDbContext<Tenant, Role, User> { public virtual DbSet<IncidentFile> IncidentFile { get; set; }

public OE_TenantDbContext() : base("Default") { //Disable initializer to disable Migrations Database.SetInitializer<OE_TenantDbContext>(null); this.Configuration.LazyLoadingEnabled = false; this.Configuration.ProxyCreationEnabled = false; }

    public OE_TenantDbContext(string nameOrConnectionString)
        : base(nameOrConnectionString)
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public OE_TenantDbContext(DbConnection existingConnection)
        : base(existingConnection, false)
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public OE_TenantDbContext(DbConnection existingConnection, bool contextOwnsConnection)
        : base(existingConnection, contextOwnsConnection)
    {
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Added - NEEDS TO BE IN TO PREVENT MUSTHAVETENANT ERROR
        base.OnModelCreating(modelBuilder);

        modelBuilder.Configurations.Add(new IncidentFileConfiguration());

     }
}

}


FOLDER: OE_Tenant.Web\Areas\Incidents\Models\Dto\IncidentFileDto.cs

using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Web.Mvc; using Abp.Application.Services.Dto; using Abp.AutoMapper; using OE_Tenant.Incidents.Entity;

namespace OE_Tenant.Web.Areas.Incidents.Models.Dto { [AutoMapTo(typeof(IncidentFile))] [AutoMapFrom(typeof(IncidentFile))] public class IncidentFileDto : FullAuditedEntityDto<Guid> { [Display(Name = "File Type", Prompt = "Select the File Type")] [Required(ErrorMessage = "Please select a File Type")] public int FileTypeId { get; set; } } }


FOLDER: OE_Tenant.Web\Areas\Incidents\ViewModels\IncidentFileViewModel.cs

using System; using Abp.AutoMapper; using OE_Tenant.Incidents.Entity; using OE_Tenant.Web.Areas.Configuration.ViewModels;

namespace OE_Tenant.Web.Areas.Incidents.ViewModels { [AutoMapFrom(typeof(IncidentFile))] [AutoMapTo(typeof(IncidentFile))] public class IncidentFileViewModel { public Guid Id { get; set; } public int FileTypeId { get; set; } public int TenantId { get; set; } } }


FOLDER: OE_Tenant.Web\Areas\Incidents\Controllers\IncidentFileController.cs

using System; using System.Collections; using System.Data.Entity; using System.Threading.Tasks; using System.Net; using System.Web.Mvc; using OE_Tenant.Incidents.Entity; using OE_Tenant.Web.Controllers; using Abp.Runtime.Validation; using OE_Tenant.Incidents.Repos; using Abp.Web.Mvc.Authorization; using OE_Tenant.Authorization; using OE_Tenant.Web.Extensions; using OE_Tenant.Web.Areas.Incidents.ViewModels; using OE_Tenant.Web.Areas.Incidents.Models.Dto; using Abp.AutoMapper; using OE_Tenant.Extensions; using System.Linq;

namespace OE_Tenant.Web.Areas.Incidents.Controllers { [AbpMvcAuthorize(AppPermissions.Pages_Files)] public class FilesController : OE_TenantControllerBase { private readonly IIncidentFileRepository _repoIncidentFile; private readonly ILkpFileTypeRepository _repoLkpFileType;

    public FilesController(
        IIncidentFileRepository repoIncidentFile,
        ILkpFileTypeRepository repoLkpFileType
    )
    {
        _repoIncidentFile = repoIncidentFile;
        _repoLkpFileType = repoLkpFileType;
    }

    [AbpMvcAuthorize(AppPermissions.Pages_Files_Index)]
    // GET: Incidents/IncidentFiles
    public ActionResult Index()
    {
        return View();
    }

    [AbpMvcAuthorize(AppPermissions.Pages_Files_Details)]
    // GET: Incidents/IncidentFiles/Details/5
    public async Task&lt;ActionResult&gt; Details(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }
        IncidentFile incidentFile = await _repoIncidentFile.GetAllIncluding(i => i.LkpFileType).SingleOrDefaultAsync(i => i.Id == id.Value);

        if (incidentFile == null)
        {
            return HttpNotFound();
        }

        var model = incidentFile.MapTo&lt;IncidentFileDto&gt;();

        return View(model);
    }

    [AbpMvcAuthorize(AppPermissions.Pages_Files_Create)]
    // GET: Incidents/IncidentFiles/Create
    public async Task&lt;ActionResult&gt; Create()
    {
        ViewBag.FileTypeId = new SelectList(await _repoLkpFileType.GetAllListAsync(), "Id", "FileType");
        return View();
    }

    // POST: Incidents/IncidentFiles/Create
    [DisableValidation]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task&lt;ActionResult&gt; Create(IncidentFileDto model)
    {
        if (ModelState.IsValid)
        {
            var incidentFileEntity = model.MapTo&lt;IncidentFile&gt;();
            await _repoIncidentFile.InsertAsync(incidentFileEntity);
            return RedirectToAction("Index");
        }
       ViewBag.FileTypeId = new SelectList(await _repoLkpFileType.GetAllListAsync(), "Id", "FileType");
        return View(model);
    }

    [AbpMvcAuthorize(AppPermissions.Pages_Files_Edit)]
    // GET: Incidents/IncidentFiles/Edit/5
    public async Task&lt;ActionResult&gt; Edit(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        IncidentFile incidentFile = await _repoIncidentFile.GetAsync(id.Value);

        if (incidentFile == null)
        {
            return HttpNotFound();
        }

        ViewBag.FileTypeId = new SelectList(await _repoLkpFileType.GetAllListAsync(), "Id", "FileType", incidentFile.FileTypeId);
        var model = incidentFile.MapTo&lt;IncidentFileDto&gt;();

        return View(model);
    }

    // POST: Incidents/IncidentFiles/Edit/5
    [DisableValidation]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task&lt;ActionResult&gt; Edit(IncidentFileDto model)
    {
        if (ModelState.IsValid)
        {
            IncidentFile incidentFile = await _repoIncidentFile.GetAsync(model.Id);

            incidentFile.FileTypeId = model.FileTypeId;

            await _repoIncidentFile.UpdateAsync(incidentFile);
            return RedirectToAction("Index");
        }

        ViewBag.FileTypeId = new SelectList(await _repoLkpFileType.GetAllListAsync(), "Id", "FileType", model.FileTypeId);
        return View(model);

    }

    [AbpMvcAuthorize(AppPermissions.Pages_Files_Delete)]
    // GET: Incidents/IncidentFiles/Delete/5
    public async Task&lt;ActionResult&gt; Delete(Guid? id)
    {
        if (id == null)
        {
            return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
        }

        IncidentFile incidentFile = await _repoIncidentFile.GetAllIncluding(i => i.LkpFileType).SingleOrDefaultAsync(i => i.Id == id.Value);

        if (incidentFile == null)
        {
            return HttpNotFound();
        }

        var model = incidentFile.MapTo&lt;IncidentFileDto&gt;();

        return View(model);

    }

    // POST: Incidents/IncidentFiles/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task&lt;ActionResult&gt; DeleteConfirmed(Guid id)
    {
        await _repoIncidentFile.DeleteAsync(id);
        return RedirectToAction("Index");
    }

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

}

Metronic is the underlying ASPNETZERO theme using CSS & JS. It's built by a separate company, <a class="postlink" href="http://www.keenthemes.com">www.keenthemes.com</a> and licensed to ASPNETZERO. Because the Metronic theme features are huge (as you can see if you look at their demos) there are lots of things that aren't necessarily used in ASPNETZERO.

Some of the modal dialogs and edit boxes in the backend are coming from Metronic. It would be really helpful if ASPNETZERO would give us a whitelist of the specific JS libraries that are required and then users could remove unnecessary components. It's probably too hard to identify the CSS to be removed.

Showing 21 to 30 of 45 entries