locked
Can you use text strings to filter a List of objects? RRS feed

  • Question

  • I am trying to filter a list of data objects using a user defined string as the where clause.
    So far linq lets me specify only part of the clause (str). The rest needs to be hard coded and handled with if statements.

    string str = "male";
    IEnumerable<Models.EmployeeDepartment> ellist = el.Where( g => g.Gender == str);

    Is there a way to allow users to build a string like: "Gender =='male'" ?

    I looked at datagridview filtering but it does not look doable unless the datasource is a table.

    Friday, November 27, 2020 9:12 PM

Answers

  • Hello,

    First, the code provided is LINQ (actually lambda).

    What you have will not work, options, learn (and it's not easy if never done before) is expression trees. In the Microsoft docs there is a very basic example. Then a decent example close to what you want but is even more to learn.

    Here are a few simple examples I wrote.

    namespace NorthWindLibrary
    {
        public class NorthOperations
        {
            /// <summary>
            /// Get tables, columns, column types and primary keys
            /// </summary>
            /// <returns></returns>
            public async Task<List<TableInformation>> DatabaseTableInformation()
            {
    
                var entityCrawler = new EntityCrawler()
                {
                    AssembleName = Assembly.GetExecutingAssembly().GetName().Name,
                    TypeName = "NorthWindAzureContext"
                };
    
                await Task.Run(() => entityCrawler.GetInformation());
                
    
                return entityCrawler.TableInformation;
    
            }
            /// <summary>
            /// Get all customers without children
            /// </summary>
            /// <returns></returns>
            public async Task<List<Customer>> GetAllCustomers()
            {
    
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers.Select(customer => customer).ToList()); 
                }
    
            }
            /// <summary>
            /// Get customer by dynamic property and value, include country
            /// </summary>
            /// <param name="propertyName"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            public async Task<Customer> GetCustomers(string propertyName, string value)
            {           
    
                Func<Customer, bool> query = DynamicQueryWithExpressionTrees(propertyName, value);
    
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers
                        .Include(customer => customer.Country)
                        .Where(query).Select(customer => customer)
                        .FirstOrDefault());
                }
    
            }
            /// <summary>
            /// Get customers by dynamic property and value
            /// </summary>
            /// <param name="propertyName"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            public async Task<List<Customer>> GetCustomersList(string propertyName, string value)
            {
    
                Func<Customer, bool> query = DynamicQueryWithExpressionTrees(propertyName, value);
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers
                        .Include(customer => customer.Country)
                        .Where(query).Select(customer => customer)
                        .ToList());
                }
    
            }
            /// <summary>
            /// Get subset of customers focusing on primary keys
            /// </summary>
            /// <returns></returns>
            public async Task<List<CustomerNameIdentifier>> CustomerNameWithIdentifiers()
            {
                using (var context = new NorthWindAzureContext())
                {
                    
                    return await Task.Run(() =>
                        context.Customers
                            .Include(customer => customer.Country)
                            .Select(customer => new CustomerNameIdentifier()
                        {
                            CustomerIdentifier = customer.CustomerIdentifier,
                            CompanyName = customer.CompanyName,
                            CountryIdentifier = customer.CountryIdentifier.Value
                        }).ToList());
                }
            }
            /// <summary>
            /// Dynamic sort by property name and order ascending or descending 
            /// </summary>
            public void SortTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.ToList().Sort1("CompanyName DESC");
                    Console.WriteLine();
                }
    
            }
            /// <summary>
            /// Example for projection of a specific model
            /// </summary>
            /// <returns></returns>
            public List<CustomerCountryListItem> ProjectionTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    List<CustomerCountryListItem> results = context.Customers.Select(Customer.Projection).ToList();
                    return results;
                }
            }
            /// <summary>
            /// Example for building a where predicate with an expression
            /// </summary>
            public void BuilderTest()
            {
                var test = Builder.Build<Customer, string>(
                    customer => customer.ContactType.ContactTitle, Operator.EQUAL,"Owner");
    
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.Where(test).ToList();
                    Console.WriteLine();
                }
            }
            /// <summary>
            /// Using expressions to build a predicate for obtaining data
            /// by multiple key values
            /// </summary>
            /// <returns></returns>
            public List<Customer> ExtensionCustomersContainsIdentifiersTest()
            {
                var ids = new List<int> { 1, 2, 3 };
    
                using (var context = new NorthWindAzureContext())
                {
                    return context.Customers
                        .Include(customer => customer.Contact)
                        .WithIdendifier(cust => cust.CustomerIdentifier, ids)
                        .ToList();
                }
    
            }
            /// <summary>
            /// Using expressions to build a predicate for obtaining data
            /// by multiple key values
            /// </summary>
            /// <returns></returns>
            public void CustomerContactsTypes()
            {
                var ids = new List<int?> { 1, 2, 3 };
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers
                        .Include(c => c.ContactType)
                        .WithContactTypes(item => item.ContactTypeIdentifier, ids)
                        .ToList();
    
                    Console.WriteLine();
                }
            }
            public List<Customer> CompleteCustomersTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    return context.PartialCompleteCustomers().ToList();
                }
            }
    
            public async Task<List<CountryItem>> GetAllCountries()
            {
    
                using (var context = new NorthWindAzureContext())
                {
    
                    return await Task.Run(() => 
                        context.Countries.Select(country => new CountryItem()
                        {
                            CountryIdentifier = country.CountryIdentifier,
                            Name = country.Name
                        }).ToList());
                }
    
            }
    
            public async Task<List<CategoryCheckedListBox>> GetAllCategories()
            {
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Categories.Select(category => new CategoryCheckedListBox()
                    {
                        CategoryID = category.CategoryID,
                        CategoryName = category.CategoryName
                    }).ToList());
                }
    
            }
            /// <summary>
            /// How a developer might try to perform a IN condition yet this
            /// is hard coded, not suited to work with dynamic values.
            /// </summary>
            private void ArticleSample1()
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.
                        Where(customer => customer.CountryIdentifier == 8 || 
                                          customer.CountryIdentifier == 7)
                        .ToList();
                }
            }
            /// <summary>
            /// Next attempt, pass in a Nullable Int array of country identifiers
            /// </summary>
            /// <param name="countryIdentifiers"></param>
            private void ArticleSample2(int?[] countryIdentifiers)
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers
                        .Where(customer => countryIdentifiers.Contains(customer.CountryIdentifier))
                        .ToList();
                }
            }
    
        }
    }
    here is the full source for the above code block.

    Next up is writing code logic to handle different conditions. For example, if I wanted a range of customers between two int values for the primary key and a country name we could do this.

    if (!int.TryParse(StartTextBox.Text, out var startValue) || !int.TryParse(EndTextBox.Text, out var endValue)) return;
    
    using (var context = new NorthwindContext())
    {
        var customerList = context.
            Customers.Include(country => country.CountryIdentifierNavigation)
            .Between(cust => cust.CustomerIdentifier, startValue, endValue)
            .Select(cust => new CustomerCountry()
            {
                Name = cust.CompanyName,
                Country = cust.CountryIdentifierNavigation.Name
            })
            .Where(item => item.Country == CountryComboBox.Text)
            .ToList();
    
        CompanyDataGridView.DataSource = customerList;
    
    }

    Or suppose we want the above but country name is only used if the first item in a ComboBox is not selected then we can do a additive condition on the initial where.

    if (!int.TryParse(StartTextBox.Text, out var startValue) || !int.TryParse(EndTextBox.Text, out var endValue)) return;
    
    using (var context = new NorthwindContext())
    {
        var customerList = context.
            Customers.Include(country => country.CountryIdentifierNavigation)
            .Between(cust => cust.CustomerIdentifier, startValue, endValue)
            .Select(cust => new CustomerCountry()
            {
                Name = cust.CompanyName,
                Country = cust.CountryIdentifierNavigation.Name
            })
            .ToList();
    
        if (CountryComboBox.Text != "Select")
        {
            customerList = customerList.Where(cust => cust.Country == CountryComboBox.Text).ToList();
        }
    
        CompanyDataGridView.DataSource = customerList;
    
    }
    The above can be found in the following GitHub repository.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    • Proposed as answer by KHURRAM RAHIM Saturday, November 28, 2020 10:16 PM
    • Marked as answer by Kenmtb Sunday, November 29, 2020 11:55 PM
    Saturday, November 28, 2020 12:19 PM
  • Hello,

    In regards to "Is there another approach/method for building dynamic filters and sorts on lists?", there are usually other approaches to any/most task, ask 10 developers to solve a task and you get at least five different suggestions. I could surely come up with at least two more suggestions while doubt you would be happy with them any more than what I've presented.

    What I don't use any more are DataTable or DataSet containers, they tend to be on the heavy side (have functionality not needed at least for me). All I work with outside of forum replies is Entity Framework Core and if needed Entity Framework 6 for older work.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    • Marked as answer by Kenmtb Sunday, November 29, 2020 1:11 AM
    Saturday, November 28, 2020 4:46 PM
  • You can't do what you're talking about, a => a.Age,  as some kind of string acting as part of a lambda expression, becuase 'a' represents an object with public properties,  and lambda expression is a function.

    And besides, C# or VB are not interpretive languages they are compiled languages, unlike JavaScript. You might be able to use a string command what you're trying to do with  JavaScript  an interpreted language. I  can't say for sure, becuase I have never tried it with JavaScript.

    Lambda Expressions in C# (c-sharpcorner.com)

    Interpreted vs Compiled Programming Languages: What's the Difference? (freecodecamp.org)

     
    • Edited by DA924x Saturday, November 28, 2020 7:54 AM
    • Marked as answer by Kenmtb Sunday, November 29, 2020 11:55 PM
    Saturday, November 28, 2020 7:46 AM

All replies

  • Hello,

    Usually when there are defined values e.g. Male, Female it's best to not use a raw string but instead present choices that are available such as Male and Female so the user doesn't mistype a value e.g. make instead of male.

    Fist option is to populate a control such as a ComboBox or ListBox with valid values or even better (and this is demonstrated using Entity Framework but could be adapted to non Entity Framework).

    Create a T4 template and point it to a table by creating a file names [tablename].iiinclude then setup as shown below where the primary key becomes the value for each member in the enum.

    So here I do this againsts a table named Categories in a SQL-Server database where the primary key is CategoryID, CategoryName becomes the enum member name and the same for ColumnDescription.

    Check out the fill T4 file

    Set Custom Tool for the ttinclude file so when saving the file it generates a enum structure.

    TextTemplatingFileGenerator

    Rather than post a lot of code on how to glue things together you can see all the code and clone or download.

    https://github.com/karenpayneoregon/EntityFrameworkEnum

    TechNet Wiki article

    https://social.technet.microsoft.com/wiki/contents/articles/53169.entity-framework-databasecode-first-enum-support.aspx

    Screen shots


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    Friday, November 27, 2020 11:37 PM
  • Hi-

    Thank you for the reply. My question may need some rewording. 
    The example I posted was linq test code. I'm looking for a way to apply a filter to a list using linq with a string as the where clause. The user would build the filter string. It could be  "Age < 50", "Name == 'Joe'" and so on.

    Can linq or some sql filter use strings in its where clause like:
    str = "Age < 50"
    .Where (str)

    The closest I got was something like:  
    Where (a => a.Age < 50)

    I can make 50 a variable but I would still have to hardwire the rest of the where clause.
    Thanks



    Saturday, November 28, 2020 3:26 AM
  • You can't do what you're talking about, a => a.Age,  as some kind of string acting as part of a lambda expression, becuase 'a' represents an object with public properties,  and lambda expression is a function.

    And besides, C# or VB are not interpretive languages they are compiled languages, unlike JavaScript. You might be able to use a string command what you're trying to do with  JavaScript  an interpreted language. I  can't say for sure, becuase I have never tried it with JavaScript.

    Lambda Expressions in C# (c-sharpcorner.com)

    Interpreted vs Compiled Programming Languages: What's the Difference? (freecodecamp.org)

     
    • Edited by DA924x Saturday, November 28, 2020 7:54 AM
    • Marked as answer by Kenmtb Sunday, November 29, 2020 11:55 PM
    Saturday, November 28, 2020 7:46 AM
  • Hello,

    First, the code provided is LINQ (actually lambda).

    What you have will not work, options, learn (and it's not easy if never done before) is expression trees. In the Microsoft docs there is a very basic example. Then a decent example close to what you want but is even more to learn.

    Here are a few simple examples I wrote.

    namespace NorthWindLibrary
    {
        public class NorthOperations
        {
            /// <summary>
            /// Get tables, columns, column types and primary keys
            /// </summary>
            /// <returns></returns>
            public async Task<List<TableInformation>> DatabaseTableInformation()
            {
    
                var entityCrawler = new EntityCrawler()
                {
                    AssembleName = Assembly.GetExecutingAssembly().GetName().Name,
                    TypeName = "NorthWindAzureContext"
                };
    
                await Task.Run(() => entityCrawler.GetInformation());
                
    
                return entityCrawler.TableInformation;
    
            }
            /// <summary>
            /// Get all customers without children
            /// </summary>
            /// <returns></returns>
            public async Task<List<Customer>> GetAllCustomers()
            {
    
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers.Select(customer => customer).ToList()); 
                }
    
            }
            /// <summary>
            /// Get customer by dynamic property and value, include country
            /// </summary>
            /// <param name="propertyName"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            public async Task<Customer> GetCustomers(string propertyName, string value)
            {           
    
                Func<Customer, bool> query = DynamicQueryWithExpressionTrees(propertyName, value);
    
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers
                        .Include(customer => customer.Country)
                        .Where(query).Select(customer => customer)
                        .FirstOrDefault());
                }
    
            }
            /// <summary>
            /// Get customers by dynamic property and value
            /// </summary>
            /// <param name="propertyName"></param>
            /// <param name="value"></param>
            /// <returns></returns>
            public async Task<List<Customer>> GetCustomersList(string propertyName, string value)
            {
    
                Func<Customer, bool> query = DynamicQueryWithExpressionTrees(propertyName, value);
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Customers
                        .Include(customer => customer.Country)
                        .Where(query).Select(customer => customer)
                        .ToList());
                }
    
            }
            /// <summary>
            /// Get subset of customers focusing on primary keys
            /// </summary>
            /// <returns></returns>
            public async Task<List<CustomerNameIdentifier>> CustomerNameWithIdentifiers()
            {
                using (var context = new NorthWindAzureContext())
                {
                    
                    return await Task.Run(() =>
                        context.Customers
                            .Include(customer => customer.Country)
                            .Select(customer => new CustomerNameIdentifier()
                        {
                            CustomerIdentifier = customer.CustomerIdentifier,
                            CompanyName = customer.CompanyName,
                            CountryIdentifier = customer.CountryIdentifier.Value
                        }).ToList());
                }
            }
            /// <summary>
            /// Dynamic sort by property name and order ascending or descending 
            /// </summary>
            public void SortTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.ToList().Sort1("CompanyName DESC");
                    Console.WriteLine();
                }
    
            }
            /// <summary>
            /// Example for projection of a specific model
            /// </summary>
            /// <returns></returns>
            public List<CustomerCountryListItem> ProjectionTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    List<CustomerCountryListItem> results = context.Customers.Select(Customer.Projection).ToList();
                    return results;
                }
            }
            /// <summary>
            /// Example for building a where predicate with an expression
            /// </summary>
            public void BuilderTest()
            {
                var test = Builder.Build<Customer, string>(
                    customer => customer.ContactType.ContactTitle, Operator.EQUAL,"Owner");
    
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.Where(test).ToList();
                    Console.WriteLine();
                }
            }
            /// <summary>
            /// Using expressions to build a predicate for obtaining data
            /// by multiple key values
            /// </summary>
            /// <returns></returns>
            public List<Customer> ExtensionCustomersContainsIdentifiersTest()
            {
                var ids = new List<int> { 1, 2, 3 };
    
                using (var context = new NorthWindAzureContext())
                {
                    return context.Customers
                        .Include(customer => customer.Contact)
                        .WithIdendifier(cust => cust.CustomerIdentifier, ids)
                        .ToList();
                }
    
            }
            /// <summary>
            /// Using expressions to build a predicate for obtaining data
            /// by multiple key values
            /// </summary>
            /// <returns></returns>
            public void CustomerContactsTypes()
            {
                var ids = new List<int?> { 1, 2, 3 };
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers
                        .Include(c => c.ContactType)
                        .WithContactTypes(item => item.ContactTypeIdentifier, ids)
                        .ToList();
    
                    Console.WriteLine();
                }
            }
            public List<Customer> CompleteCustomersTest()
            {
                using (var context = new NorthWindAzureContext())
                {
                    return context.PartialCompleteCustomers().ToList();
                }
            }
    
            public async Task<List<CountryItem>> GetAllCountries()
            {
    
                using (var context = new NorthWindAzureContext())
                {
    
                    return await Task.Run(() => 
                        context.Countries.Select(country => new CountryItem()
                        {
                            CountryIdentifier = country.CountryIdentifier,
                            Name = country.Name
                        }).ToList());
                }
    
            }
    
            public async Task<List<CategoryCheckedListBox>> GetAllCategories()
            {
                using (var context = new NorthWindAzureContext())
                {
                    return await Task.Run(() => context.Categories.Select(category => new CategoryCheckedListBox()
                    {
                        CategoryID = category.CategoryID,
                        CategoryName = category.CategoryName
                    }).ToList());
                }
    
            }
            /// <summary>
            /// How a developer might try to perform a IN condition yet this
            /// is hard coded, not suited to work with dynamic values.
            /// </summary>
            private void ArticleSample1()
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers.
                        Where(customer => customer.CountryIdentifier == 8 || 
                                          customer.CountryIdentifier == 7)
                        .ToList();
                }
            }
            /// <summary>
            /// Next attempt, pass in a Nullable Int array of country identifiers
            /// </summary>
            /// <param name="countryIdentifiers"></param>
            private void ArticleSample2(int?[] countryIdentifiers)
            {
                using (var context = new NorthWindAzureContext())
                {
                    var results = context.Customers
                        .Where(customer => countryIdentifiers.Contains(customer.CountryIdentifier))
                        .ToList();
                }
            }
    
        }
    }
    here is the full source for the above code block.

    Next up is writing code logic to handle different conditions. For example, if I wanted a range of customers between two int values for the primary key and a country name we could do this.

    if (!int.TryParse(StartTextBox.Text, out var startValue) || !int.TryParse(EndTextBox.Text, out var endValue)) return;
    
    using (var context = new NorthwindContext())
    {
        var customerList = context.
            Customers.Include(country => country.CountryIdentifierNavigation)
            .Between(cust => cust.CustomerIdentifier, startValue, endValue)
            .Select(cust => new CustomerCountry()
            {
                Name = cust.CompanyName,
                Country = cust.CountryIdentifierNavigation.Name
            })
            .Where(item => item.Country == CountryComboBox.Text)
            .ToList();
    
        CompanyDataGridView.DataSource = customerList;
    
    }

    Or suppose we want the above but country name is only used if the first item in a ComboBox is not selected then we can do a additive condition on the initial where.

    if (!int.TryParse(StartTextBox.Text, out var startValue) || !int.TryParse(EndTextBox.Text, out var endValue)) return;
    
    using (var context = new NorthwindContext())
    {
        var customerList = context.
            Customers.Include(country => country.CountryIdentifierNavigation)
            .Between(cust => cust.CustomerIdentifier, startValue, endValue)
            .Select(cust => new CustomerCountry()
            {
                Name = cust.CompanyName,
                Country = cust.CountryIdentifierNavigation.Name
            })
            .ToList();
    
        if (CountryComboBox.Text != "Select")
        {
            customerList = customerList.Where(cust => cust.Country == CountryComboBox.Text).ToList();
        }
    
        CompanyDataGridView.DataSource = customerList;
    
    }
    The above can be found in the following GitHub repository.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    • Proposed as answer by KHURRAM RAHIM Saturday, November 28, 2020 10:16 PM
    • Marked as answer by Kenmtb Sunday, November 29, 2020 11:55 PM
    Saturday, November 28, 2020 12:19 PM
  • Thank you all. I appreciate the explanations and detailed example. I will have to look at expression trees and give up on my lambda approach.

    My DataGridView is bound to a List of objects. The goal is to perform filters and sorts on a List of data objects.  It seems like this would be a common requirement? If my data source was a table, I could build dynamic sql strings or even use datagridview filter.

    Is there another approach/method for building dynamic filters and sorts on lists? 
    If not I may just bind my DGVV to tables instead of lists.





    Saturday, November 28, 2020 4:22 PM
  • Hello,

    In regards to "Is there another approach/method for building dynamic filters and sorts on lists?", there are usually other approaches to any/most task, ask 10 developers to solve a task and you get at least five different suggestions. I could surely come up with at least two more suggestions while doubt you would be happy with them any more than what I've presented.

    What I don't use any more are DataTable or DataSet containers, they tend to be on the heavy side (have functionality not needed at least for me). All I work with outside of forum replies is Entity Framework Core and if needed Entity Framework 6 for older work.


    Please remember to mark the replies as answers if they help and unmarked them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.

    NuGet BaseConnectionLibrary for database connections.

    My GitHub code samples
    GitHub page

    Check out:  the new Microsoft Q&A forums

    • Marked as answer by Kenmtb Sunday, November 29, 2020 1:11 AM
    Saturday, November 28, 2020 4:46 PM
  • Golden advice. I have worked with EF and if it is kept locked tight in the DAL it is a great approach.

    Sunday, November 29, 2020 1:28 AM
  • Golden advice. I have worked with EF and if it is kept locked tight in the DAL it is a great approach.

    Any solution using SoC is a great  approach.

    https://en.m.wikipedia.org/wiki/Separation_of_concerns

    Sunday, November 29, 2020 7:37 AM