使用Linq到Entities的存储过程中的动态表

时间:2010-10-27 09:11:54

标签: c# sql entity-framework .net-4.0 linq-to-entities

我对实体框架和Linq to Entities有疑问。我的.NET版本是4.0。我正在重构现有应用程序的数据库访问层,我计划使用Linq to Entities(而不是今天的DataReaders和SQL字符串)。数据库的结构不能改变。

我的问题来自存储过程,简化后的内容如下所示:

CREATE PROCEDURE getElement @tableid as int, @elementid as int AS
BEGIN
DECLARE @tablename as varchar(50)
SELECT @tablename = tablename FROM tables WHERE tableid = @tableid
EXEC('SELECT * FROM ' + @tablename + ' WHERE elementid = ' + @elementid)
END

我知道返回的行将有一个名为 elementid 的列,并且基于此值,我知道还有其他哪些列。

今天,这是通过一个SqlDataReader解决的,它对 elemendid 元素进行了类似字典的查找。

public Element getElement(SqlDataReader dr)
{
    switch((int)dr["elementid"])
    {
        case 1:
            return getTextElement(dr);
        case 2:
            return getImageElement(dr);
        //...
    }
}

Element是一个抽象基类。 getTextElement返回TextElement : ElementgetImageElement返回ImageElement : Element

如何在Entity Framework中对此进行建模?复杂类型似乎并没有削减它,因为它似乎不支持动态属性。我也看了一个EntityObject Generator,但我并不是真的有自定义T4代码的经验(也许我应该学习这个问题?)。对我来说,完美的解决方案是让导入的存储过程返回一个带有 dynamic 类型的对象,但实体框架4似乎不支持这个。

2 个答案:

答案 0 :(得分:0)

我认为您遇到的问题是EF和Linq to Sql设计人员根据表的已知结构生成模型。您使用EXEC评估结果,因此无法分析过程以确定结果模型的外观。我不确定这可以使用ORM工具解决,您可能需要专门化两个存储过程,一个用于显式返回TextElement模型,另一个用于ImageElement模型。

答案 1 :(得分:0)

我只是想我会添加如何解决这个问题。

我创建了几个辅助类来模拟Linq to Entities的行为,并在我的特殊存储过程中使用它们。它远非完美,甚至不是很好,但它使得生成的代码看起来非常类似于Linq to Entities。这对我来说很重要,因为我的数据库层的其余部分将使用Linq to Entities。

在完美的世界中,我可以为Linq to Entities制定一个查询,然后使用与我现在正在做的结果有点类似的结果。

我们走了......

代码使用如下:

var connectionString = new SqlConnectionStringBuilder
{
    DataSource = @"C:\Temp\Northwind.mdf"
};

var commandText = "select * from Customers";

using (var rows = new SqlCommandHelper(connectionString.ToString(), System.Data.CommandType.Text, commandText))
{
    foreach (dynamic row in rows)
    {
        try
        {
            Console.WriteLine(row.Fax ?? "Emtpy");
        }
        catch (IndexOutOfRangeException)
        {
            Console.WriteLine("Invalid column name");
        }
    }
}

正如您所看到的,行的枚举看起来与我使用Linq to Entities而不是SqlCommandHelper的方式类似。

SqlCommandHelper类是以下代码:

class SqlCommandHelper : IEnumerable<DynamicSqlRow>, IDisposable
{
    private SqlConnection connection;
    private SqlCommand command;

    public SqlCommandHelper(string connectionString, System.Data.CommandType commandType, string commandText, params SqlParameter[] parameters)
    {
        connection = new SqlConnection(connectionString);
        command = new SqlCommand
        {
            CommandText = commandText,
            CommandType = commandType,
            Connection = connection
        };

        command.Parameters.AddRange(parameters);
    }

    public IEnumerator<DynamicSqlRow> GetEnumerator()
    {
        if (connection.State != System.Data.ConnectionState.Open)
        {
            connection.Open();
        }

        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                yield return new DynamicSqlRow(reader);
            }
        }            
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Dispose()
    {
        command.Dispose();
        connection.Dispose();
    }
}

正如你所看到的,神奇在于DynamicSqlRow。我需要注意的是,您需要导入System.Dynamic DynamicSqlRow命名空间进行编译。

class DynamicSqlRow : DynamicObject
{
    System.Data.IDataReader reader;

    public DynamicSqlRow(System.Data.IDataReader reader)
    {
        this.reader = reader;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var row = reader[binder.Name];

        result = row is DBNull ? null : row;

        return true;
    }
}

我希望这段代码可能对其他人有用,或者让人们想到更好的解决方案。

对我来说一个有用的链接是来自MSDN的Walkthrough: Creating and Using Dynamic Objects

小心