---
title: "Rule-based entity recognition in C#"
url: https://recursiveai.org/onenlp/cookbook/add-rule-based-entities/
description: "Add labeled entities to text in C# with OneNLP EntityRuler. Use phrase and token rules, retrieve rule IDs and understand overlap precedence."
version: "1.0.0"
---

# Rule-based entity recognition in C#

**OneNLP** is a free, open-source natural language processing library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneNlp) · [GitHub](https://github.com/recursive-ai-oss/onenlp-csharp) · [Docs](https://recursiveai.org/onenlp/docs/) · [Download example (.zip)](https://recursiveai.org/onenlp/cookbook/add-rule-based-entities/add-rule-based-entities.zip)

Rule-based entity recognition labels known names or token patterns without training a model.

No model package is needed.

```shell
dotnet add package RecursiveAI.OneNlp
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using RecursiveAI.OneNlp;
using RecursiveAI.OneNlp.Matching;

var doc = Nlp.Analyze(
    "Acme Corp hired Jo.");
var ruler = new EntityRuler();
ruler.AddPhrase("ORG", "Acme Corp");
ruler.Process(doc);
foreach (var entity in doc.Ents)
    Console.WriteLine(
        $"{entity.Label}: {entity}");
```

```text
ORG: Acme Corp
```

Invalid rule files raise `PatternFormatException`; a valid rule that finds nothing adds no entities.

Unzip [the example](https://recursiveai.org/onenlp/cookbook/add-rule-based-entities/add-rule-based-entities.zip), then `dotnet run`.

## Variations

**Return rule IDs and match an identifier by shape.** In `Xdddd`, `X` means an uppercase letter and each `d` means a digit. Replace `Program.cs` with:

```csharp
using RecursiveAI.OneNlp;
using RecursiveAI.OneNlp.Matching;

var doc = Nlp.Analyze("Acme Corp filed K1234.");
var ruler = new EntityRuler();
ruler.AddPhrase("ORG", "Acme Corp", "acme");
ruler.Add("CASE_NO",
    Pattern.Shape("Xdddd"), "case");
var entities = ruler.FindEntities(doc);
foreach (var entity in entities)
    Console.WriteLine(
        $"{entity.Id}: {entity.Span}");
```

```text
acme: Acme Corp
case: K1234
```

## Related recipes

- [Match text token patterns in C#](https://recursiveai.org/onenlp/cookbook/match-token-patterns/)
- [Match phrases in text in C#](https://recursiveai.org/onenlp/cookbook/match-phrases/)
- [Zero-shot entity extraction in C#](https://recursiveai.org/onenlp/cookbook/extract-custom-entity-types/)

---

## The short answer

Create an `EntityRuler`, add a phrase or token pattern under an entity label, and call `Process(doc)`. The example adds `Acme Corp` as an `ORG` entity. This is a spelling rule, not a statistical prediction about every organization name. No model package is needed for these rules.

## What is an example of a rule-based approach?

A company dictionary is one example: label an exact phrase such as `Acme Corp` as `ORG`. A structured identifier is another: label a token whose shape is one uppercase letter followed by four digits as `CASE_NO`. The variation implements both and prints their rule IDs.

Phrase rules compare `ORTH` by default: the original spelling, including capitalization. `LOWER` compares lowercase spellings using .NET’s invariant culture. `NORM` compares the document’s normalized token value, falling back to lowercase spelling when no value was set.

For case-insensitive phrase rules, replace the ruler’s constructor line with:

```csharp
var ruler = new EntityRuler(false, TokenProperty.Lower);
```

The first argument leaves entity overwriting disabled. The ruler also accepts `SHAPE` for phrase comparison. Token rules can combine tests across consecutive positions, so a rule can depend on nearby words or punctuation as well as the identifier itself.

`Process` writes accepted spans into the document’s entities. Use `FindEntities` when you need the rule ID alongside each candidate selected by the ruler. IDs belong to rules and are not stored in the document’s entity annotations. The variation therefore reads `RuleEntity.Id`, not a property on `doc.Ents`.

According to the [ruler documentation](https://recursiveai.org/onenlp/docs/matching/#entityruler), competing rule matches are considered longest first. Equal lengths prefer the earlier start; an exact tie prefers the rule added first. Once a match is accepted, overlapping rule matches are dropped. This means the order of dictionary registration matters for identical spans with different labels or IDs.

By default, a rule match overlapping an existing entity is dropped. To let rule matches replace overlapping existing entities, set this property after creating the ruler and before calling `Process`:

```csharp
ruler.OverwriteEnts = true;
```

Each existing entity overlapped by an accepted rule match is removed.

## What is the purpose of NER in NLP?

Named entity recognition marks spans and assigns labels so a program can work with mentions instead of scanning raw prose. In this rule-based example, `ORG` identifies the exact company phrase to a downstream consumer. Your application chooses the labels and decides what to do with them; adding a label does not resolve two differently written names to one company.

Rules are useful when you can enumerate the names or state the spelling pattern. They cannot infer an unlisted organization merely because a sentence sounds like it describes one.

Rule entities have confidence `1` under the library’s documented contract. That value is not measured evidence that the phrase is a real organization or that the dictionary is correct. A misspelled rule, an overly broad shape or an ambiguous name can still produce an unwanted entity. Test rules on examples containing both intended mentions and similar text that should remain unmarked.
