Open Closed

Extending user functionality #519


0
doubledp created

Hi Halil,

I desperately need you help. As per my previous post [http://forum.aspnetboilerplate.com/viewtopic.php?f=5&t=580]), I am sure you realize that I am doing some sort of dashboard tool. I am pleased to announce that I can now retrieve the data successfully via SP with it still fitting into the way you are doing things within ASP.NET Zero.

A requirement has now been added that users should only see data as configured. Changing the SP is no problem as I just parameterized it.

What I would like to achieve is something very similar to how the roles are configured.

The database that I am working with via the SPs has consolidated data and unique result sets is identified via a module code, company code and a location.

This is what I have done so far: I have created a static list of modules names, very much the same as you have done with the static role names. I have created the basic application logic, presentation logic, domain logic for company, branches and locations, to dynamically add to them as needed

The idea behind branches is to allow the admin to setup a list with module and company combinations. (Similar to how the features work). This branch in turn is used as a selection within the location setup that the inherits the list of modules/company combinations, which is up for manipulation. (Similar to how the user defined permissions work). Finally multiple locations can then be added to the user, which inherits all of the modules/company combinations.

My thinking is that if things are done in this way, I would be able to access this setup in the same way I would access the other user information. Am I right in thinking that?

I have tried to look at how you do things but there are some places where I get lost. For example how does the static list of permissions get read for use within the permissionManager.

Please you expert guidance will be much appreciated. If you can please point me in the right direction and tell me what to look out for, what should be affected for this to slot into the ASP.NET Zero.


12 Answer(s)
  • 0
    hikalkan created
    Support Team

    Hi,

    Your requirement seems complicated in first sight. It's better to ask your implementation detail problems since for me it takes time to understand all and provide a complete solution :) Also, there may be alternative solutions which have pros and cons.

    So,

    ...for example how does the static list of permissions get read for use within the permissionManager.

    It's easy. Since ABP is open source, you can find these also yourself.

    1. ABP defines a abstract base class which you implement to define permissions (we could make an interface too): <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/src/Abp/Authorization/AuthorizationProvider.cs">https://github.com/aspnetboilerplate/as ... rovider.cs</a> So, you implement AuthorizationProvider and define permissions.

    2. You implement and register it to the configuration (like that: <a class="postlink" href="https://github.com/aspnetzero/aspnet-zero/blob/master/src/MyCompanyName.AbpZeroTemplate.Application/AbpZeroTemplateApplicationModule.cs#L17">https://github.com/aspnetzero/aspnet-ze ... ule.cs#L17</a>) This should be done in PreInitialize of your module, as you know.

    3. ABP, then initializes permission manager (<a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/src/Abp/AbpKernelModule.cs#L88">https://github.com/aspnetboilerplate/as ... ule.cs#L88</a>) on PostInitialize.

    4. permission manager (which is singleton) collects permissions from all provders (<a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/src/Abp/Authorization/PermissionManager.cs#L43">https://github.com/aspnetboilerplate/as ... ger.cs#L43</a>) and stores in a list.

    5. When you want to get permissions, it gets from the list (<a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/blob/master/src/Abp/Authorization/PermissionManager.cs#L62">https://github.com/aspnetboilerplate/as ... ger.cs#L62</a>).

  • 0
    doubledp created

    Thank you very much :D

    Like I said before, I don't want you to provide me with a solution. I don't think it is fair and also I won't be learning anything. :geek:

    I can definitely work with the direction you have provided me and I am forever in your debt with regards to that.

    I have looked at the code within the ABP solution, but I must be honest, I get a bit lost at times :oops:

    All I know is that this is most well written architecture I have ever seen and would very much like to stick this brilliance.

  • 0
    doubledp created

    Hi Halil,

    How to I register my startup configuration so I can do the following:

    Configuration.Mapping.Providers.Add<AppMappingProvider>();
    

    I have noticed that the other configurations is part of the AbpConfigurationStartup class.

    Is there a way to extend this to include my startup configuration class?

  • 0
    hikalkan created
    Support Team

    Hi,

    Not like

    Configuration.Mapping.Providers.Add&lt;AppMappingProvider&gt;();
    

    But can do like

    Configuration.**Modules.YourModuleName().**Mapping.Providers.Add&lt;AppMappingProvider&gt;();
    

    See documentation, I think you will understand it:

    <a class="postlink" href="http://www.aspnetboilerplate.com/Pages/Documents/Startup-Configuration#DocCreateModuleConfig">http://www.aspnetboilerplate.com/Pages/ ... duleConfig</a>

    Write me if you can not.

  • 0
    doubledp created

    Hi,

    Yes I realized that it was the wrong approach.

    I created a separate project for ApplicationTemplate.CustomModule and included it as part of the ApplicationTemplate solution. I then added that ApplicationTemplate.CustomModule project as a reference to ApplicationTemplate.Core but I am unable to resolve to Configuration.Modules.ApplicationTemplateCustomModule()

    Please help, if you have just do the shells for this type of implementation of a separate project using the provider mechanism? I feel very depressed at this stage about not getting it right :cry:

  • 0
    hikalkan created
    Support Team

    Don't be depressed, please. I really want that you success :)

    Configuration.Modules.ApplicationTemplateCustomModule()

    Here, ApplicationTemplateCustomModule() is an extension method, right? In order to use this extension method, you need to reference to the project (dll) which defines the method. Also, you need to add namespace import (using ... statement).

    Have you done these? What's the problem?

  • 0
    doubledp created

    Sorry Halil, it is because I have been struggling for so long. I have done all of those things, except for the one thing...

    And that is not including the 'using ApplicationTemplate.CustomModule.Configuration'. Such a stupid mistake :oops:

    Thank you for reminding me of that. :D

    What do you think is better though?

    Adding Configuration.Modules.ApplicationTemplateCustomModule() in ApplicationTemplateCoreModule class that is part of the ApplicationTemplate.Core project and adding the [DependsOn(typeof(ApplicationTemplateCustomModule))] or Adding Configuration.Modules.ApplicationTemplateCustomModule() inside the ApplicationTemplateCustomModule class that is part of the ApplicationTemplate.CustomModule project and adding the [DependsOn(typeof(ApplicationTemplateCoreModule))]

    Pardon the many questions, I would just like to use the framework and the template project in the way it's been intended to be used, instead of 'hacking' things together to make it work.

  • 0
    hikalkan created
    Support Team

    Pardon the many questions, I would just like to use the framework and the template project in the way it's been intended to be used, instead of 'hacking' things together to make it work

    Thank you for gently approach :)

    I think main application should depend on a custom module, theoretically. Thus, the module can be re-usable between different apps. But it may depend for your purpose.

  • 0
    doubledp created

    I am making some headroom with this. Thank you very much for all the help so far :D

    Can you please explain how the Discriminator column on the Permissions table work? I have looked and cannot find where it has been defined within the domain entity.

    I suspect it has to do with the PermissonSetting, but I can't find where the value gets derived to RolePermissionSetting or UserPermissionSetting.

    How does it mapped from PermissionSetting to the Discriminator column? Where is the Discriminator column defined for the database migration?

  • 0
    hikalkan created
    Support Team

    Hi,

    Discriminator is a special column that is used by EntityFramework to distinguish entities in the same table. See "Table per Hierarchy": <a class="postlink" href="http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph">http://weblogs.asp.net/manavi/inheritan ... rarchy-tph</a>

  • 0
    doubledp created

    Wow, I had no idea that Entity Framework is this powerful! I have no much to learn... :geek:

    I implemented accordingly but now I have a problem when adding the migration, please see below:

    using Abp.Authorization.Users;
    using Abp.Domain.Entities;
    using Abp.Domain.Entities.Auditing;
    using Abp.MultiTenancy;
    using Caerus.Autoline.Authorization.Branches;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace Caerus.Authorization.Branches
    {
        public class Branch<TTenant, TUser> : BranchBase, IFullAudited<TUser>, IAudited<TUser>, IMustHaveTenant<TTenant, TUser>, IEntity<int>
            where TUser : AbpUser<TTenant, TUser>
            where TTenant : AbpTenant<TTenant, TUser>
        {
            public const int MaxDisplayNameLength = 50;
    
            [ForeignKey("TenantId")]
            public virtual TTenant Tenant { get; set; }
    
            [Required]
            [MaxLength(MaxDisplayNameLength)]
            public virtual string DisplayName { get; set; }
    
            [ForeignKey("BranchId")]
            public virtual ICollection<BranchMappingSetting> Mappings { get; set; }
    
            public TUser CreatorUser { get; set; }
    
            public TUser DeleterUser { get; set; }
    
            public TUser LastModifierUser { get; set; }
    
            public Branch()
            {
                Name = Guid.NewGuid().ToString("N");
            }
    
            public Branch(int tenantId, string displayName)
            {
                TenantId = tenantId;
                DisplayName = DisplayName;
            }
    
            public Branch(int tenantId, string name, string displayName)
                : this(tenantId, displayName)
            {
                Name = name;
            }
    
            public override string ToString()
            {
                return string.Format("[Branch {0}, Name={1}]", Id, Name);
            }
        }
    }
    
    using Abp.Domain.Entities;
    using Abp.Domain.Entities.Auditing;
    using Caerus.Autoline.Authorization.Branches;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
    namespace Caerus.Authorization.Branches
    {
        [Table("Branches")]
        public abstract class BranchBase : FullAuditedEntity, IBranch<int>, IMustHaveTenant
        {
            public const int MaxBranchNameLength = 50;
    
            public virtual int TenantId { get; set; }
    
            [Required]
            [StringLength(MaxBranchNameLength)]
            public virtual string Name { get; set; }
    
        }
    }
    
    using Abp.Zero.EntityFramework;
    using Caerus.Authorization.Branches;
    using Caerus.Authorization.Companies;
    using Caerus.Authorization.Locations;
    using Caerus.Authorization.Roles;
    using Caerus.Authorization.Users;
    using Caerus.MultiTenancy;
    using Caerus.Storage;
    using System.Data.Common;
    using System.Data.Entity;
    
    namespace Caerus.EntityFramework
    {
        public class CaerusDbContext : AbpZeroDbContext<Tenant, Role, User>
        {
            /* Define an IDbSet for each entity of the application */
    
            public virtual IDbSet<BinaryObject> BinaryObjects { get; set; }
    
            public virtual IDbSet<Branch<Tenant, User>> Branches { get; set; }
    
            /* Setting "Default" to base class helps us when working migration commands on Package Manager Console.
             * But it may cause problems when working Migrate.exe of EF. ABP works either way.         * 
             */
            public CaerusDbContext()
                : base("Caerus")
            {
    
            }
    
            /* This constructor is used by ABP to pass connection string defined in CaerusDataModule.PreInitialize.
             * Notice that, actually you will not directly create an instance of CaerusDbContext since ABP automatically handles it.
             */
            public CaerusDbContext(string nameOrConnectionString)
                : base(nameOrConnectionString)
            {
    
            }
    
            /* This constructor is used in tests to pass a fake/mock connection.
             */
            public CaerusDbContext(DbConnection dbConnection)
                : base(dbConnection, true)
            {
    
            }
        }
    }
    
  • 0
    doubledp created

    Is this because the base class that I inherit from is in the custom module assembly and the entity class is in the core module assembly?

    Or do I need to add a reference to Entity Framework in my custom module assembly?