Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a set of builder APIs that can handle shared state and creation #1541

Open
twsouthwick opened this issue Oct 17, 2023 · 0 comments
Open

Comments

@twsouthwick
Copy link
Member

Overview

Currently, there are a number of static APIs on each package type to enable creating based off of different sources. However, this has a couple of problems:

  • Makes a lot of code duplication to enable any new open related APIs
  • No easy way to create without knowing the package type (for scenarios where we just need a package)
  • Any initialization has to be duplicated and is not passed on to methods such as Clone()

Proposal

Expose a builder pattern with something like this that builds a middleware pipeline to construct the package:

public interface IPackageBuilder<TPackage>
    where TPackage : OpenXmlPackage
{
    /// <summary>
    /// Create an instance of the <typeparamref name="TPackage"/> package.
    /// </summary>
    /// <returns>A <typeparamref name="TPackage"/> instance.</returns>
    TPackage Create();

    /// <summary>
    /// Gets a key/value collection that can be used to share data between middleware.
    /// </summary>
    IDictionary<string, object?> Properties { get; }

    /// <summary>
    /// Add middleware to the package builder.
    /// </summary>
    /// <param name="configure">The middleware to add.</param>
    /// <returns>The <see cref="IPackageBuilder{TPackage}"/>.</returns>
    IPackageBuilder<TPackage> Use(Func<PackageInitializerDelegate<TPackage>, PackageInitializerDelegate<TPackage>> configure);

    /// <summary>
    /// Create a copy of the builder that will be independent of the original, but retains the existing middleware and properties.
    /// </summary>
    /// <returns>A new <see cref="IPackageBuilder{TPackage}"/>.</returns>
    IPackageBuilder<TPackage> Clone();

    /// <summary>
    /// Builds the pipeline to initialize the package. Additional calls to this will return the cached pipeline unless
    /// more middleware has been added.
    /// </summary>
    /// <returns>The pipeline to initialize a package.</returns>
    PackageInitializerDelegate<TPackage> Build();
}

This can enable something like the following:

var factory = WordprocessingDocument.CreateBuilder()
  .Use((package, next) => next(package))
  .UseSomeCommonBehavior()
  .Build();

using var package = factory.Open(stream);

This sets up a factory that will produce a package based on the supplied middleware, which can contain shared state, etc that is needed in each package. It also sets up things like Clone to use this as well so all packages are set up in a consistent way.

An initial implementation is available here: #1474

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant