Base solution for your next web application
Open Closed

Suggestion : Abstract OrganizationUnit #785


User avatar
0
finallyfreeguy created

Abstract version of OrganizationUnit would be nice...

/// <summary>
    /// Represents an organization unit (OU).
    /// </summary>
    public abstract class OrganizationUnitBase<TSelf> : FullAuditedEntity<long>, IMayHaveTenant
    {
        /// <summary>
        /// Maximum length of the <see cref="DisplayName"/> property.
        /// </summary>
        public const int MaxDisplayNameLength = 128;

        /// <summary>
        /// Maximum depth of an UO hierarchy.
        /// </summary>
        public const int MaxDepth = 16;

        /// <summary>
        /// Length of a code unit between dots.
        /// </summary>
        public const int CodeUnitLength = 5;

        /// <summary>
        /// Maximum length of the <see cref="Code"/> property.
        /// </summary>
        public const int MaxCodeLength = MaxDepth * (CodeUnitLength + 1) - 1;

        /// <summary>
        /// TenantId of this entity.
        /// </summary>
        public virtual int? TenantId { get; set; }

        /// <summary>
        /// Parent <see cref="OrganizationUnitBase"/>.
        /// Null, if this OU is root.
        /// </summary>
        [ForeignKey("ParentId")]
        public virtual TSelf Parent { get; set; }

        /// <summary>
        /// Parent <see cref="OrganizationUnitBase"/> Id.
        /// Null, if this OU is root.
        /// </summary>
        public virtual long? ParentId { get; set; }

        /// <summary>
        /// Hierarchical Code of this organization unit.
        /// Example: "00001.00042.00005".
        /// This is a unique code for a Tenant.
        /// It's changeable if OU hierarch is changed.
        /// </summary>
        [Required]
        [StringLength(MaxDisplayNameLength)]
        public virtual string Code { get; set; }

        /// <summary>
        /// Display name of this role.
        /// </summary>
        [Required]
        [StringLength(MaxDisplayNameLength)]
        public virtual string DisplayName { get; set; }

        /// <summary>
        /// Children of this OU.
        /// </summary>
        public virtual ICollection<TSelf> Children { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="OrganizationUnitBase"/> class.
        /// </summary>
        public OrganizationUnitBase()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OrganizationUnitBase"/> class.
        /// </summary>
        /// <param name="tenantId">Tenant's Id or null for host.</param>
        /// <param name="displayName">Display name.</param>
        /// <param name="parentId">Parent's Id or null if OU is a root.</param>
        public OrganizationUnitBase(int? tenantId, string displayName, long? parentId = null)
        {
            TenantId = tenantId;
            DisplayName = displayName;
            ParentId = parentId;
        }

        /// <summary>
        /// Creates code for given numbers.
        /// Example: if numbers are 4,2 then returns "00004.00002";
        /// </summary>
        /// <param name="numbers">Numbers</param>
        public static string CreateCode(params int[] numbers)
        {
            if (numbers.IsNullOrEmpty())
            {
                return null;
            }

            return numbers.Select(number => number.ToString(new string('0', CodeUnitLength))).JoinAsString(".");
        }

        /// <summary>
        /// Appends a child code to a parent code. 
        /// Example: if parentCode = "00001", childCode = "00042" then returns "00001.00042".
        /// </summary>
        /// <param name="parentCode">Parent code. Can be null or empty if parent is a root.</param>
        /// <param name="childCode">Child code.</param>
        public static string AppendCode(string parentCode, string childCode)
        {
            if (childCode.IsNullOrEmpty())
            {
                throw new ArgumentNullException("childCode", "childCode can not be null or empty.");
            }

            if (parentCode.IsNullOrEmpty())
            {
                return childCode;
            }

            return parentCode + "." + childCode;
        }

        /// <summary>
        /// Gets relative code to the parent.
        /// Example: if code = "00019.00055.00001" and parentCode = "00019" then returns "00055.00001".
        /// </summary>
        /// <param name="code">The code.</param>
        /// <param name="parentCode">The parent code.</param>
        public static string GetRelativeCode(string code, string parentCode)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException("code", "code can not be null or empty.");
            }

            if (parentCode.IsNullOrEmpty())
            {
                return code;
            }

            if (code.Length == parentCode.Length)
            {
                return null;
            }

            return code.Substring(parentCode.Length + 1);
        }

        /// <summary>
        /// Calculates next code for given code.
        /// Example: if code = "00019.00055.00001" returns "00019.00055.00002".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string CalculateNextCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException("code", "code can not be null or empty.");
            }

            var parentCode = GetParentCode(code);
            var lastUnitCode = GetLastUnitCode(code);

            return AppendCode(parentCode, CreateCode(Convert.ToInt32(lastUnitCode) + 1));
        }

        /// <summary>
        /// Gets the last unit code.
        /// Example: if code = "00019.00055.00001" returns "00001".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string GetLastUnitCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException("code", "code can not be null or empty.");
            }

            var splittedCode = code.Split('.');
            return splittedCode[splittedCode.Length - 1];
        }

        /// <summary>
        /// Gets parent code.
        /// Example: if code = "00019.00055.00001" returns "00019.00055".
        /// </summary>
        /// <param name="code">The code.</param>
        public static string GetParentCode(string code)
        {
            if (code.IsNullOrEmpty())
            {
                throw new ArgumentNullException("code", "code can not be null or empty.");
            }

            var splittedCode = code.Split('.');
            if (splittedCode.Length == 1)
            {
                return null;
            }

            return splittedCode.Take(splittedCode.Length - 1).JoinAsString(".");
        }
    }

We can use it in many entities like ProductCategory, CustomerCategory, UserCategory...

public class ProductOrganization : OrganizationUnitBase<ProductOrganization>
    {
        public ProductOrganization()
        {
        }

        public ProductOrganization(int? tenantId, string displayName, long? parentId = null)
            : base(tenantId, displayName, parentId)
        {

        }
    }

7 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    Making entities abstract in the framework leads to a complex structure. User, Role and Tenant are abstract currently, and all code using these should also be abstract. That's why we decided to not make OU abstract. Instead, you can use different patterns to extend it.

  • User Avatar
    0
    finallyfreeguy created

    In EF tph or tpt have advantages and disadvantages therefore I dont want to use table hierarchy if I dont have a good reason. For learning purpose could you explain why abstract entities leads framework more complex?

  • User Avatar
    0
    sudmanche created

    So what is the suggested way of extending OU? I would like to add a extra property, such as OUType int or similar.

  • User Avatar
    0
    hikalkan created
    Support Team

    This document may help you: <a class="postlink" href="https://www.aspnetzero.com/Documents/Extending-Existing-Entities">https://www.aspnetzero.com/Documents/Ex ... g-Entities</a> It's same for module-zero since AspNet Zero uses it.

  • User Avatar
    0
    klainer created

    Hi what if I want to make query based on extended attribute ?

    When I do this:

    private readonly IRepository<MYOrganizationUnit, long> _organizationUnitRepository;
    

    I need this:

    _organizationUnitRepository.FirstOrDefault( x =>  x.custmAttribute = "xxx");
    

    I got error, dependency is not satisfied

    Thanks

  • User Avatar
    0
    ismcagdas created
    Support Team

    Can you share the stacktrace of this exception ?

  • User Avatar
    0
    hikalkan created
    Support Team

    Have you added your new entity to your dbcontext?