从entityframework中检索一个没有ONE字段的对象

时间:2012-01-23 14:52:58

标签: c# .net sql entity-framework

我正在使用实体框架来连接数据库。我有一个小问题:

我有一个表有一个varbinary(MAX)列(带有文件流)。

我正在使用SQL请求来管理“数据”部分,而其余部分使用EF(文件的元数据)。

我有一个代码必须获取文件的所有文件id,filename,guid,修改日期等。这根本不需要“数据”字段。

有没有办法检索List但没有填充此列?

这样的东西
context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList();

...

我知道我可以创建匿名对象,但我需要将结果传递给方法,所以没有匿名方法。我不想把它放在匿名类型的列表中,然后创建一个非匿名类型的列表(文件)。

目标是避免这种情况:

using(RsSolutionsEntities context = new RsSolutionsEntities())
{
    var file = context.Files
        .Where(f => f.Id == idFile)
        .Select(f => new {
            f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
            f.DateModification, f.FileId
        }).FirstOrDefault();

    return new File() {
        DataType = file.DataType, DateModification = file.DateModification,
        FileId = file.FileId, FileName = file.FileName, Id = file.Id,
        MimeType = file.MimeType, Size = file.Size
    };
}

(我在这里使用的是匿名类型,否则你会得到一个NotSupportedException:无法在LINQ to Entities查询中构造实体或复杂类型'ProjectName.File'。)

(例如,此代码抛出先前的异常:

File file2 = context.Files.Where(f => f.Id == idFile)
  .Select(f => new File() {Id = f.Id, DataType = f.DataType}).FirstOrDefault();

和“文件”是我使用context.Files.ToList()获得的类型。这是一个很好的课程:

using File = MyProjectNamespace.Common.Data.DataModel.File;

File是我的EF datacontext的已知类:

public ObjectSet<File> Files
{
    get { return _files  ?? (_files = CreateObjectSet<File>("Files")); }
}
private ObjectSet<File> _files;

9 个答案:

答案 0 :(得分:18)

  

有没有办法检索List但没有填充此列?

不要没有您想要避免的投影。如果列已映射,则它是您实体的自然部分。没有此列的实体不完整 - 它是不同的数据集=投影。

  

我在这里使用的是匿名类型,否则你会得到一个   NotSupportedException:实体或复杂类型“ProjectName.File”   不能在LINQ to Entities查询中构造。

如异常所示,您无法投影到映射的实体。我在上面提到了原因 - 投影使得不同的数据集和EF不喜欢“部分实体”。

  

错误16错误3023:从行开始映射片段时出现问题   2717:表中的Files Files.Data文件必须映射:它没有   默认值且不可为空。

从设计师中删除属性是不够的。您必须将EDMX作为XML打开并从SSDL中删除列,这将使您的模型非常脆弱(每次从数据库更新都会使您的列回归)。如果您不想映射列,则应使用不带列的数据库视图并映射视图而不是表,但您将无法插入数据。

作为解决所有问题的解决方法,请使用table splitting并将有问题的二进制列分隔给与您的主File实体具有1:1关系的另一个实体。

答案 1 :(得分:9)

我会做这样的事情:

var result = from thing in dbContext.Things
             select new Thing {
                 PropertyA = thing.PropertyA,
                 Another = thing.Another
                 // and so on, skipping the VarBinary(MAX) property
             };

Thing是您知道如何实现的实体。生成的SQL语句不应在其结果集中包含大列,因为查询中不需要它。

编辑:从您的编辑中,您收到错误 NotSupportedException:无法在LINQ to Entities查询中构造实体或复杂类型“ProjectName.File”。因为您尚未将该类映射为实体。您不能在EF不知道的LINQ to Entities查询中包含对象,并期望它生成适当的SQL语句。

您可以映射其定义中排除VarBinary(MAX)列的其他类型,或使用上述代码。

答案 2 :(得分:7)

你可以这样做:

var files = dbContext.Database.SqlQuery<File>("select FileId, DataType, MimeType from Files");

或者这个:

var files = objectContext.ExecuteStoreQuery<File>("select FileId, DataType, MimeType from Files");

取决于您的EF版本

答案 3 :(得分:5)

我有这个要求,因为我有一个Document实体,其中Content字段包含文件内容,即大小为100MB,我有一个我想要返回的搜索功能其余的专栏。

我选择使用投影:

IQueryable<Document> results = dbContext.Documents.Include(o => o.UploadedBy).Select(o => new {
    Content = (string)null,
    ContentType = o.ContentType,
    DocumentTypeId = o.DocumentTypeId,
    FileName = o.FileName,
    Id = o.Id,
    // etc. even with related entities here like:
    UploadedBy = o.UploadedBy
});

然后我的WebApi控制器将此results对象传递给公共分页函数,该函数应用.Skip.Take.ToList

这意味着当查询执行时,它无法访问Content列,因此不会触及100MB数据,并且查询速度与您想要的一样快期待它。

接下来,我将它转发回我的DTO类,在这种情况下,它几乎与实体类完全相同,所以这可能不是你需要实现的步骤,但它遵循我的典型WebApi编码模式,所以:

var dtos = paginated.Select(o => new DocumentDTO
{
    Content = o.Content,
    ContentType = o.ContentType,
    DocumentTypeId = o.DocumentTypeId,
    FileName = o.FileName,
    Id = o.Id,
    UploadedBy = o.UploadedBy == null ? null : ModelFactory.Create(o.UploadedBy)
});

然后我返回DTO列表:

return Ok(dtos);

所以它使用了投影,这可能不符合原始海报的要求,但是如果您正在使用DTO课程,那么无论如何您都要进行转换。您可以轻松地执行以下操作将它们作为实际实体返回:

var dtos = paginated.Select(o => new Document
{
    Content = o.Content,
    ContentType = o.ContentType,
    DocumentTypeId = o.DocumentTypeId,
    //...

只需几步,但这对我来说很有效。

答案 4 :(得分:1)

  

我在这里使用匿名类型,否则你会得到一个   NotSupportedException:实体或复杂类型&#39; ProjectName.File&#39;   不能在LINQ to Entities查询中构造。

var file = context.Files
        .Where(f => f.Id == idFile)
        .FirstOrDefault() // You need to exeucte the query if you want to reuse the type
        .Select(f => new {
            f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
            f.DateModification, f.FileId
        }).FirstOrDefault();

将表格进一步去标准化也是一种不错的做法,即一个带有元数据,一个带有效负载以避免投影。投影将起作用,唯一的问题是,需要在将新列添加到表中时进行编辑。

答案 5 :(得分:0)

我希望分享我尝试解决此问题,以防其他人处于相同的情况。

我从Jeremy Danyow建议开始,对我来说这是一个不那么痛苦的选择。

// You need to include all fields in the query, just make null the ones you don't want.
var results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName");

就我而言,我需要一个IQueryable<>结果对象,所以最后添加了AsQueryable()。这当然让我添加了对.Where.Take以及我们都知道的其他命令的调用,并且它们运行良好。但有一点需要注意:

正常代码(基本上是context.myEntity.AsQueryable())返回System.Data.Entity.DbSet<Data.DataModel.myEntity>,而此方法返回System.Linq.EnumerableQuery<Data.DataModel.myEntity>

显然这意味着我的自定义查询被执行&#34;按原样#34;一旦需要,我后来添加的过滤就在之后完成而不是在数据库中完成。

因此,我试图通过使用EF创建的确切查询来模仿实体框架的对象,即使使用那些[Extent1]别名,但它没有起作用。分析结果对象时,其查询结束如

FROM [dbo].[TableName] AS [Extent1].Where(c => ...

而不是预期的

FROM [dbo].[TableName] AS [Extent1] WHERE ([Extent1]...

无论如何,这是有效的,只要表格不大,这种方法就足够快了。否则,除了通过连接字符串手动添加条件之外别无选择,例如经典动态SQL。一个非常基本的例子,以防你不知道我在说什么:

string query = "SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName";
if (parameterId.HasValue)
    query += " WHERE Field1 = " + parameterId.Value.ToString();
var results = context.Database.SqlQuery<myEntity>(query);

如果您的方法有时需要此字段,您可以添加bool参数,然后执行以下操作:

IQueryable<myEntity> results;
if (excludeBigData)
    results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName").AsQueryable();
else
    results = context.myEntity.AsQueryable();

如果有人设法使Linq扩展正常工作,就像它是原始EF对象一样,请发表评论,以便我可以更新答案。

答案 6 :(得分:0)

我试过了:

在edmx图表(EF 6)中,我单击了要隐藏在EF中的列,在其属性上,您可以将其getter和setter设置为private。那样,对我而言,它有效。

我返回一些包含用户引用的数据,所以我想隐藏密码字段,即使它已加密和腌制,我只是不想在我的json上,而且我没有&#39 ; t想做一个:

Select(col => new {}) 

因为创建和维护很痛苦,特别是对于有很多关系的大桌子。

使用此方法的缺点是,如果您重新生成模型,则需要再次修改其getter和setter。

答案 7 :(得分:0)

对于EF Core 2 我实现了这样的解决方案:

var files = context.Files.AsNoTracking()
                         .IgnoreProperty(f => f.Report)
                         .ToList();

基本思路是例如查询以下内容:

SELECT [f].[Id], [f].[Report], [f].[CreationDate]
FROM [File] AS [f]

对此:

SELECT [f].[Id], '' as [Report], [f].[CreationDate]
FROM [File] AS [f]

您可以在此处查看完整的源代码: https://github.com/aspnet/EntityFrameworkCore/issues/1387#issuecomment-495630292

答案 8 :(得分:0)

使用 Entity Framework Power Tools,您可以在 efpt.config.json 中执行以下操作:

"Tables": [
  {
     "ExcludedColumns": [
        "FileData"
     ],
     "Name": "[dbo].[Attachment]",
     "ObjectType": 0
  }
]