Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LoadFromCollection ordering nested table columns while filtering member info #1196

Open
ChristopherBrownie opened this issue Nov 29, 2023 · 7 comments
Assignees
Labels
7.1 Used for PR's targetting the develop7_1 branch enhancement New feature or request

Comments

@ChristopherBrownie
Copy link

When specifying member info to include, it seems the order of the members specified determines the order of the columns (which is great!).

The following gives me columns Id, FirstName, LastName in that order.

LoadFromCollection(items, c => {
    c.Members = new MemberInfo[]
    {
        x.GetProperty("Id"),
        x.GetProperty("FirstName")
        x.GetProperty("LastName")
    }
});

Now that #1052 has been fixed, adding in a EPPlusNestedTableColumn attribute to this produces unexpected ordering.

LoadFromCollection(items, c => {
    c.Members = new MemberInfo[]
    {
        x.GetProperty("Id"),
        x.GetProperty("FirstName")
        x.GetProperty("LastName")
        x.GetProperty("Residence")
    }
});

// With the residence property on the model looking like...
[EPPlusNestedTableColumn(HeaderPrefix = "Residence.")]
public virtual Residence Residence { get; set; }

The above produces columns in the order of Id, Residence.Address, FirstName, Residence.Id, LastName. The nested table columns have been scattered about while the main properties remain in the correct order.

I would expect to keep all residence columns together and display them in the position specified in the member info, which in this case is after LastName.

@swmal
Copy link
Contributor

swmal commented Nov 30, 2023

Thanks for reporting, we'll have a look at this.

@swmal swmal self-assigned this Nov 30, 2023
@swmal
Copy link
Contributor

swmal commented Nov 30, 2023

@ChristopherBrownie Just to make this clearer, can you give me the class definitions (or at least enough to replicate this issue), including the attributes, for the T class in LoadFromCollection<T> and the Residence class?

@ChristopherBrownie
Copy link
Author

ChristopherBrownie commented Nov 30, 2023

Sure thing, here's a simple version of the classes:

public class Person : BaseModel
{
    [Key]
    public int Id { get; set; }

    [ForeignKey(nameof(Residence))]
    public int? ResidenceSystemID { get; set; }

    [OfficeOpenXml.Attributes.EpplusNestedTableColumn(HeaderPrefix = "Residence.")]
    public virtual Residence Residence { get; set; }

    [Display(Name = "First Name")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    public int? Age { get; set; }
}

public class Residence : BaseModel
{
    [OfficeOpenXml.Attributes.EpplusIgnore]
    public virtual ICollection<Person> Residents { get; set; }

    public string Address { get; set; }
    
    public string City { get; set; }

    public string State { get; set; }
    
    public string Zip { get; set; }

    public string Type { get; set; }
}

public abstract class BaseModel
{
    [Key]
    public int Id { get; set; }

    public DateTime? CreatedDate { get; set; }

    public DateTime? LastEditDate { get; set; }
}

@swmal
Copy link
Contributor

swmal commented Dec 1, 2023

@ChristopherBrownie - I had a look at this, don't know which EPPlus/.NET version you are using, so tested with the latest EPPlus 7 code base under .NET 6. From what I can see the columns are (using the classes in your previous message):

  • Id
  • FirstName
  • LastName
  • Residence.Address
  • Residence.City
  • Residence.State
  • Residence.Zip
  • Residence.Type
  • Residence.Id
  • Residence.CreatedDate
  • Residence.LastEditDate

The first three are in the order as specified by the members sent in to the function. And since no other order was provided for the Residence properties are ordered as they were provided by .NET reflection (that's why the base class properties comes last). My recommendation would be that you specify the order you want using one of the following two options:

  1. Add the EPPlusTableColumn or the EPPlusNestedTableColumn attributes with the Order property set on all properties that you want to include. Then add the EPPlusIgnore attribute on properties that you don't want to include. Remove the members from the call to LoadFromCollection.
  2. Keep the members in the call to LoadFromCollection and add the EPPlusTableColumnSortOrder on class level on both classes. You must have it on the Person class too for this to work, even if the members are in the right order. See example
[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Id), nameof(FirstName), nameof(LastName), nameof(Residence)})]
public class Person : BaseModel
{

}

[EPPlusTableColumnSortOrder(Properties = new string[] { nameof(Id), nameof(CreatedDate), nameof(LastEditDate), nameof(Address), nameof(City), nameof(Zip), nameof(State), nameof(Type)})]
public class Residence : BaseModel
{

}

@ChristopherBrownie
Copy link
Author

Thanks for looking into this. You are correct I am in .NET 6 with EPPlus 7.0.2.

I seem to be getting inconsistent results on my end. Based on the feedback you provided for ordering properties, is the order of specified filtered members used to set the order of the columns? I assumed it was, but the wiki doesn't specify. It seems to work for me without a nested table column, but is this ordering just coincidental or an intended feature?

@swmal
Copy link
Contributor

swmal commented Dec 4, 2023

The member filter can be used to specify the order without the nested class, it is a very different internal logic in EPPlus for how to build up the columns when this attribute is added. Since the members filter only works on the outermost class the sortorder will be built up using a combination of the members and any other ordering specified by attributes. My advice in the previous message is based on how it works today, when I debugged this I noticed that in this particular case (when combined with a nested property) the "reflection-order" was used.

I will try to look deeper into this, ideally the order of the members should define the column order even if one of the properties is a nested class, if not possible we will update the docs/wiki. Until we have found a better solution for this, my advice is to define the sort order via attributes as described above if you are using any other attribute than EPPlusIgnore.

@swmal
Copy link
Contributor

swmal commented Dec 6, 2023

@ChristopherBrownie I have looked into this again today and we can make this more consistent in the next version. I'm working on a solution where any members provided via the MemberInfo[] will override the sort order as specified in attributes. Needs some more testing, for example when there are inheritance as in your example. Will ping you again when this is testable in our development nuget feed.

@swmal swmal added 7.1 Used for PR's targetting the develop7_1 branch enhancement New feature or request labels Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
7.1 Used for PR's targetting the develop7_1 branch enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants