Skip to content

The PersistenceManager ​

PersistenceManager is the server side of Breeze. It does two things a plain DbContext or NHibernate ISession does not:

  1. Describes the model — turns your mapping into the JSON metadata document the client builds its entity types from.
  2. Applies a save bundle — takes the JSON a client sends, turns it back into entities in the right states, saves them in one transaction, and reports back the server-assigned keys.

It is abstract. You use the subclass for your ORM.

ORMClassConstructed from
Entity Framework CoreEFPersistenceManager<T>your DbContext
NHibernateNHPersistenceManageran ISession

Subclassing it ​

Write one per context. Even with no members it is worth having, because interceptors and metadata overrides go here rather than in the controller:

cs
using Breeze.Persistence.EFCore;

public class NorthwindPersistenceManager : EFPersistenceManager<NorthwindContext> {
  public NorthwindPersistenceManager(NorthwindContext context) : base(context) { }
}

Context gives the typed DbContext back, which is what query actions return sets from:

cs
[HttpGet]
public IQueryable<Customer> Customers() => _pm.Context.Customers;

Lifetime ​

Create one per request, in the controller's constructor, from a DbContext that DI has already scoped to the request:

cs
public PersistenceManagerGuideController(NorthwindContext context) {
  _pm = new NorthwindPersistenceManager(context);
}

It wraps a DbContext, so it inherits the DbContext's rules: not thread-safe, and not meant to outlive the request. Registering one as a singleton shares one change-tracker between every concurrent request.

WARNING

A PersistenceManager is built for one save. It keeps the state of the save in progress, and a second SaveChanges on the same instance rebuilds that state rather than adding to it. This is the ordinary case — one instance, one request, one save — but it is worth knowing before you reach for a longer-lived one.

NHibernate is the same shape with an ISession in place of the DbContext; the test server builds an ISessionFactory once as a singleton and opens a session per request from it.

What you get without writing anything ​

MemberWhat it does
Metadata()the JSON metadata document, built from your mapping
SaveChangesAsyncapplies a save bundle in a transaction, returns a SaveResult
SaveChangesthe synchronous form
SaveOptionswhat the client sent alongside the changes, including its Tag
KeyGeneratorhow temporary client keys become real ones

Two ways to intercept a save ​

Every hook exists both as a delegate property, set per request, and as a virtual method, overridden once in your subclass. They do the same work; pick by how widely the rule applies.

cs
[HttpPost]
public Task<SaveResult> SaveWithAudit([FromBody] JObject saveBundle) {
  _pm.BeforeSaveEntityDelegate = SetAuditFields;
  return _pm.SaveChangesAsync(saveBundle);
}

Once, in the subclass — a rule for every save through this manager:

cs
public class AuditedPersistenceManager : EFPersistenceManager<NorthwindContext> {
  public AuditedPersistenceManager(NorthwindContext context) : base(context) { }

  protected override bool BeforeSaveEntity(EntityInfo entityInfo) {
    // return false to drop this entity from the save
    return true;
  }
}

Saving covers what each hook receives and when it runs.

Generated keys ​

When a client adds an entity it invents a temporary key, and the server has to tell it what the real one turned out to be. For an identity column the ORM already knows, and Breeze reports the mapping back with no help from you — see key mappings.

For a key the database does not generate, set a IKeyGenerator. NumericKeyGenerator is the one that ships, which draws from a NextId table:

cs
_pm.KeyGenerator = new NumericKeyGenerator((DbConnection)_pm.GetDbConnection());

Metadata from somewhere else ​

EFPersistenceManager<T> builds metadata from the EF model. To supply your own instead — a hand-written document, or one for types EF does not map — override BuildAltJsonMetadata(); returning null, the default, keeps the generated document. See Metadata.

Released under the MIT License.