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