DbSet.Cast() 错误:无法从非泛型 DbSet 为“Entity”类型的对象创建 DbSet

本文关键字:DbSet 泛型 Entity 创建 对象 类型 TEntity Cast 错误 | 更新日期: 2023-09-27 18:37:07

版本信息:

我正在使用 C# 4.5、实体框架 6.0 和 MEF。

代码和单元测试

我创建了一个测试项目来解释这个问题:https://skydrive.live.com/redir?resid=E3C97EC293A34048!2234

请打开 UnitTest 项目并尝试运行 TestIfItWorks() 单元测试。

问题

我想将非通用 DbSet 转换为其通用版本,但出现以下异常:InvalidCastException: Cannot create a DbSet<IUser> from a non-generic DbSet for objects of type 'User'

var nonGeneric = context.Set(typeof(User));
var generic = nonGeneric.Cast<IUser>(); //Exception in here

User 类正在实现 IUser,因此您会认为强制转换应该不是问题,除非 DbSet 代码仅限于具体类(我希望不是,否则我需要围绕非泛型 DbSet 创建一个包装器以将其转换为通用 DbSet 或找到当前 DbSet 实现的替代方案)。

如果您想知道为什么我使用接口,即使它们目前不受Microsoft支持,我给您一些解释(希望这会过滤掉"不要那样做"而不是提供解决方案的响应):

我正在使用 MEF 和 EntityFramework 创建一个松散耦合的数据层引擎,通过该引擎我可以为每个项目提供实体(及其相应的配置)。我一直在广泛使用接口来定义引擎。元数据和上下文中实体的具体实现是在运行时使用 MEF 发现的。

代码摘录

[TestMethod]
public void TestIfItWorks()
{
    //TODO: Please open the App.Config and change the PluginsPath to match the Plugins folder in your machine.
    using (var dbContext = new MyContext()) //Please ignore this line for now. This was UnitOfWork which I replaced with Context to create a simple unit test
    {
        dbContext.Setup(); //Please ignore this line for now. This was part of UnitOfWork which I moved to here to create a simple unit test
        //The purpose of all these is to be able to read and write user info from/to database while User class is defined in an external assembly
        //but we can import it by MEF using IUser interface.
        //Failed Attempt# 1: Use User class directly! This doesnt work because User is in an external class which we dont have reference to
        //var failedAttempt1 = dbContext.Set<User>(); 
        //Failed Attempt# 2: But the good thing is that we have access to IUser and its exports
        //then lets get a DbSet<IUser> instead
        var failedAttempt2 = dbContext.Set<IUser>();
        try
        {
            var throwsException2 = failedAttempt2.FirstOrDefault();
        }
        catch (InvalidOperationException ex)
        {
            //InvalidOperationException: 
            // The entity type IUser is not part of the model for the current context.
            // It also didnt work when I tried to define a class that inherits from EntityTypeConfiguration<IUser>at TestImplementation
        }


        //Ok then lets do it differently this time. Lets get User type (that we know we have good configuration for)
        //from our Container and ask Context to give us the nonGeneric version
        var userImplementationType = Logic.Instance.GetExportedTypes<IUser>().FirstOrDefault();
        Assert.IsNotNull(userImplementationType, "We havn't been able to load TestImplementation into catalog. Please ensure the PluginsPath is set correctly at App.Config");
        var nonGeneric = dbContext.Set(userImplementationType);
        //
        // This is working so far, we can add and remove records from database using
        // the nonGeneric version of DbSet. You can uncomment the following code block provide a unique ID
        // and test it yourself.
        //
        var newUser = Logic.Instance.New<IUser>();
        newUser.Id = "99";
        newUser.UserName = "Aidin Sadighi";
        nonGeneric.Add(newUser);
        try
        {
            dbContext.SaveChanges();
        }
        catch (DbUpdateException ex)
        {
            //This is OK because most probably this is a duplicate user. Just increase the Id to make it unique.
        }

        //Failed Attempt#3: Cast non generic DbSet to generic
        try
        {
            //TODO: I need to fix this. Help me please 
            var genericSet = nonGeneric.Cast<IUser>();
        }
        catch (InvalidCastException ex)
        {
            //Cannot create a DbSet<IUser> from a non-generic DbSet for objects of type 'User'.
            throw;
        }
    }
}

DbSet.Cast<TEntity>() 错误:无法从非泛型 DbSet 为“Entity”类型的对象创建 DbSet

为此,我实际上建议使用反射。 在 DbContext 的构造函数中,可以为函数指针设置一个属性:

method = this.GetType().GetMethod("Set", new Type[0]).MakeGenericMethod(typeof(UserImplementation));

然后,您可以使用以下命令调用它:

method.Invoke(this, new object[0]);

这应该返回一个类型为 DbSet<UserImplementation> 的对象,其中 .然后可以调用 Cast<>() 方法。

替换

nonGeneric.Cast<IUser>();

Enumerable.Cast<IUser>(nonGeneric);
好的,

我对实体框架一无所知,但从查看文档

http://msdn.microsoft.com/en-us/library/gg696521%28v=vs.103%29.aspx

DbSet<TEntity> item = DbContext.Set<TEntity>;

所以实际上你的代码和这样一样:

DbSet<User> nonGeneric = context.Set<User>();

并获得IUser

DbSet<IUser> nonGeneric = context.Set<User>();

或者也许

var generic = nonGeneric.Cast<DbSet<IUser>>();

由于我无法将通用 DbSet 转换为类型化的 DbSet,因此我改用了类型 IQueryable,它可以从 DbSet 中执行我需要的相同操作。

这是一个扩展程序,可以让你:

    public static IQueryable<T> GetIQueryableByTableName<T>(this DbContext context, string tableName)
    {
        var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == tableName);
        if (type == null)
        {
            throw new Exception("GetIQueryableByTableName received an invalid table name.");
        }
        return context.GetType().GetMethod("Set", new Type[0]).MakeGenericMethod(type).Invoke(context, new object[0]) as IQueryable<T>;
    }