Skip to content

Commit 309a304

Browse files
Improve S2971 to suggest to use AsEnumerable in LINQ database query (#8593)
1 parent 195ae28 commit 309a304

File tree

7 files changed

+234
-73
lines changed

7 files changed

+234
-73
lines changed

analyzers/src/SonarAnalyzer.CSharp/Rules/CollectionQuerySimplification.cs

+99-71
Large diffs are not rendered by default.

analyzers/src/SonarAnalyzer.Common/Helpers/KnownType.cs

+12
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public sealed partial class KnownType
8989
public static readonly KnownType Microsoft_EntityFramework_DbContext = new("System.Data.Entity.DbContext");
9090
public static readonly KnownType Microsoft_EntityFrameworkCore_DbContext = new("Microsoft.EntityFrameworkCore.DbContext");
9191
public static readonly KnownType Microsoft_EntityFrameworkCore_DbContextOptionsBuilder = new("Microsoft.EntityFrameworkCore.DbContextOptionsBuilder");
92+
public static readonly KnownType Microsoft_EntityFrameworkCore_DbSet_TEntity = new("Microsoft.EntityFrameworkCore.DbSet", "TEntity");
9293
public static readonly KnownType Microsoft_EntityFrameworkCore_Migrations_Migration = new("Microsoft.EntityFrameworkCore.Migrations.Migration");
9394
public static readonly KnownType Microsoft_EntityFrameworkCore_MySQLDbContextOptionsExtensions = new("Microsoft.EntityFrameworkCore.MySQLDbContextOptionsExtensions");
9495
public static readonly KnownType Microsoft_EntityFrameworkCore_NpgsqlDbContextOptionsExtensions = new("Microsoft.EntityFrameworkCore.NpgsqlDbContextOptionsExtensions");
@@ -256,10 +257,14 @@ public sealed partial class KnownType
256257
public static readonly KnownType System_Data_Common_CommandTrees_DbExpression = new("System.Data.Common.CommandTrees.DbExpression");
257258
public static readonly KnownType System_Data_DataSet = new("System.Data.DataSet");
258259
public static readonly KnownType System_Data_DataTable = new("System.Data.DataTable");
260+
public static readonly KnownType System_Data_Entity_Core_Objects_ObjectQuery = new("System.Data.Entity.Core.Objects.ObjectQuery");
259261
public static readonly KnownType System_Data_Entity_Database = new("System.Data.Entity.Database");
260262
public static readonly KnownType System_Data_Entity_DbSet = new("System.Data.Entity.DbSet");
261263
public static readonly KnownType System_Data_Entity_DbSet_TEntity = new("System.Data.Entity.DbSet", "TEntity");
264+
public static readonly KnownType System_Data_Entity_Infrastructure_DbQuery = new("System.Data.Entity.Infrastructure.DbQuery");
265+
public static readonly KnownType System_Data_Entity_Infrastructure_DbQuery_TResult = new("System.Data.Entity.Infrastructure.DbQuery", "TResult");
262266
public static readonly KnownType System_Data_IDbCommand = new("System.Data.IDbCommand");
267+
public static readonly KnownType System_Data_Linq_ITable = new("System.Data.Linq.ITable");
263268
public static readonly KnownType System_Data_Odbc_OdbcCommand = new("System.Data.Odbc.OdbcCommand");
264269
public static readonly KnownType System_Data_Odbc_OdbcDataAdapter = new("System.Data.Odbc.OdbcDataAdapter");
265270
public static readonly KnownType System_Data_OracleClient_OracleCommand = new("System.Data.OracleClient.OracleCommand");
@@ -600,6 +605,13 @@ public sealed partial class KnownType
600605
System_Runtime_CompilerServices_CallerFilePathAttribute,
601606
System_Runtime_CompilerServices_CallerLineNumberAttribute,
602607
System_Runtime_CompilerServices_CallerMemberNameAttribute);
608+
public static readonly ImmutableArray<KnownType> DatabaseBaseQueryTypes =
609+
ImmutableArray.Create(
610+
System_Data_Entity_Infrastructure_DbQuery,
611+
System_Data_Entity_Infrastructure_DbQuery_TResult,
612+
Microsoft_EntityFrameworkCore_DbSet_TEntity,
613+
System_Data_Linq_ITable,
614+
System_Data_Entity_Core_Objects_ObjectQuery);
603615
public static readonly ImmutableArray<KnownType> FloatingPointNumbers =
604616
ImmutableArray.Create(
605617
System_Half,

analyzers/tests/SonarAnalyzer.Test/Rules/CollectionQuerySimplificationTest.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,34 @@ public class CollectionQuerySimplificationTest
2929

3030
[TestMethod]
3131
public void CollectionQuerySimplification() =>
32-
builder.AddPaths("CollectionQuerySimplification.cs").Verify();
32+
builder.AddPaths("CollectionQuerySimplification.cs")
33+
.Verify();
34+
#if NETFRAMEWORK
35+
36+
[TestMethod]
37+
public void CollectionQuerySimplification_NetFx() =>
38+
builder.AddPaths("CollectionQuerySimplification.NetFx.cs")
39+
.AddReferences(FrameworkMetadataReference.SystemDataLinq)
40+
.Verify();
41+
42+
#endif
3343

3444
#if NET
3545

3646
[TestMethod]
3747
public void CollectionQuerySimplification_CSharp9() =>
3848
builder.AddPaths("CollectionQuerySimplification.CSharp9.cs")
49+
.AddReferences(GetReferencesEntityFrameworkNet())
3950
.WithTopLevelStatements()
4051
.Verify();
4152

42-
#endif
53+
private static IEnumerable<MetadataReference> GetReferencesEntityFrameworkNet() =>
54+
Enumerable.Empty<MetadataReference>()
55+
.Concat(NuGetMetadataReference.MicrosoftEntityFrameworkCore("2.2.6"))
56+
.Concat(NuGetMetadataReference.MicrosoftEntityFrameworkCoreRelational("2.2.6"))
57+
.Concat(NuGetMetadataReference.EntityFramework("6.2.0"))
58+
.Concat(NuGetMetadataReference.SystemComponentModelTypeConverter());
4359

60+
#endif
4461
}
4562
}

analyzers/tests/SonarAnalyzer.Test/TestCases/CollectionQuerySimplification.CSharp9.cs

+76
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,79 @@
99
list.Select((object _, int _) => 1).Any(element => element != null); // Compliant
1010

1111
list.Select((element, col) => element as object).Any(element => element != null); //Noncompliant {{Use 'OfType<object>()' here instead.}}
12+
13+
14+
// https://github.com/SonarSource/sonar-dotnet/issues/3604
15+
public class EntityFrameworkReproGH3604
16+
{
17+
public class MyEntity
18+
{
19+
public int Id { get; set; }
20+
}
21+
22+
public class MyDbContext : Microsoft.EntityFrameworkCore.DbContext
23+
{
24+
public Microsoft.EntityFrameworkCore.DbSet<MyEntity> MyEntities { get; set; }
25+
}
26+
27+
public void GetEntitiesFromEntityFrameworkCoreDbContext(MyDbContext dbContext)
28+
{
29+
_ = dbContext.MyEntities.OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
30+
// ^^^^^^
31+
_ = dbContext.MyEntities.ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
32+
// ^^^^^^
33+
_ = (from v in dbContext.MyEntities
34+
orderby v.Id
35+
select v).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
36+
// ^^^^^^
37+
}
38+
39+
public void GetEntitiesFromEntityFrameworkCoreDbSet(Microsoft.EntityFrameworkCore.DbSet<MyEntity> entities)
40+
{
41+
_ = entities.OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
42+
// ^^^^^^
43+
_ = entities.ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
44+
// ^^^^^^
45+
_ = (from v in entities
46+
orderby v.Id
47+
select v).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
48+
// ^^^^^^
49+
}
50+
51+
public void GetEntitiesFromEntityFrameworkDbSet_TEntity(System.Data.Entity.DbSet<MyEntity> entities)
52+
{
53+
_ = entities.OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
54+
// ^^^^^^
55+
_ = entities.ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
56+
// ^^^^^^
57+
_ = (from v in entities
58+
orderby v.Id
59+
select v).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
60+
// ^^^^^^
61+
}
62+
63+
public void GetEntitiesFromEntityFrameworkDbSet(System.Data.Entity.DbSet entities)
64+
{
65+
_ = entities.Cast<MyEntity>().OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
66+
// ^^^^^^
67+
_ = entities.Cast<MyEntity>().ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
68+
// ^^^^^^
69+
}
70+
71+
public void GetEntitiesFromEntityFrameworkObjectQuery_TEntity(System.Data.Entity.Core.Objects.ObjectQuery<MyEntity> entities)
72+
{
73+
_ = entities.OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
74+
// ^^^^^^
75+
_ = entities.ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
76+
// ^^^^^^
77+
_ = (from v in entities
78+
orderby v.Id
79+
select v).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
80+
// ^^^^^^
81+
}
82+
83+
public bool SomeTest(MyEntity entity)
84+
{
85+
return true;
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Linq;
2+
3+
public class MyEntity
4+
{
5+
public int Id { get; set; }
6+
}
7+
8+
public class DataLinq
9+
{
10+
public void GetEntitiesFromLinqToSqlTable(System.Data.Linq.Table<MyEntity> entities)
11+
{
12+
_ = entities.OrderBy(v => v.Id).ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
13+
// ^^^^^^
14+
_ = entities.ToList().Where(SomeTest).ToList(); // Noncompliant {{Use 'AsEnumerable' here instead.}}
15+
// ^^^^^^
16+
}
17+
18+
public bool SomeTest(MyEntity entity)
19+
{
20+
return true;
21+
}
22+
}

analyzers/tests/SonarAnalyzer.Test/TestCases/CollectionQuerySimplification.cs

+5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ public CollectionQuerySimplification(List<object> coll)
1111
{
1212
var x = coll.Select(element => element as object).Any(element => element != null); // Noncompliant {{Use 'OfType<object>()' here instead.}}
1313
// ^^^^^^
14+
var x4 = coll.Select(element => element as object).Any(element => element == null);
15+
var x2 = coll.Select(element => element as object).Any(element => null != element); // Noncompliant {{Use 'OfType<object>()' here instead.}}
16+
// ^^^^^^
17+
var x3 = coll.Select(element => element as IList<int>).Any(element => element.Count != 0); // Compliant
1418
x = coll.Select((element) => ((element as object))).Any(element => (element != null) && CheckCondition(element) && true); // Noncompliant use OfType
19+
x = coll.Select(element => ((element as object))).Any(element => (element != null) && CheckCondition(element) && true); // Noncompliant use OfType
1520
var y = coll.Where(element => element is object).Select(element => element as object); // Noncompliant use OfType
1621
// ^^^^^
1722
y = coll.Where(element => element is object).Select(element => element as object[]);

analyzers/tests/SonarAnalyzer.TestFramework/MetadataReferences/FrameworkMetadataReference.cs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public static class FrameworkMetadataReference
3737
public static References SystemIOCompressionFileSystem { get; } = Create("System.IO.Compression.FileSystem.dll");
3838
public static References SystemCore { get; } = Create("System.Core.dll");
3939
public static References SystemData { get; } = Create("System.Data.dll");
40+
public static References SystemDataLinq { get; } = Create("System.Data.Linq.dll");
4041
public static References SystemDataOracleClient { get; } = Create("System.Data.OracleClient.dll");
4142
public static References SystemDirectoryServices { get; } = Create("System.DirectoryServices.dll");
4243
public static References SystemDrawing { get; } = Create("System.Drawing.dll");

0 commit comments

Comments
 (0)