依赖注入含必填参数时的通用面包屑助手类重构咨询
Absolutely, this refactoring plan is not only feasible—it’s a perfect application of the DRY (Don’t Repeat Yourself) principle that will cut down on redundant code and make your codebase much easier to maintain. Let’s walk through how to implement it properly, plus key optimizations to fix subtle issues in your original code.
Step 1: Implement the Generic CoreFactoryHelper
Since your helper classes only differ by two values (_controller name and the root breadcrumb text), we can extract those as constructor parameters and build a reusable core class. Here’s the refactored code:
public interface IBreadcrumbFactoryHelper { BreadcrumbModel GetBreadcrumb(string name, string action, object routeValues); } public class CoreFactoryHelper : IBreadcrumbFactoryHelper { private readonly string _controllerName; private readonly string _rootBreadcrumbLabel; private readonly UrlHelper _urlHelper; // Inject dependencies + configuration values directly in the constructor public CoreFactoryHelper(UrlHelper urlHelper, string controllerName, string rootBreadcrumbLabel) { _urlHelper = urlHelper ?? throw new ArgumentNullException(nameof(urlHelper)); _controllerName = controllerName ?? throw new ArgumentNullException(nameof(controllerName)); _rootBreadcrumbLabel = rootBreadcrumbLabel ?? throw new ArgumentNullException(nameof(rootBreadcrumbLabel)); } public BreadcrumbModel GetBreadcrumb(string name, string action, object routeValues) { var breadcrumb = new BreadcrumbModel(); // Use the injected UrlHelper instead of pulling from DependencyResolver var rootUrl = _urlHelper.Action("Index", _controllerName).ToLower(); breadcrumb.AddItem(_rootBreadcrumbLabel, rootUrl); // Fix the duplicate condition check from your original code if (!string.IsNullOrWhiteSpace(action) && !string.IsNullOrWhiteSpace(name)) { string itemUrl = routeValues == null ? _urlHelper.Action(action, _controllerName) : _urlHelper.Action(action, _controllerName, routeValues); breadcrumb.AddItem(name, itemUrl?.ToLower()); } return breadcrumb; } }
Key Optimizations & Improvements
1. Ditch DependencyResolver.Current
Your original code pulls a second UrlHelper from the dependency resolver even though it’s already injected—this is unnecessary and breaks proper dependency injection practices. Using the injected instance makes your code easier to test (you can mock UrlHelper for unit tests) and aligns with inversion of control principles.
2. Add Parameter Validation
We’ve added null checks in the constructor to catch invalid configurations early, preventing null reference exceptions later in runtime.
3. Fix the Duplicate Condition
Your original code had a typo: !string.IsNullOrWhiteSpace(action) && !string.IsNullOrWhiteSpace(action)—we corrected this to check both action and name, which is what you intended.
4. Register Specific Instances in Your DI Container
To use this generic helper for different controllers, register dedicated instances in your dependency injection setup. For example, in ASP.NET Core:
// Register for VideoLibrary services.AddScoped<IBreadcrumbFactoryHelper>(sp => new CoreFactoryHelper( sp.GetRequiredService<UrlHelper>(), "VideoLibrary", "Video Library" ) ); // If you need multiple distinct helpers (e.g., for a MusicLibrary), add another registration: services.AddScoped<IBreadcrumbFactoryHelper>(sp => new CoreFactoryHelper( sp.GetRequiredService<UrlHelper>(), "MusicLibrary", "Music Library" ) ); // Alternatively, use named registrations or a marker interface if you need to resolve specific helpers
Why This Works
By extracting the variable parts as constructor parameters, you eliminate all duplicate logic while keeping the flexibility to support any controller’s breadcrumb needs. This approach is scalable—adding a new helper for a BookLibrary is just a new DI registration, no new class required.
内容的提问来源于stack exchange,提问作者Pete

