使用Find-method时,Entity Framework会抛出异常

时间:2015-11-16 22:12:44

标签: c# .net asp.net-mvc entity-framework linq

我有一个简单的Web应用程序,允许用户上传包含特定数据的2个.csv文件。要保留数据,请在两个不同的Entity Framework中使用Import-methods

首先导入方法

    public void ImportOne(string path)
    {
        StreamReader sr = new StreamReader(path);

        using (var db = new ContextEv("RndContext"))
        {
            db.Database.ExecuteSqlCommand("DELETE FROM TableA");
            db.Database.ExecuteSqlCommand("DELETE FROM TableB");

            while (!sr.EndOfStream)
            {
                string[] data = sr.ReadLine().Split(';');
                string houseId = data[0];

                    House house = new House()
                    {
                        HouseId = houseId,
                    };

                    House dummy = db.Houses.Find(houseId);

                    if (!dummy.HouseId.Equals(house.HouseId))
                    {
                        db.Houses.Add(house);
                    }
                }
            }
        }
    }

此行失败:House dummy = db.Houses.Find(houseId);,但有以下异常:

  

其中一个主键值的类型与类型不匹配   在实体中定义。请参阅内部异常   详情。\ r \ nParametername:keyValues

InnerException的ErrorContext:

  

关键字' AS',第1行,第22列

InnerException的错误描述:

  

查询语法无效。

好吧,我检查了这个类型是否真的存在问题。但是我没有发现任何错误。

"搞笑"关于它的事情是,我在另一个Import-method中使用相同的Find-method,它没有任何异常!

using (var db = new ContextEv("RndContext"))
            {
                db.Database.ExecuteSqlCommand("DELETE FROM TableC");
                db.Database.ExecuteSqlCommand("DELETE FROM TableD");

            StreamReader sr = new StreamReader(path);

            while (!sr.EndOfStream)
            {
                string[] data = sr.ReadLine().Split(';');
                string houseId = data[5];

                    House house = db.Houses.Find(houseId);

                    ...
                    ...
                    db.SaveChanges();
                }
            }

我不确定您回答我的问题确实需要哪些代码,但如果有人要求提供特定代码,我会非常乐意发布更多代码。

更新1回复user89861

  

' db.Houses.ToList()。查找(h => h.HouseId == houseId)'扔了一个   类型' System.NullReferenceException'

的例外      

"北   System.Data.Entity.Internal.Linq.InternalQuery 1.GetEnumerator()\r\n
bei System.Data.Entity.Internal.Linq.InternalSet
1.GetEnumerator(个)\ r \ n   北   System.Data.Entity.Infrastructure.DbQuery 1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()\r\n bei System.Collections.Generic.List 1..ctor(IEnumerable 1 collection)\r\n bei System.Linq.Enumerable.ToList[TSource](IEnumerable 1 source)&#34;

3 个答案:

答案 0 :(得分:0)

如果找不到结果,则Find()返回null,因此您应该在访问其成员之前验证变量库:

House house = db.Houses.Find(obj => {return obj.HouseId == houseId;});
if (house == null) continue; //go to next iteration

答案 1 :(得分:0)

.find()方法不是强类型的,它使用对象参数,因此如果传递给它的参数的数据类型不兼容(例如,您传递一个整数的字符串),您在运行时期间会出现错误价值是预期的。)

通常,我通常更喜欢使用.Where()和/或.FirstOrDefault()而不是.Find(),因为您必须指定字段名称,例如

var usr=db.Employees.Where(
    x => x.FirstName=="James" && x.LastName=="Bond").FirstOrDefault();

或者你可以直接写:

var usr=db.Employees.FirstOrDefault(
    x => x.FirstName=="James" && x.LastName=="Bond");

如果没有找到记录,则返回Null

它使代码更加清晰,适用于以后的审核以及可能需要的数据模型更改 - 考虑是否必须在复合主键中添加字段,例如上例中的出生日期:在这种情况下很容易看到您必须将其添加到.Where语句。

因为它是强类型的,所以它还允许您使用智能感知来查找查询中涉及的字段的正确数据类型,方法是右键单击并选择&#34;转到定义。&#34; < / p>

所有这些好处使得故障排除变得更加容易。此外,.Where.FirstOrDefault.Find更通用,因为它们支持多种序列(查看here at SO以获得更详细的说明),并且不限于主键

缺点是.Any().Single().First().Where()(以及他们的...OrDefault()下标)正在生成SQL查询,因此往返于数据库,就像你使用临时查询或调用存储过程一样。

但是,如果您在主键中搜索 ,则必须使用它们。 LinqPad工具很好地展示了EF如何将这些查询转换为SQL代码(假设Northwind为示例数据库):

SELECT TOP (1) 
[Extent1].[EmployeeID] AS [EmployeeID], -- primary key (PK)
[Extent1].[LastName] AS [LastName], [Extent1].[FirstName] AS [FirstName], 
[Extent1].[Title] AS [Title], [Extent1].[TitleOfCourtesy] AS [TitleOfCourtesy], 
[Extent1].[BirthDate] AS [BirthDate], [Extent1].[HireDate] AS [HireDate], 
[Extent1].[Address] AS [Address], [Extent1].[City] AS [City], 
[Extent1].[Region] AS [Region], 
[Extent1].[PostalCode] AS [PostalCode], [Extent1].[Country] AS [Country], 
[Extent1].[HomePhone] AS [HomePhone], [Extent1].[Extension] AS [Extension], 
[Extent1].[Photo] AS [Photo], [Extent1].[Notes] AS [Notes], 
[Extent1].[ReportsTo] AS [ReportsTo], [Extent1].[PhotoPath] AS [PhotoPath]
FROM [dbo].[Employees] AS [Extent1]
WHERE (N'James' = [Extent1].[FirstName]) AND (N'Bond' = [Extent1].[LastName])

在这里您可以看到,尽可能限制结果集中的字段是有意义的,否则生成的查询将返回您不感兴趣的值并返回不必要的大量数据。

.Find()方法仅适用于EmployeeID,因为这是主键(PK)。对于查询中涉及的所有其他字段,您无法使用.Find()并且必须使用其他查询方法(.Where().Single().First()或{{1 }})。

在您的特定情况下,它看起来像(请注意,您应该只在需要时创建一个新对象,因此我已将其移至.Any()语句):

if

但请注意,在这种情况下,可以使用string houseId = data[0]; House dummy = db.Houses.FirstOrDefault(x=>x.HouseId==houseId); if (dummy==null) { House house = new House() { HouseId = houseId }; db.Houses.Add(house); }

进一步优化它
.Any()

如果您不需要从数据库中检索对象,这样可以避免返回不必要的数据(如前所述)。

答案 2 :(得分:0)

我设法解决了这个奇怪的错误。在Context-Constructor中,我只添加了

public ContextEv(string dbName) : base("name=" + dbName)
{
           //Database.SetInitializer(new DropCreateDatabaseAlways<ContextEv>());
}

但是起初我不得不手动删除每个表,因为上面的代码并没有真正做到。在让它运行一次之后我不得不评论代码并再次运行以便表格出现(为什么,我真的不知道......也许你们中的一些人知道)。

非常感谢大家的帮助!我真的从你的答案中学到了一些东西。