Skip to content

Reference

Keeping your app working

Reflection, serialization, DI, EF and XAML all resolve names at runtime — here is what Nebula preserves automatically and how to preserve the rest.

Obfuscation renames types and members. The one thing that breaks an app is when something is looked up by name at runtime — reflection, serialization, DI-by-convention, XAML/Razor binding, config binding — and that name was renamed. This page explains what Nebula preserves for you and how to preserve anything else.

The golden rule: if code (or a framework) finds a type or member by its string name at runtime, that name must be preserved. If it’s found by type identity (typeof, generics, DI by type), obfuscation is transparent.

What Nebula preserves automatically

autoDetect is on by default and scans your assembly for names that must survive:

  • Reflection by string — anything passed to Type.GetType, Assembly.GetType, Activator.CreateInstance, AppDomain, GetMethod/GetProperty/GetField by literal is preserved.
  • Serialization contracts — types/members carrying [Serializable], [DataContract]/[DataMember], System.Text.Json ([JsonPropertyName], [JsonConverter]), Newtonsoft.Json ([JsonProperty], [JsonObject]), or System.Xml.Serialization ([XmlElement], …) attributes are kept, so serialized shapes don’t change.
  • Interface/virtual contracts — members implementing an interface Nebula can’t see, or overriding an external base, are preserved conservatively.

Every run logs what it kept: auto-detect: preserving N name(s) referenced via reflection/serialization/attributes.

Preserving specific names

When something is resolved by name that auto-detect can’t see (e.g. a name built at runtime, or a convention), preserve it explicitly. Three ways, strongest-to-weakest scoped:

1. In the configexclude takes type full names (preserves the type + all members) or Type.Member (one member); excludeNamespaces preserves whole namespaces:

{ "preservePublicApi": true,
  "exclude": ["MyApp.Models.Customer", "MyApp.Api.OrderController.Create"],
  "excludeNamespaces": ["MyApp.PublicContracts"] }

2. With an attribute — annotate the code with the BCL System.Reflection.ObfuscationAttribute (no Nebula reference needed):

[System.Reflection.Obfuscation(Exclude = true, ApplyToMembers = true)]
public class Customer { public string Name { get; set; } }

3. Preserve the whole public surfacepreservePublicApi: true keeps every public/protected name (the default for libraries).

include is the inverse allowlist: when set, only the listed types are eligible for renaming and everything else is preserved — handy to protect just one internal namespace.

Common frameworks

  • JSON / XML serialization — property names become the wire format. Keep the attributes (auto-detected) or exclude the DTOs; otherwise a renamed Customer.Name serializes as a.b and breaks round-tripping with other systems.
  • Dependency injectionAddScoped<IFoo, Foo>() is type-based and safe. Assembly-scanning/convention registration (e.g. “everything ending in Service”) relies on names — preserve the scanned types.
  • Entity Framework Core — entity property names map to columns and migrations reference them; preserve entity types/properties, or use explicit HasColumnName mappings so the DB schema is independent of the obfuscated names.
  • Options / configuration bindingConfiguration.Bind/IOptions<T> map config keys to property names; preserve those settings classes.
  • WPF / MAUI XAML & Blazor — preserve view-models and the members bound in markup (see Protecting different project types).

Strings vs names

String encryption hides string literals in your code; it does not touch member names. Member names are handled by renaming/preserve rules above. So encrypting strings never breaks reflection — a preserved member keeps its name whether or not other strings are encrypted.

A safe workflow

  1. Obfuscate with preservePublicApi: true and autoDetect: true first (conservative).
  2. Run your full test suite / app against the protected build — this is the real check.
  3. If something resolves-by-name breaks, add a targeted exclude or [Obfuscation] attribute — don’t disable a whole pass.
  4. Tighten (turn off preservePublicApi for apps) once it’s green.

If a protected build throws, Troubleshooting maps the exception to the fix, and de-obfuscation turns a renamed stack trace back into original names.