Partcreationpolicy Needs Export
ID |
csharp.partcreationpolicy_needs_export |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
CSharp |
Tags |
code_smell, composition, dependency-injection, misleading-annotation |
Description
Reports a class carrying [PartCreationPolicy(…)] but neither [Export] nor [InheritedExport].
The creation policy tunes how a composition container instantiates a part it publishes, so on a class
the container never publishes it has no effect at all.
Rationale
An inert attribute would be harmless if nobody read it. This one is read by people.
[PartCreationPolicy(CreationPolicy.Shared)] states, in exactly the place a reader looks for that
information, that instances of the class are shared. Without an export nothing enforces it: callers
construct the class themselves, each gets a fresh instance, and the invariants that "shared" implied —
one cache, one connection, one subscription — quietly do not hold.
The usual cause is an export that was removed during a refactor, or one that was never added because the class was written policy-first.
[PartCreationPolicy(CreationPolicy.Shared)] // FLAW — nothing exports this class
public class OrphanCache : ICache
{
public object Get(string key) => null;
}
[Export(typeof(ICache))]
[PartCreationPolicy(CreationPolicy.Shared)] // OK — the policy applies to a real export
public class MemoryCache : ICache
{
public object Get(string key) => null;
}
[InheritedExport]
[PartCreationPolicy(CreationPolicy.NonShared)] // OK — an inherited export is still an export
public class RequestScope
{
}
Remediation
If the class is meant to be a composition part, add the [Export] (or [InheritedExport]) the policy
was written for, naming the contract importers use. If it is not a part, delete the
[PartCreationPolicy] attribute — and, when the sharing it described is genuinely required, enforce
it in code rather than leaving an annotation to imply it.