EE_Kraig
EE_Kraig

Reputation: 93

How do I choose a particular DbSet from a DbContext using EFCore 3.1?

My app works with 5 tables right now, maybe more in the future, and I would like to minimize code duplication. One example below is for Swd0004 and I also have the same code for Swd0013, and other duplicated functions for each table. The user chooses what table to work with from a drop down box. I'd like to take this drop down box selection and somehow pass that to the functions.

private void SWD_0004_Lookup()
{
    using (var ctx = new EFContext())
    {
        var query = ctx.Swd0004;
        var readList = query.ToList();
        Debug.WriteLine("QUERY COUNT: " + readList.Count);
        // create datatable from query results so I can populate datagridview
        var dt = LINQResultToDataTable(query);
        dgvQueryResults.DataSource = dt;
        GetColumnHeaders(dt);
        DataDT = dt; // copy local dt so we can use DataDT for getting record data
    }
}

This is a database first app. Swd0004 is a table in a database and therefore a DbSet in my EFContext class. My Swd0004 class:

namespace SWDDB.Models
{
    public partial class Swd0004
    {
        public int Id { get; set; }
        public string PartNumber { get; set; }
...

Then do I need to add a generic to it to implement @abdusco answer? Maybe I'm not familiar enough with generics.

    public partial class Swd0004<TEntity> : ??? where TEntity : class

Here's another example:

private List<Swd0004> ConvertDT(DataTable dt)
{
    var importList = new List<Swd0004>();
    // convert DataRow object to SWD object, otherwise BulkInsert will fail
    foreach (DataRow row in dt.Rows)
    {
        var obj = CreateItemFromRow<Swd0004>(row);
        // BulkInsert needs a list
        importList.Add(obj);
    }
    return importList;
}

As you can see, I have to explicitly state Swd0004 three times. So I would have to duplicate this function for each table, 5 times.

UPDATE I'm trying to implement what @bolkay suggested. I created this class:

using SWDDB.Models;
using System.Diagnostics;
using System.Linq;

namespace SWDDB
{
    public abstract class BaseLookup<TEntity> where TEntity : class
    {
        public virtual void PerformLookup()
        {
            using (var ctx = new EFContext())
            {
                var query = ctx.Set<TEntity>();
                Debug.WriteLine("QUERY COUNT: " + query.ToList().Count);
            }
        }
    }
    public class SWD0004Lookup : BaseLookup<Swd0004>
    {
        
    }
}

It feels like I'm close but I can't figure out how to call PerformLookup from my mainline code.

Upvotes: 0

Views: 248

Answers (2)

abdusco
abdusco

Reputation: 11081

You can get a DbSet using DbContext.Set<TEntity>() instead of using properties in your EfContext class:

private void SWD_Lookup<TEntity>() // <-- turn it into a generic method
{
    using (var ctx = new EFContext())
    {
        var query = ctx.Set<TEntity>(); // <-- get dbset by passing the type
        // ...
    }
}

then you can call it like this:

SWD_Lookup<Swd0004>();
SWD_Lookup<Swd0013>();

Upvotes: 0

bolkay
bolkay

Reputation: 1909

I don't fully understand the logic behind users selecting a table to work with.

You can do something like this as a starting point to remove duplication of methods.

Create a base class with the logic.

    public abstract class BaseLookUp<TEntity> where TEntity : class
    {
        public virtual void PerformLookup()
        {
            using (var ctx = new EFContext())
            {
                var query = ctx.Set<TEntity>();
                //etc...
            }
        }
    }
    public class SWD004Lookup : BaseLookUp<SWD004004>
    {

    }

Totally depending on your needs, you could just make it a static method etc.

Upvotes: 1

Related Questions