具有类名的查询上下文仅在运行时知道

时间:2013-11-14 09:32:18

标签: c# entity-framework generics

我正在创建一段代码,允许我根据字符串类名动态查询数据库。

我想要的简要概述:

我的背景:

public class MyContext : DbContext
{
    public DbSet<Foo> Foos {get;set;}
    public DbSet<Bar> Bars {get;set;}
}

通过我的设置,我通过执行基本的context.Foos.Where(...)来查询这个上下文,我基本上也希望这样做,但不必在编译时知道.Foos部分,我希望它能在运行时完成。

逻辑上我想要的是:

var results = context."ClassName".Where(...)

我尝试使用通用方法执行此操作,例如:

//method in context
public IEnumerable<T> GetGenericData<T>() where T : Type
{
    return context.Set<T>();
}

//call in repo
context.GetGenericData<Type.GetType("Foo")>();

不确定我是否会走这条路。理想情况下,我希望得到一个解决方案,这意味着我不必沿着SQL路线

编辑: 我正在进一步努力,我已经到了这一步。

//method in context
public DbSet GetGenericData(Type _type)
{
    return context.Set(_type);
}

//calling like
var data = context.GetGenericData(myType);

现在的问题是,var data是一个DbSet。我需要能够将其转换为在运行时定义的Type列表。有没有一个简单的解决方案来做到这一点,因为我不能简单地做var data = context.GetGenericData(myType).toList()

EDIT2: 要为问题添加更多上下文,我正在创建一段代码来从文件中读取数据库表,例如,获取这些表中的数据并将其序列化为XML文件。

基本上我会有一个包含以下内容的文件:

Foo
Bar
MoreStuff

然后将读取,并将Foo表中的所有数据导出到名为Foo.xml的xml文件中。还必须进行相反的操作,其中从xml文件中读取数据并将其导入数据库

2 个答案:

答案 0 :(得分:6)

如果您在运行时不知道实体类型但具有类型名称,则可以在反射Type.GetType(字符串typeName)的帮助下按名称获取类型,并使用DbContext.Set重载,如 - &gt; DbSet Set.Set(类型类型)

http://msdn.microsoft.com/en-us/library/gg679544(v=vs.113).aspx

的详细信息

更新:

void Main()
{
    var typeMapping = new Dictionary<Type, Func<object, dynamic>>(){
        {typeof(A), a => (A)a}
    };
    var array = new[]{new A(){ o = new Object() },new A(){ o = new Object() },new A(){ o = new Object() },new A(){ o = new Object() },};    
    var objectArray = array.OfType<object>();
    objectArray.Select(a => typeMapping[a.GetType()](a)).Dump();

}

class A{
    public object o {get;set;}
}

这是来自LinqPad的样本。你的问题是关于动态编程,我无法理解目的但仍然。在此粘贴中,您必须为所有实体类型定义类型映射,例如CLR类型和转换操作,它们返回所需类型的引用。例如,数组变量是目的,objectArray是您的数据。

UPADATE2:

使用SerializableAttribute标记您的实体类型 http://msdn.microsoft.com/en-us/library/system.serializableattribute(v=vs.110).aspx 比调用ToList或ToArray扩展名

var data = context.GetGenericData(myType).Tolist();

并使用XmlSerializer序列化它,如

var fileStream = File.OpenWrite("someFilePathName");
new XmlSerializer().Serialize(fileStream, data);

就是这样!它会起作用,因为CLR会确切知道DbSet集合中的项目类型。你不需要施展任何东西。然后XmlSerializer将完成序列化器收集实体所需的所有工作,并将其写入流,即文件流。还有一件事是添加一些检查并为IDisposable实例添加使用块。

UPDATE3:

无法将DbSet强制转换为与ToList完全匹配,因为这样可以避免在实体的编译时使用类型。所以这里是加载数据并将其转换为对象以继续操作的想法

Set(typeof(TableBase)).Load();
var collectionToExport = Set(typeof (TableBase)).Local.Cast<object>().ToList();

和所有在一起:

var type = Type.GetType("Foo");
context.Set(type).Load();
var collectionToExport = context.Set(type).Local.Cast<object>().ToList();
var fileStream = File.OpenWrite("someFilePathName");
new XmlSerializer().Serialize(fileStream, data);

答案 1 :(得分:0)

您的方法很可靠,但我会重新考虑IEnumerable方法,因为它不支持Include和其他特定于数据库的操作。