Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,89 @@ Will result in the following verified file with all data in the database:
<!-- endSnippet -->


## Model diagram

An `IModel` can be verified as a [Mermaid ER diagram](https://mermaid.js.org/syntax/entityRelationshipDiagram.html), using [DbToMermaid](https://github.com/SimonCropp/DbToMermaid). EF Core only.

<!-- snippet: ModelDiagram -->
<a id='snippet-ModelDiagram'></a>
```cs
await Verify(data.Model);
```
<sup><a href='/src/Verify.EntityFramework.Tests/ModelDiagramTests.cs#L10-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-ModelDiagram' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Result: [ModelDiagramTests.Markdown.verified.md](/src/Verify.EntityFramework.Tests/ModelDiagramTests.Markdown.verified.md)

The diagram can be verified as markdown, svg, png, or any combination. One file is verified for each format.

<!-- snippet: ModelDiagramFormats -->
<a id='snippet-ModelDiagramFormats'></a>
```cs
await Verify(data.Model)
.ModelAsDiagram(
ModelDiagramFormat.Markdown |
ModelDiagramFormat.Svg |
ModelDiagramFormat.Png);
```
<sup><a href='/src/Verify.EntityFramework.Tests/ModelDiagramTests.cs#L22-L30' title='Snippet source file'>snippet source</a> | <a href='#snippet-ModelDiagramFormats' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Result:

* [ModelDiagramTests.Formats.verified.md](/src/Verify.EntityFramework.Tests/ModelDiagramTests.Formats.verified.md)
* [ModelDiagramTests.Formats.verified.svg](/src/Verify.EntityFramework.Tests/ModelDiagramTests.Formats.verified.svg)
* [ModelDiagramTests.Formats.verified.png](/src/Verify.EntityFramework.Tests/ModelDiagramTests.Formats.verified.png)

<img src="/src/Verify.EntityFramework.Tests/ModelDiagramTests.Formats.verified.png" alt="ModelDiagramTests.Formats.verified.png">

Available values:

<!-- snippet: ModelDiagramFormat.cs -->
<a id='snippet-ModelDiagramFormat.cs'></a>
```cs
namespace VerifyTests.EntityFramework;

/// <summary>
/// The files produced when a model is verified as a diagram. Can be combined.
/// </summary>
[Flags]
public enum ModelDiagramFormat
{
/// <summary>
/// A Mermaid ER diagram in a markdown code block. Uses the `md` extension.
/// </summary>
Markdown = 1,

/// <summary>
/// The diagram rendered to an image. Uses the `svg` extension.
/// </summary>
Svg = 2,

/// <summary>
/// The diagram rendered to an image. Uses the `png` extension.
/// </summary>
Png = 4,

All = Markdown | Svg | Png
}
```
<sup><a href='/src/Verify.EntityFramework/ModelDiagramFormat.cs#L1-L25' title='Snippet source file'>snippet source</a> | <a href='#snippet-ModelDiagramFormat.cs' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

A rendered png may not be pixel identical across machines, so compare using [ssim](https://github.com/VerifyTests/Verify/blob/main/docs/comparer.md):

<!-- snippet: UseSsimForPng -->
<a id='snippet-UseSsimForPng'></a>
```cs
VerifierSettings.UseSsimForPng();
```
<sup><a href='/src/Verify.EntityFramework.Tests/ModuleInitializer.cs#L27-L31' title='Snippet source file'>snippet source</a> | <a href='#snippet-UseSsimForPng' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The svg and png are rendered by [Naiad](https://github.com/Papyrine/Naiad). Its build check requires a sponsorship, or exemption, property to be set in the consuming project. The build error lists the options.


## IgnoreNavigationProperties

`IgnoreNavigationProperties` extends `SerializationSettings` to exclude all navigation properties from serialization:
Expand Down
4 changes: 3 additions & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project>
<PropertyGroup>
<NoWarn>CS1591;CS0649;CS8632;EF1001;NU1608;NU1109</NoWarn>
<Version>16.0.0</Version>
<Version>16.1.0</Version>
<LangVersion>preview</LangVersion>
<AssemblyVersion>1.0.0</AssemblyVersion>
<PackageTags>EntityFramework, Verify</PackageTags>
Expand All @@ -13,5 +13,7 @@
<SuppressNETCoreSdkPreviewMessage>true</SuppressNETCoreSdkPreviewMessage>
<Verify_SponsorshipExemption>OpenSource</Verify_SponsorshipExemption>
<Verify_SponsorshipExemptionUntil>2027-08</Verify_SponsorshipExemptionUntil>
<Papyrine_SponsorshipExemption>OpenSource</Papyrine_SponsorshipExemption>
<Papyrine_SponsorshipExemptionUntil>2027-08</Papyrine_SponsorshipExemptionUntil>
</PropertyGroup>
</Project>
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<ItemGroup>
<PackageVersion Include="EfClassicLocalDb" Version="27.2.3" />
<PackageVersion Include="EfLocalDb" Version="27.2.3" />
<PackageVersion Include="EfToMermaid" Version="2.2.0" />
<PackageVersion Include="EntityFramework" Version="6.5.2" />
<PackageVersion Include="MarkdownSnippets.MsBuild" Version="28.5.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
```mermaid
erDiagram
Companies["**Companies**"] {
int Id pk
nvarchar(max) Name
}
Employees["**Employees**"] {
int Id pk
int Age
int CompanyId
nvarchar(max) Name
}
Companies ||--o{ Employees : "FK_Employees_Companies_CompanyId"
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
```mermaid
erDiagram
Companies["**Companies**"] {
int Id pk
nvarchar(max) Name
}
Employees["**Employees**"] {
int Id pk
int Age
int CompanyId
nvarchar(max) Name
}
Companies ||--o{ Employees : "FK_Employees_Companies_CompanyId"
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions src/Verify.EntityFramework.Tests/ModelDiagramTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using VerifyTests.EntityFramework;

public class ModelDiagramTests
{
[Test]
public async Task Markdown()
{
await using var data = BuildData();

#region ModelDiagram

await Verify(data.Model);

#endregion
}

[Test]
public async Task Formats()
{
await using var data = BuildData();

#region ModelDiagramFormats

await Verify(data.Model)
.ModelAsDiagram(
ModelDiagramFormat.Markdown |
ModelDiagramFormat.Svg |
ModelDiagramFormat.Png);

#endregion
}

[Test]
public async Task SvgOnly()
{
await using var data = BuildData();
await Verify(data.Model)
.ModelAsDiagram(ModelDiagramFormat.Svg);
}

[Test]
public async Task NoFormat()
{
var settings = new VerifySettings();
await Assert.That(() => settings.ModelAsDiagram(0))
.Throws<ArgumentException>();
}

static SampleDbContext BuildData()
{
var options = new DbContextOptionsBuilder<SampleDbContext>();
options.UseSqlServer("fake");
return new(options.Options);
}
}
6 changes: 6 additions & 0 deletions src/Verify.EntityFramework.Tests/ModuleInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public static void Init()
[ModuleInitializer]
public static void InitOther()
{
#region UseSsimForPng

VerifierSettings.UseSsimForPng();

#endregion

VerifierSettings.InitializePlugins();
Recording.IgnoreNames("sql");
}
Expand Down
65 changes: 65 additions & 0 deletions src/Verify.EntityFramework/ModelDiagram.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using DbToMermaid;

namespace VerifyTests;

public static class VerifyEntityFrameworkModelDiagram
{
const string contextKey = "EntityFramework.ModelDiagram";

/// <summary>
/// Select the files produced when an <see cref="IModel" /> is verified as a Mermaid ER diagram.
/// </summary>
/// <param name="settings">The settings to apply to.</param>
/// <param name="formats">The files to produce. Can be combined, with one file verified per format.</param>
public static SettingsTask ModelAsDiagram(
this SettingsTask settings,
ModelDiagramFormat formats = ModelDiagramFormat.Markdown)
{
settings.CurrentSettings.ModelAsDiagram(formats);
return settings;
}

/// <inheritdoc cref="ModelAsDiagram(SettingsTask, ModelDiagramFormat)" />
public static void ModelAsDiagram(
this VerifySettings settings,
ModelDiagramFormat formats = ModelDiagramFormat.Markdown)
{
if ((formats & ModelDiagramFormat.All) == 0)
{
throw new ArgumentException("At least one format is required.", nameof(formats));
}

settings.Context[contextKey] = formats;
}

internal static async Task<ConversionResult> Convert(IModel model, IReadOnlyDictionary<string, object> context)
{
var formats = ModelDiagramFormat.Markdown;
if (context.TryGetValue(contextKey, out var value))
{
formats = (ModelDiagramFormat) value;
}

var targets = new List<Target>();

if (formats.HasFlag(ModelDiagramFormat.Markdown))
{
var markdown = await EfToMermaid.RenderMarkdown(model);
targets.Add(new("md", markdown));
}

if (formats.HasFlag(ModelDiagramFormat.Svg))
{
var svg = await EfToMermaid.RenderSvg(model);
targets.Add(new("svg", svg));
}

if (formats.HasFlag(ModelDiagramFormat.Png))
{
var png = await EfToMermaid.RenderPng(model);
targets.Add(new("png", new MemoryStream(png)));
}

return new(null, targets);
}
}
25 changes: 25 additions & 0 deletions src/Verify.EntityFramework/ModelDiagramFormat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace VerifyTests.EntityFramework;

/// <summary>
/// The files produced when a model is verified as a diagram. Can be combined.
/// </summary>
[Flags]
public enum ModelDiagramFormat
{
/// <summary>
/// A Mermaid ER diagram in a markdown code block. Uses the `md` extension.
/// </summary>
Markdown = 1,

/// <summary>
/// The diagram rendered to an image. Uses the `svg` extension.
/// </summary>
Svg = 2,

/// <summary>
/// The diagram rendered to an image. Uses the `png` extension.
/// </summary>
Png = 4,

All = Markdown | Svg | Png
}
1 change: 1 addition & 0 deletions src/Verify.EntityFramework/Verify.EntityFramework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EfToMermaid" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" />
<PackageReference Include="Microsoft.SqlServer.TransactSql.ScriptDom" />
<PackageReference Include="Verify" />
Expand Down
1 change: 1 addition & 0 deletions src/Verify.EntityFramework/VerifyEntityFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ public static void Initialize(IModel? model, bool recordCommands)
VerifierSettings.RegisterFileConverter(
QueryableToSql,
(target, _) => QueryableConverter.IsQueryable(target));
VerifierSettings.RegisterFileConverter<IModel>(VerifyEntityFrameworkModelDiagram.Convert);
VerifierSettings.IgnoreMembersWithType(typeof(IDbContextFactory<>));
VerifierSettings.IgnoreMembersWithType<DbContext>();

Expand Down
5 changes: 3 additions & 2 deletions src/mdsnippets.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
{
"$schema": "https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/master/schema.json",
"TocExcludes": [ "NuGet package", "Release Notes", "Icon" ],
"MaxWidth": 90,
"ValidateContent": true,
"Convention": "InPlaceOverwrite"
"Convention": "InPlaceOverwrite",
"ExcludeMarkdownFiles": [ "*.verified.md", "*.received.md" ]
}
Loading