我正在使用实体框架来连接数据库。我有一个小问题:
我有一个表有一个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;
答案 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
}
]