Generic method to retrieve DbSet from DbContext

前端 未结 5 1220
长情又很酷
长情又很酷 2021-01-04 03:02

I\'m using the Entity Framework with a large database (made up of more than 200 tables).

Trying to create a generic method that returns the DbSet

相关标签:
5条回答
  • 2021-01-04 03:43

    I had the same requirement and solved it by using the following:

    public static void GetEntitiesGeneric<TEntity>()// where TEntity : System.Data.Entity.Core.Objects.DataClasses.EntityObject  <-- NO LONGER NEEDED
    {
        try
        {
            var key = typeof(TEntity).Name;
            var adapter = (IObjectContextAdapter)MyDbContext;
            var objectContext = adapter.ObjectContext;
            // 1. we need the container for the conceptual model
            var container = objectContext.MetadataWorkspace.GetEntityContainer(
                objectContext.DefaultContainerName, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace);
            // 2. we need the name given to the element set in that conceptual model
            var name = container.BaseEntitySets.Where((s) => s.ElementType.Name.Equals(key)).FirstOrDefault().Name;
            // 3. finally, we can create a basic query for this set
            var query = objectContext.CreateQuery<TEntity>("[" + name + "]");
    
            // Work with your query ...
        }
        catch (Exception ex)
        {
            throw new ArgumentException("Invalid Entity Type supplied for Lookup", ex);
        }
    }
    

    The code was taken from Using Generics for Lookup Tables in Entity Framework and adapted for EF 6 using the DbContext (first part of the method where the objectcontext is extracted from the dbcontext

    Hope it helps

    0 讨论(0)
  • 2021-01-04 03:56

    I don't know how you have created your model, and thus how your entities look like. But, if it's Code First, the entity classes don't inherit from a common base class, so you cannot add a type constraint to your generic.

    I don't recommend using a base class to be able to specify a constraint. It's much better to do it using an interface. An empty interface will allow you to specify a constraint without having to change your classes at all.

    So, what you can do is define an interface like this:

    public interface IEntity {};
    

    And then:

    • implement it in all classes, which can be done in partial classes files, modifying a T4 template or in some other way depending on how your model looks like
    • use it to specify the generic type constrain with where IEntity

    This is the cleanest way to do it, without any interference to your classes.

    0 讨论(0)
  • 2021-01-04 04:01

    Issue

    I suppose your TableA class doesn't implement EntityObject. That's why you're getting this error. To solve this you can have an abstract class/interface which will be base for all context entities (i.e. IContextEntity which will have unique Id definition):

    public class TableA : IContextEntity
    {
       ...
    }
    

    Then same method but with new interface instead of EntityObject and you can mock/test it easily

    public IQueryable<T> GetFromDatabase<T>() where T : IContextEntity
    {
         ...
    }
    


    Usage(Tests)

    Second important thing is the way you want to use this method. In case of Entity Framework context it is really important to have separation between integration and unit tests. In code you provided you're trying to reach database which means that this test will be integration:

    using (sqlEntities ctx = new sqlEntities()) // This will open a DB connection
    

    Connecting to a databases or external sources is usually a bad practice unless you know what you do and this is exactly it. If you just need some fake/dummy data to perform an action on it - use Stubs.

    0 讨论(0)
  • 2021-01-04 04:04

    Why don't you try changing your constrain to class instead of EntityObject

    public IQueryable<T> GetFromDatabase<T>() where T : class
    
    0 讨论(0)
  • 2021-01-04 04:04

    For any future googlers, my colleague and I just hacked this out in Visual Basic (EF 6 version). It works for our use case of simply getting a list back out but will probably work for the other use cases. No try catch or checking in this one.

    Private Class getList(Of T As Class)
        Public Shared Function getList() As List(Of T)
            Using ctx As New MVPBTEntities()
                ' Get the DbSet of the type T from the entities model (i.e. DB)
                Dim dbSet = ctx.Set(Of T)()
                Return dbSet.ToList
            End Using
        End Function
    End Class
    
    0 讨论(0)
提交回复
热议问题