Search code examples
c#listlinqimplicit-conversionexplicit-conversion

C# Object List Type Change based on condition


I am developing a complex application on .NET 6 using Microsoft Blazor. There are many classes and many functions manipulating List<Object> with different way, but also there are cases where the logic is the same but the <Object> class of List may vary.

For example, in the following code there is linq joins:

List<DataModel_STG_Table_Snapshot_Release_Ind> dataModel_STG_Table_Snapshot_Release_Inds = _dbContext.DataModel_STG_Table_Snapshot_Releases_Ind.Where(x => x.datamodel_id == y).ToList();
List<DataModel_STG_Table_Snapshot_Mapping_Ind> dataModel_STG_Table_Snapshot_Mapping_Inds = _dbContext.DataModel_STG_Table_Snapshot_Mappings_Ind.Where(x => x.datamodel_id == y).ToList();

var table_leftoutrtjoin = (
                from left in dataModel_STG_Table_Snapshot_Release_Inds
                join right in dataModel_STG_Table_Snapshot_Mapping_Inds
                on new { left.datamodel_stg_table_schm, left.datamodel_stg_table_name }
                equals new { right.datamodel_stg_table_schm, right.datamodel_stg_table_name }
                into ot
                from otnew in ot.DefaultIfEmpty()
                select new
                {
                    datamodel_stg_table_schm_raw = left.datamodel_stg_table_schm,
                    datamodel_stg_table_name_raw = left.datamodel_stg_table_name,

                    datamodel_stg_table_schm_ind = otnew?.datamodel_stg_table_schm,
                    datamodel_stg_table_name_ind = otnew?.datamodel_stg_table_name
                }).ToList();
                  

var table_rightouterjoin = (
                from left in dataModel_STG_Table_Snapshot_Mapping_Inds
                join right in dataModel_STG_Table_Snapshot_Release_Inds
                on new { left.datamodel_stg_table_schm, left.datamodel_stg_table_name }
                equals new { right.datamodel_stg_table_schm, right.datamodel_stg_table_name }
                into ot
                from otnew in ot.DefaultIfEmpty()
                select new
                {
                    datamodel_stg_table_schm_raw = otnew?.datamodel_stg_table_schm,
                    datamodel_stg_table_name_raw = otnew?.datamodel_stg_table_name,

                    datamodel_stg_table_schm_ind = left.datamodel_stg_table_schm,
                    datamodel_stg_table_name_ind = left.datamodel_stg_table_name
                }).ToList();

var table_model_lines = table_leftoutrtjoin.Concat(table_rightouterjoin);
table_model_lines = table_model_lines.Distinct().ToList(); //INNER MATCH DUPLICATION REMOVAL

The object classes above described here:

public class DataModel_STG_Table_Ind
{
    [Key]
    public int datamodel_stg_table_id { get; set; }
    public int datamodel_id { get; set; }
    public string datamodel_stg_table_schm { get; set; } = null!;
    public string datamodel_stg_table_name { get; set; } = null!;
}

public class DataModel_STG_Table_Snapshot_Release_Ind
{
    [Key]
    public int datamodel_stg_table_id { get; set; }
    public int datamodel_id { get; set; }
    public string datamodel_stg_table_schm { get; set; } = null!;
    public string datamodel_stg_table_name { get; set; } = null!;
}

public class DataModel_STG_Table_Snapshot_Mapping_Ind
{
    [Key]
    public int datamodel_stg_table_id { get; set; }
    public int datamodel_id { get; set; }
    public string datamodel_stg_table_schm { get; set; } = null!;
    public string datamodel_stg_table_name { get; set; } = null!;
    public bool datamodel_stg_table_is_constructed { get; set; }
}

And then several things are happening based on the outcome, but some times this function need to execute with different list, I need a way to change the List, so instead of the following:

List<DataModel_STG_Table_Snapshot_Release_Ind> dataModel_STG_Table_Snapshot_Release_Inds = _dbContext.DataModel_STG_Table_Snapshot_Releases_Ind.Where(x => x.datamodel_id == y).ToList();
List<DataModel_STG_Table_Snapshot_Mapping_Ind> dataModel_STG_Table_Snapshot_Mapping_Inds = _dbContext.DataModel_STG_Table_Snapshot_Mappings_Ind.Where(x => x.datamodel_id == y).ToList();

I may have the following:

List<DataModel_STG_Table_Ind> dataModel_STG_Table_Inds = _dbContext.DataModel_STG_Tables_Ind.Where(x => x.datamodel_id == y).ToList();
List<DataModel_STG_Table_Snapshot_Release_Ind> dataModel_STG_Table_Snapshot_Release_Inds = _dbContext.DataModel_STG_Table_Snapshot_Releases_Ind.Where(x => x.datamodel_id == y).ToList();

Notice that the switch can happen between List<DataModel_STG_Table_Snapshot_Release_Ind> and List<DataModel_STG_Table_Ind> (DataModel_STG_Table_Ind class have subset fields of DataModel_STG_Table_Snapshot_Release_Ind fields, and join fields are present on both), so is there any way to accomplish that without creating a new function and duplicating the join and the rest of the code?

I tried something like this in the join as I have flexibility on the list context and not on database context

(group_type.ForModeling() ? dataModel_STG_Table_Inds.ToList<DataModel_STG_Table_Snapshot_Release_Ind> : dataModel_STG_Table_Snapshot_Release_Inds)

but seems not possible due to implicit conversion:

var table_leftoutrtjoin = (
                from left in (group_type.ForModeling() ? dataModel_STG_Table_Inds.ToList<DataModel_STG_Table_Snapshot_Release_Ind> : dataModel_STG_Table_Snapshot_Release_Inds)
                join right in dataModel_STG_Table_Snapshot_Mapping_Inds
                on new { left.datamodel_stg_table_schm, left.datamodel_stg_table_name }
                equals new { right.datamodel_stg_table_schm, right.datamodel_stg_table_name }
                into ot
                from otnew in ot.DefaultIfEmpty()
                select new
                {
                    datamodel_stg_table_schm_raw = left.datamodel_stg_table_schm,
                    datamodel_stg_table_name_raw = left.datamodel_stg_table_name,

                    datamodel_stg_table_schm_ind = otnew?.datamodel_stg_table_schm,
                    datamodel_stg_table_name_ind = otnew?.datamodel_stg_table_name
                }).ToList();

Solution

  • So really you're looking at Liskov Substitution Principal.

    Create an interface for the data types and as long as you reference that and only refer to the common properties between the classes i.e., those defined in the interface you can use whatever logic you wish.

    For example:

    public interface IDataModel_STG_Table
    {
        int datamodel_stg_table_id { get; set; }
        int datamodel_id { get; set; }
        string datamodel_stg_table_schm { get; set; }
        string datamodel_stg_table_name { get; set; }
    }
    
    public class DataModel_STG_Table_Ind : IDataModel_STG_Table
    {
        public int datamodel_stg_table_id { get; set; }
        public int datamodel_id { get; set; }
        public string datamodel_stg_table_schm { get; set; }
        public string datamodel_stg_table_name { get; set; }
    }
    
    public class DataModel_STG_Table_Snapshot_Release_Ind : IDataModel_STG_Table
    {
        public int datamodel_stg_table_id { get; set; }
        public int datamodel_id { get; set; }
        public string datamodel_stg_table_schm { get; set; }
        public string datamodel_stg_table_name { get; set; }
    }
    
    public class DataModel_STG_Table_Snapshot_Mapping_Ind : IDataModel_STG_Table
    {
        public int datamodel_stg_table_id { get; set; }
        public int datamodel_id { get; set; }
        public string datamodel_stg_table_schm { get; set; }
        public string datamodel_stg_table_name { get; set; }
        public bool datamodel_stg_table_is_constructed { get; set; }
    }
    

    Then your lists are no longer specified as containing the concrete classes but instances of classes that implement the interface. Thus they are populated as follows:

    List<IDataModel_STG_Table> dataModel_STG_Table_Snapshot_Release_Inds = _dbContext.DataModel_STG_Table_Snapshot_Releases_Ind.Where(x => x.datamodel_id == y).ToList();
    List<IDataModel_STG_Table> dataModel_STG_Table_Snapshot_Mapping_Inds = _dbContext.DataModel_STG_Table_Snapshot_Mappings_Ind.Where(x => x.datamodel_id == y).ToList();
    

    or

    List<IDataModel_STG_Table> dataModel_STG_Table_Inds = _dbContext.DataModel_STG_Tables_Ind.Where(x => x.datamodel_id == y).ToList();
    List<IDataModel_STG_Table> dataModel_STG_Table_Snapshot_Release_Inds = _dbContext.DataModel_STG_Table_Snapshot_Releases_Ind.Where(x => x.datamodel_id == y).ToList();
    

    Now you can now place your join code within a function such as

    public IEnumerable<IDataModel_STG_Table> LeftJoinTables(List<IDataModel_STG_Table> table1, List<IDataModel_STG_Table> table2)
    

    Then call using

    var result = LeftJoinTables(dataModel_STG_Table_Snapshot_Release_Inds, dataModel_STG_Table_Snapshot_Mapping_Inds);
    

    or

    var result = LeftJoinTables(DataModel_STG_Table_Inds, dataModel_STG_Table_Snapshot_Mapping_Inds);
    

    You may need some tweaking to the join code to ensure that your return type is IEnumerable<IDataModel_STG_Table> but that would include any of the collection types that implement IEnumerable including List<IDataModel_STG_Table>.

    The calling logic knows what the concrete class types are, but I'd be wary of casting any results with DataModel_STG_Table_Snapshot_Mapping_Ind as this includes a field/property not present within the interface.