是否应采用反射统计多数据库实体中PrimaryImageId属性的使用次数?
Hey there! Let's work through how to count how many times a specific PrimaryImageId is used across all your entities that have the [HasPrimaryImage] attribute and implement IHasPrimaryImage—no hardcoding entity types required.
First, let's break down the core challenge: we need to dynamically find all eligible entity types, then query each one for the target ID. Here's a step-by-step solution:
1. Dynamically Discover Eligible Entity Types
First, we'll use reflection to scan your assembly (or assemblies) and pick out every type that meets your criteria:
using System.Reflection; using System.Linq.Expressions; // Get all concrete types in your executing assembly that have the HasPrimaryImage attribute AND implement IHasPrimaryImage var eligibleEntityTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.GetCustomAttribute<HasPrimaryImageAttribute>() != null && typeof(IHasPrimaryImage).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface) // Skip abstract types/interfaces since we can't instantiate them .ToList();
This gives us a clean list of every entity type we need to query, even if you add new ones later.
2. Query Each Entity Type for the Target ID
Next, we need to run a count query against each of these types. If you're using a generic repository, you'll need to use reflection to invoke the generic count method dynamically. Let's assume your repository has an async method like Task<int> CountAsync<T>(Expression<Func<T, bool>> predicate):
var targetPrimaryImageId = Guid.Parse("your-target-image-id-here"); // Replace with your actual ID int totalUsageCount = 0; var repository = new YourGenericRepository(); // Or use your injected repository instance // Get the generic CountAsync method from your repository class var countMethodInfo = typeof(YourGenericRepository) .GetMethod(nameof(YourGenericRepository.CountAsync), BindingFlags.Public | BindingFlags.Instance); foreach (var entityType in eligibleEntityTypes) { // Build the predicate expression: e => e.PrimaryImageId == targetPrimaryImageId var parameter = Expression.Parameter(entityType, "e"); var primaryImageIdProperty = Expression.Property(parameter, nameof(IHasPrimaryImage.PrimaryImageId)); var targetIdConstant = Expression.Constant(targetPrimaryImageId); var equalityExpression = Expression.Equal(primaryImageIdProperty, targetIdConstant); var predicate = Expression.Lambda(equalityExpression, parameter); // Create the generic version of CountAsync for this specific entity type var genericCountMethod = countMethodInfo.MakeGenericMethod(entityType); // Invoke the method and await the result var countTask = (Task<int>)genericCountMethod.Invoke(repository, new object[] { predicate }); totalUsageCount += await countTask; } Console.WriteLine($"Total times PrimaryImageId {targetPrimaryImageId} is used: {totalUsageCount}");
If You're Using EF Core Directly (No Repository)
If you're working directly with a DbContext, you can simplify this by using DbContext.Set<T> dynamically—no reflection for the query itself:
foreach (var entityType in eligibleEntityTypes) { var dbSet = yourDbContext.Set(entityType); // Use EF.Property to access the PrimaryImageId property without knowing the concrete type var count = await dbSet.CountAsync(e => EF.Property<Guid>(e, nameof(IHasPrimaryImage.PrimaryImageId)) == targetPrimaryImageId); totalUsageCount += count; }
3. Key Optimizations
- Cache Eligible Types: Instead of scanning the assembly every time, cache the list of eligible entity types on app startup to avoid repeated reflection overhead.
- Handle Edge Cases: Add null checks for
countMethodInfo(in case the repository method name changes) and wrap database calls in try/catch blocks to handle connection issues. - Parallel Queries (Optional): If you have many entity types, you can run queries in parallel with
Task.WhenAll, but be mindful of database connection limits to avoid throttling.
This approach lets you avoid hardcoding entity types and automatically includes any new entities you add later that have the [HasPrimaryImage] attribute and implement IHasPrimaryImage.
内容的提问来源于stack exchange,提问作者Carmax

