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.