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

J4N*_*J4N 51 .net c# sql entity-framework

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

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

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

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

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

就像是

context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList();
Run Code Online (Sandbox Code Playgroud)

??

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

目标是避免这种情况:

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
    };
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

和"文件"是我得到的类型context.Files.ToList().这是好班级:

using File = MyProjectNamespace.Common.Data.DataModel.File;
Run Code Online (Sandbox Code Playgroud)

File是我的EF datacontext的已知类:

public ObjectSet<File> Files
{
    get { return _files  ?? (_files = CreateObjectSet<File>("Files")); }
}
private ObjectSet<File> _files;
Run Code Online (Sandbox Code Playgroud)

Lad*_*nka 19

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

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

我在这里使用匿名类型,否则您将得到NotSupportedException:无法在LINQ to Entities查询中构造实体或复杂类型"ProjectName.File".

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

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

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

作为解决所有问题的解决方法,使用表拆分并将有问题的二进制列分离到与主File实体具有1:1关系的另一个实体.

  • 这是放弃EF的原因.绝对是疯了,这就像事情发生的那样,不包括一个大的专栏,它在正常的数据库解决方案中作为单个最大列完美地完成,而所有其余的可用于列出这些对象等等.当前方式的解决方案极大地增加了复杂性 - 无论是添加各种额外类型来进行投影,还是将完全相同的类型转换为相同类型并再次返回的代码; 或者,制作一张1:1的桌子,以及不需要的时候. (10认同)

Yuc*_*uck 11

我会做这样的事情:

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

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

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

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

  • 我已经尝试过了,但是 EF 告诉我不能在选择中放置复杂类型。 (2认同)

Jer*_*yow 8

你可以这样做:

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

或这个:

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

取决于您的EF版本

  • 这对我有用。但是,应该注意,我为空的Data字段添加了一个null列。“选择ID,名称,路径,CreateDate,LastUpdated,空的来自[文件]的AS数据”; (2认同)

Sea*_*ean 6

我之所以有此要求,是因为我有一个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
});
Run Code Online (Sandbox Code Playgroud)

然后我的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)
});
Run Code Online (Sandbox Code Playgroud)

然后我返回DTO列表:

return Ok(dtos);
Run Code Online (Sandbox Code Playgroud)

因此,它使用的投影可能不符合原始海报的要求,但是如果您使用的是DTO类,则无论如何都要进行转换。您可以轻松地执行以下操作以将其返回为实际实体:

var dtos = paginated.Select(o => new Document
{
    Content = o.Content,
    ContentType = o.ContentType,
    DocumentTypeId = o.DocumentTypeId,
    //...
Run Code Online (Sandbox Code Playgroud)

只需几个额外的步骤,但这对我来说很好。


Ham*_*edH 6

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

var files = context.Files.AsNoTracking()
                         .IgnoreProperty(f => f.Report)
                         .ToList();
Run Code Online (Sandbox Code Playgroud)

基本思想是将例如这个查询:

SELECT [f].[Id], [f].[Report], [f].[CreationDate]
FROM [File] AS [f]
Run Code Online (Sandbox Code Playgroud)

进入这个:

SELECT [f].[Id], '' as [Report], [f].[CreationDate]
FROM [File] AS [f]
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到完整的源代码:https : //github.com/aspnet/EntityFrameworkCore/issues/1387#issuecomment-495630292