├── .gitignore ├── EntityFrameworkCore.Diagrams ├── .gitattributes ├── .gitignore ├── EntityFrameworkCore.Diagrams.Demo │ ├── .bowerrc │ ├── Controllers │ │ └── HomeController.cs │ ├── EntityFrameworkCore.Diagrams.Demo.csproj │ ├── Migrations │ │ ├── 20170713212439_Initial.Designer.cs │ │ ├── 20170713212439_Initial.cs │ │ └── ApplicationDbContextModelSnapshot.cs │ ├── Models │ │ ├── AlbumContent.cs │ │ ├── AlbumContentComment.cs │ │ ├── ApplicationDbContext.cs │ │ ├── Content.cs │ │ ├── Friendship.cs │ │ ├── PhotoAlbum.cs │ │ ├── Post.cs │ │ ├── PrivateMessage.cs │ │ └── Profile.cs │ ├── Program.cs │ ├── Startup.cs │ ├── Views │ │ ├── Home │ │ │ ├── About.cshtml │ │ │ ├── Contact.cshtml │ │ │ └── Index.cshtml │ │ ├── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _Layout.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ │ ├── _ViewImports.cshtml │ │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bower.json │ ├── bundleconfig.json │ └── wwwroot │ │ ├── css │ │ ├── site.css │ │ └── site.min.css │ │ ├── favicon.ico │ │ ├── images │ │ ├── banner1.svg │ │ ├── banner2.svg │ │ ├── banner3.svg │ │ └── banner4.svg │ │ └── js │ │ ├── site.js │ │ └── site.min.js ├── EntityFrameworkCore.Diagrams.Frontend │ ├── .angular-cli.json │ ├── .editorconfig │ ├── .firebaserc │ ├── .htmlhintrc │ ├── .vscode │ │ └── launch.json │ ├── README.md │ ├── database.rules.json │ ├── e2e │ │ ├── app.e2e-spec.ts │ │ ├── app.po.ts │ │ └── tsconfig.e2e.json │ ├── firebase.json │ ├── git-version.ts │ ├── karma.conf.js │ ├── package.json │ ├── protractor.conf.js │ ├── src-website │ │ ├── _syntax-highlight.scss │ │ ├── _theme.scss │ │ ├── app │ │ │ ├── app-routing.module.ts │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── components │ │ │ │ ├── demo │ │ │ │ │ ├── demo.component.html │ │ │ │ │ ├── demo.component.scss │ │ │ │ │ ├── demo.component.spec.ts │ │ │ │ │ └── demo.component.ts │ │ │ │ ├── footer │ │ │ │ │ ├── footer.component.html │ │ │ │ │ ├── footer.component.scss │ │ │ │ │ ├── footer.component.spec.ts │ │ │ │ │ └── footer.component.ts │ │ │ │ ├── getting-started │ │ │ │ │ ├── getting-started.component.html │ │ │ │ │ ├── getting-started.component.scss │ │ │ │ │ ├── getting-started.component.spec.ts │ │ │ │ │ └── getting-started.component.ts │ │ │ │ ├── guides │ │ │ │ │ ├── guides.component.html │ │ │ │ │ ├── guides.component.scss │ │ │ │ │ ├── guides.component.spec.ts │ │ │ │ │ └── guides.component.ts │ │ │ │ ├── header │ │ │ │ │ ├── header.component.html │ │ │ │ │ ├── header.component.scss │ │ │ │ │ ├── header.component.spec.ts │ │ │ │ │ └── header.component.ts │ │ │ │ ├── homepage │ │ │ │ │ ├── homepage.component.html │ │ │ │ │ ├── homepage.component.scss │ │ │ │ │ ├── homepage.component.spec.ts │ │ │ │ │ └── homepage.component.ts │ │ │ │ └── navbar │ │ │ │ │ ├── navbar.component.html │ │ │ │ │ ├── navbar.component.scss │ │ │ │ │ ├── navbar.component.spec.ts │ │ │ │ │ └── navbar.component.ts │ │ │ ├── directives │ │ │ │ ├── markdown.directive.spec.ts │ │ │ │ ├── markdown.directive.ts │ │ │ │ ├── syntax-highlight.directive.spec.ts │ │ │ │ └── syntax-highlight.directive.ts │ │ │ └── services │ │ │ │ ├── firebase-backend.service.spec.ts │ │ │ │ └── firebase-backend.service.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── github-circle-white-transparent.svg │ │ │ └── icon.png │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ ├── environment.ts │ │ │ └── firebase-config.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.scss │ │ ├── test.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── typings.d.ts │ ├── src │ │ ├── _mixins.scss │ │ ├── _theme.scss │ │ ├── app │ │ │ ├── app.module.ts │ │ │ ├── components │ │ │ │ ├── app │ │ │ │ │ ├── app.component.html │ │ │ │ │ ├── app.component.scss │ │ │ │ │ ├── app.component.spec.ts │ │ │ │ │ └── app.component.ts │ │ │ │ ├── clr-type │ │ │ │ │ ├── clr-type.component.html │ │ │ │ │ ├── clr-type.component.scss │ │ │ │ │ ├── clr-type.component.spec.ts │ │ │ │ │ └── clr-type.component.ts │ │ │ │ ├── db-diagram │ │ │ │ │ ├── db-diagram.component.html │ │ │ │ │ ├── db-diagram.component.scss │ │ │ │ │ ├── db-diagram.component.spec.ts │ │ │ │ │ └── db-diagram.component.ts │ │ │ │ ├── db-entity-diagram-figure │ │ │ │ │ ├── db-entity-diagram-figure.component.html │ │ │ │ │ ├── db-entity-diagram-figure.component.scss │ │ │ │ │ ├── db-entity-diagram-figure.component.spec.ts │ │ │ │ │ └── db-entity-diagram-figure.component.ts │ │ │ │ ├── db-relation-connector │ │ │ │ │ ├── db-relation-connector.component.html │ │ │ │ │ ├── db-relation-connector.component.scss │ │ │ │ │ ├── db-relation-connector.component.spec.ts │ │ │ │ │ └── db-relation-connector.component.ts │ │ │ │ ├── export-dialog │ │ │ │ │ ├── export-dialog.component.html │ │ │ │ │ ├── export-dialog.component.scss │ │ │ │ │ ├── export-dialog.component.spec.ts │ │ │ │ │ └── export-dialog.component.ts │ │ │ │ ├── minimap │ │ │ │ │ ├── minimap.component.html │ │ │ │ │ ├── minimap.component.scss │ │ │ │ │ ├── minimap.component.spec.ts │ │ │ │ │ └── minimap.component.ts │ │ │ │ └── scaling-toolbar │ │ │ │ │ ├── scaling-toolbar.component.html │ │ │ │ │ ├── scaling-toolbar.component.scss │ │ │ │ │ ├── scaling-toolbar.component.spec.ts │ │ │ │ │ └── scaling-toolbar.component.ts │ │ │ ├── core │ │ │ │ └── event-debouncer.ts │ │ │ ├── directives │ │ │ │ ├── devicemotion-scroll.directive.spec.ts │ │ │ │ ├── devicemotion-scroll.directive.ts │ │ │ │ ├── download-data.directive.spec.ts │ │ │ │ ├── download-data.directive.ts │ │ │ │ ├── draggable.directive.spec.ts │ │ │ │ ├── draggable.directive.ts │ │ │ │ ├── mouse-edge-pan.directive.spec.ts │ │ │ │ ├── mouse-edge-pan.directive.ts │ │ │ │ ├── scalable.directive.spec.ts │ │ │ │ ├── scalable.directive.ts │ │ │ │ ├── scrollbar-width.directive.spec.ts │ │ │ │ └── scrollbar-width.directive.ts │ │ │ ├── models │ │ │ │ ├── clr-type.ts │ │ │ │ ├── column-settings.ts │ │ │ │ ├── db-entity-foreign-key.ts │ │ │ │ ├── db-entity-index.ts │ │ │ │ ├── db-entity-key.ts │ │ │ │ ├── db-entity-layout.ts │ │ │ │ ├── db-entity-property-layout.ts │ │ │ │ ├── db-entity-property.ts │ │ │ │ ├── db-entity-relation-connector.ts │ │ │ │ ├── db-entity-relation-layout.ts │ │ │ │ ├── db-entity.ts │ │ │ │ ├── db-model-layout.ts │ │ │ │ ├── db-model.ts │ │ │ │ ├── dto │ │ │ │ │ ├── db-diagram-dto.ts │ │ │ │ │ ├── db-entity-layout-dto.ts │ │ │ │ │ ├── db-entity-relation-layout-dto.ts │ │ │ │ │ └── db-model-layout-dto.ts │ │ │ │ ├── line.ts │ │ │ │ ├── point.ts │ │ │ │ ├── table-settings.ts │ │ │ │ └── value-generated.ts │ │ │ └── services │ │ │ │ ├── api.service.spec.ts │ │ │ │ ├── api.service.ts │ │ │ │ ├── diagram-layout.service.spec.ts │ │ │ │ └── diagram-layout.service.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ ├── environment.ts │ │ │ ├── test-data.ts │ │ │ └── versions.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.scss │ │ ├── test.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── typings.d.ts │ ├── tsconfig.json │ └── tslint.json ├── EntityFrameworkCore.Diagrams.sln └── EntityFrameworkCore.Diagrams │ ├── Dto │ ├── ClrType.cs │ ├── DbEntity.cs │ ├── DbEntityBase.cs │ ├── DbEntityForeignKey.cs │ ├── DbEntityForeignKeyBase.cs │ ├── DbEntityIndex.cs │ ├── DbEntityIndexBase.cs │ ├── DbEntityKey.cs │ ├── DbEntityProperty.cs │ ├── DbEntityPropertyBase.cs │ ├── DbModel.cs │ ├── DistinctClrTypesComparer.cs │ ├── DistinctEntitiesComparer.cs │ ├── DtoConverter.cs │ ├── NormalizedClrType.cs │ ├── NormalizedDbEntity.cs │ ├── NormalizedDbEntityForeignKey.cs │ ├── NormalizedDbEntityIndex.cs │ ├── NormalizedDbEntityKey.cs │ ├── NormalizedDbEntityProperty.cs │ ├── NormalizedDbModel.cs │ └── NormalizedDtoConverter.cs │ ├── EfDiagramsMiddleware.cs │ ├── EfDiagramsOptions.cs │ ├── EfDiagramsServiceCollectionExtensions.cs │ └── EntityFrameworkCore.Diagrams.csproj ├── LICENSE.TXT ├── README.md ├── icon.pdn └── icon.png /.gitignore: -------------------------------------------------------------------------------- 1 | EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/wwwroot/ 2 | EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/dist -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "wwwroot/lib" 3 | } 4 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace EntityFrameworkCore.Diagrams.Demo.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | public IActionResult Index() 12 | { 13 | return View(); 14 | } 15 | 16 | public IActionResult About() 17 | { 18 | ViewData["Message"] = "Your application description page."; 19 | 20 | return View(); 21 | } 22 | 23 | public IActionResult Contact() 24 | { 25 | ViewData["Message"] = "Your contact page."; 26 | 27 | return View(); 28 | } 29 | 30 | public IActionResult Error() 31 | { 32 | return View(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/EntityFrameworkCore.Diagrams.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp1.1 5 | 6 | 7 | 8 | $(PackageTargetFallback);portable-net45+win8+wp8+wpa81; 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/AlbumContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class AlbumContent 9 | { 10 | public int AlbumId { get; set; } 11 | 12 | public PhotoAlbum Album { get; set; } 13 | 14 | public int ContentId { get; set; } 15 | 16 | public Content Content { get; set; } 17 | 18 | public List Comments { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/AlbumContentComment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class AlbumContentComment 9 | { 10 | public int AlbumId { get; set; } 11 | 12 | public int ContentId { get; set; } 13 | 14 | public AlbumContent AlbumContent { get; set; } 15 | 16 | public int PostId { get; set; } 17 | 18 | public Post Post { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/ApplicationDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace EntityFrameworkCore.Diagrams.Demo.Models 8 | { 9 | public class ApplicationDbContext : DbContext 10 | { 11 | public ApplicationDbContext(DbContextOptions options) 12 | : base(options) 13 | { 14 | } 15 | 16 | public DbSet AlbumContents { get; set; } 17 | 18 | public DbSet AlbumContentComments { get; set; } 19 | 20 | public DbSet Contents { get; set; } 21 | 22 | public DbSet Friendships { get; set; } 23 | 24 | public DbSet PhotoAlbums { get; set; } 25 | 26 | public DbSet Posts { get; set; } 27 | 28 | public DbSet PrivateMessages { get; set; } 29 | 30 | public DbSet Profiles { get; set; } 31 | 32 | protected override void OnModelCreating(ModelBuilder modelBuilder) 33 | { 34 | base.OnModelCreating(modelBuilder); 35 | 36 | modelBuilder.Entity() 37 | .HasKey(e => new { e.AlbumId, e.ContentId }); 38 | modelBuilder.Entity() 39 | .HasOne(e => e.Album) 40 | .WithMany(e => e.Contents) 41 | .HasForeignKey(e => e.AlbumId); 42 | modelBuilder.Entity() 43 | .HasOne(e => e.Content) 44 | .WithMany(e => e.AlbumContents) 45 | .HasForeignKey(e => e.ContentId); 46 | 47 | modelBuilder.Entity() 48 | .HasKey(e => new { e.AlbumId, e.ContentId, e.PostId }); 49 | modelBuilder.Entity() 50 | .HasOne(e => e.AlbumContent) 51 | .WithMany(e => e.Comments) 52 | .HasForeignKey(e => new { e.AlbumId, e.ContentId }); 53 | modelBuilder.Entity() 54 | .HasOne(e => e.Post) 55 | .WithMany(e => e.AlbumContentComments) 56 | .HasForeignKey(e => e.PostId); 57 | 58 | modelBuilder.Entity() 59 | .HasKey(e => new { e.SourceProfileId, e.TargetProfileId }); 60 | modelBuilder.Entity() 61 | .HasOne(e => e.SourceProfile) 62 | .WithMany(e => e.OutgoingFriendRequests) 63 | .HasForeignKey(e => e.SourceProfileId); 64 | modelBuilder.Entity() 65 | .HasOne(e => e.TargetProfile) 66 | .WithMany(e => e.IncomingFriendRequests) 67 | .HasForeignKey(e => e.TargetProfileId); 68 | 69 | modelBuilder.Entity() 70 | .HasOne(e => e.Profile) 71 | .WithMany(e => e.PhotoAlbums) 72 | .HasForeignKey(e => e.ProfileId); 73 | 74 | modelBuilder.Entity() 75 | .HasOne(e => e.Author) 76 | .WithMany(e => e.Posts) 77 | .HasForeignKey(e => e.AuthorId); 78 | 79 | modelBuilder.Entity() 80 | .HasKey(e => new { e.PostId, e.TargetProfileId }); 81 | modelBuilder.Entity() 82 | .HasOne(e => e.Post) 83 | .WithMany(e => e.PrivateMessages) 84 | .HasForeignKey(e => e.PostId); 85 | modelBuilder.Entity() 86 | .HasOne(e => e.TargetProfile) 87 | .WithMany(e => e.PrivateMessages) 88 | .HasForeignKey(e => e.TargetProfileId); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/Content.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class Content 9 | { 10 | public int Id { get; set; } 11 | 12 | public string MimeType { get; set; } 13 | 14 | public string Uri { get; set; } 15 | 16 | public DateTime CreatedAt { get; set; } 17 | 18 | public List AlbumContents { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/Friendship.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class Friendship 9 | { 10 | public int SourceProfileId { get; set; } 11 | 12 | public Profile SourceProfile { get; set; } 13 | 14 | public int TargetProfileId { get; set; } 15 | 16 | public Profile TargetProfile { get; set; } 17 | 18 | public DateTime CreatedAt { get; set; } 19 | 20 | public DateTime? ConfirmedAt { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/PhotoAlbum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class PhotoAlbum 9 | { 10 | public int Id { get; set; } 11 | 12 | public string Name { get; set; } 13 | 14 | public string Description { get; set; } 15 | 16 | public int ProfileId { get; set; } 17 | 18 | public Profile Profile { get; set; } 19 | 20 | public DateTime CreatedAt { get; set; } 21 | 22 | public List Contents { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/Post.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class Post 9 | { 10 | public int Id { get; set; } 11 | 12 | public string Message { get; set; } 13 | 14 | public int AuthorId { get; set; } 15 | 16 | public Profile Author { get; set; } 17 | 18 | public DateTime CreatedAt { get; set; } 19 | 20 | public int LikesCount { get; set; } 21 | 22 | public List AlbumContentComments { get; set; } 23 | 24 | public List PrivateMessages { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/PrivateMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class PrivateMessage 9 | { 10 | public int TargetProfileId { get; set; } 11 | 12 | public Profile TargetProfile { get; set; } 13 | 14 | public int PostId { get; set; } 15 | 16 | public Post Post { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Models/Profile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Demo.Models 7 | { 8 | public class Profile 9 | { 10 | public int Id { get; set; } 11 | 12 | public string FirstName { get; set; } 13 | 14 | public string MiddleName { get; set; } 15 | 16 | public string LastName { get; set; } 17 | 18 | public DateTime BirthDate { get; set; } 19 | 20 | public List OutgoingFriendRequests { get; set; } 21 | 22 | public List IncomingFriendRequests { get; set; } 23 | 24 | public List PhotoAlbums { get; set; } 25 | 26 | public List Posts { get; set; } 27 | 28 | public List PrivateMessages { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | 8 | namespace EntityFrameworkCore.Diagrams.Demo 9 | { 10 | public class Program 11 | { 12 | public static void Main(string[] args) 13 | { 14 | var host = new WebHostBuilder() 15 | .UseKestrel() 16 | .UseContentRoot(Directory.GetCurrentDirectory()) 17 | .UseIISIntegration() 18 | .UseStartup() 19 | .UseApplicationInsights() 20 | .Build(); 21 | 22 | host.Run(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using EntityFrameworkCore.Diagrams; 11 | using EntityFrameworkCore.Diagrams.Demo.Models; 12 | using Npgsql.EntityFrameworkCore.PostgreSQL.Storage; 13 | using Npgsql.EntityFrameworkCore.PostgreSQL; 14 | using Npgsql.EntityFrameworkCore; 15 | using Npgsql; 16 | using Microsoft.EntityFrameworkCore; 17 | 18 | namespace EntityFrameworkCore.Diagrams.Demo 19 | { 20 | public class Startup 21 | { 22 | public Startup(IHostingEnvironment env) 23 | { 24 | var builder = new ConfigurationBuilder() 25 | .SetBasePath(env.ContentRootPath) 26 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 27 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 28 | .AddEnvironmentVariables(); 29 | Configuration = builder.Build(); 30 | } 31 | 32 | public IConfigurationRoot Configuration { get; } 33 | 34 | // This method gets called by the runtime. Use this method to add services to the container. 35 | public void ConfigureServices(IServiceCollection services) 36 | { 37 | services.AddDbContext(options => 38 | options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"))); 39 | 40 | // Add framework services. 41 | services.AddMvc(); 42 | } 43 | 44 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 45 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 46 | { 47 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 48 | loggerFactory.AddDebug(); 49 | 50 | if (env.IsDevelopment()) 51 | { 52 | app.UseDeveloperExceptionPage(); 53 | app.UseBrowserLink(); 54 | app.AddEfDiagrams(); 55 | } 56 | else 57 | { 58 | app.UseExceptionHandler("/Home/Error"); 59 | } 60 | 61 | app.UseStaticFiles(); 62 | 63 | app.UseMvc(routes => 64 | { 65 | routes.MapRoute( 66 | name: "default", 67 | template: "{controller=Home}/{action=Index}/{id?}"); 68 | }); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/Home/About.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "About"; 3 | } 4 |

@ViewData["Title"].

5 |

@ViewData["Message"]

6 | 7 |

Use this area to provide additional information.

8 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/Home/Contact.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Contact"; 3 | } 4 |

@ViewData["Title"].

5 |

@ViewData["Message"]

6 | 7 |
8 | One Microsoft Way
9 | Redmond, WA 98052-6399
10 | P: 11 | 425.555.0100 12 |
13 | 14 |
15 | Support: Support@example.com
16 | Marketing: Marketing@example.com 17 |
18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Error"; 3 | } 4 | 5 |

Error.

6 |

An error occurred while processing your request.

7 | 8 |

Development Mode

9 |

10 | Swapping to Development environment will display more detailed information about the error that occurred. 11 |

12 |

13 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. 14 |

15 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | @inject Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet JavaScriptSnippet 2 | 3 | 4 | 5 | 6 | 7 | @ViewData["Title"] - EntityFrameworkCore.Diagrams.Demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | @Html.Raw(JavaScriptSnippet.FullScript) 20 | 21 | 22 | 42 |
43 | @RenderBody() 44 |
45 |
46 |

© 2017 - EntityFrameworkCore.Diagrams.Demo

47 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 62 | 68 | 69 | 70 | 71 | @RenderSection("Scripts", required: false) 72 | 73 | 74 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using EntityFrameworkCore.Diagrams.Demo 2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 3 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "User ID = postgres; Password = postgres; Host = 127.0.0.1; Port = 5432; Database = EfDiagramsDemo; Pooling = true; MinPoolSize = 20;" 4 | }, 5 | "Logging": { 6 | "IncludeScopes": false, 7 | "LogLevel": { 8 | "Default": "Warning" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asp.net", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "3.3.7", 6 | "jquery": "2.2.0", 7 | "jquery-validation": "1.14.0", 8 | "jquery-validation-unobtrusive": "3.2.6" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/bundleconfig.json: -------------------------------------------------------------------------------- 1 | // Configure bundling and minification for the project. 2 | // More info at https://go.microsoft.com/fwlink/?LinkId=808241 3 | [ 4 | { 5 | "outputFileName": "wwwroot/css/site.min.css", 6 | // An array of relative input file paths. Globbing patterns supported 7 | "inputFiles": [ 8 | "wwwroot/css/site.css" 9 | ] 10 | }, 11 | { 12 | "outputFileName": "wwwroot/js/site.min.js", 13 | "inputFiles": [ 14 | "wwwroot/js/site.js" 15 | ], 16 | // Optionally specify minification options 17 | "minify": { 18 | "enabled": true, 19 | "renameLocals": true 20 | }, 21 | // Optionally generate .map file 22 | "sourceMap": false 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Wrapping element */ 7 | /* Set some basic padding to keep content from hitting the edges */ 8 | .body-content { 9 | padding-left: 15px; 10 | padding-right: 15px; 11 | } 12 | 13 | /* Set widths on the form inputs since otherwise they're 100% wide */ 14 | input, 15 | select, 16 | textarea { 17 | max-width: 280px; 18 | } 19 | 20 | /* Carousel */ 21 | .carousel-caption p { 22 | font-size: 20px; 23 | line-height: 1.4; 24 | } 25 | 26 | /* Make .svg files in the carousel display properly in older browsers */ 27 | .carousel-inner .item img[src$=".svg"] { 28 | width: 100%; 29 | } 30 | 31 | /* Hide/rearrange for smaller screens */ 32 | @media screen and (max-width: 767px) { 33 | /* Hide captions */ 34 | .carousel-caption { 35 | display: none; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/css/site.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:50px;padding-bottom:20px}.body-content{padding-left:15px;padding-right:15px}input,select,textarea{max-width:280px}.carousel-caption p{font-size:20px;line-height:1.4}.carousel-inner .item img[src$=".svg"]{width:100%}@media screen and (max-width:767px){.carousel-caption{display:none}} -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/favicon.ico -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Write your Javascript code. 2 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/js/site.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Demo/wwwroot/js/site.min.js -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "db-diagrams" 5 | }, 6 | "apps": [ 7 | { 8 | "name": "efd", 9 | "root": "src", 10 | "outDir": "../EntityFrameworkCore.Diagrams/wwwroot/db-diagrams", 11 | "assets": [ 12 | "assets", 13 | "favicon.ico" 14 | ], 15 | "index": "index.html", 16 | "main": "main.ts", 17 | "polyfills": "polyfills.ts", 18 | "test": "test.ts", 19 | "tsconfig": "tsconfig.app.json", 20 | "testTsconfig": "tsconfig.spec.json", 21 | "prefix": "efd", 22 | "styles": [ 23 | "../node_modules/material-design-icons/iconfont/material-icons.css", 24 | "styles.scss" 25 | ], 26 | "scripts": [], 27 | "environmentSource": "environments/environment.ts", 28 | "environments": { 29 | "dev": "environments/environment.ts", 30 | "prod": "environments/environment.prod.ts" 31 | } 32 | }, 33 | { 34 | "name": "efd-website", 35 | "root": "src-website", 36 | "outDir": "dist", 37 | "assets": [ 38 | "assets", 39 | "favicon.ico" 40 | ], 41 | "index": "index.html", 42 | "main": "main.ts", 43 | "polyfills": "polyfills.ts", 44 | "test": "test.ts", 45 | "tsconfig": "tsconfig.app.json", 46 | "testTsconfig": "tsconfig.spec.json", 47 | "prefix": "efd", 48 | "styles": [ 49 | "styles.scss" 50 | ], 51 | "scripts": [], 52 | "environmentSource": "environments/environment.ts", 53 | "environments": { 54 | "dev": "environments/environment.ts", 55 | "prod": "environments/environment.prod.ts" 56 | } 57 | } 58 | ], 59 | "e2e": { 60 | "protractor": { 61 | "config": "./protractor.conf.js" 62 | } 63 | }, 64 | "lint": [{ 65 | "project": "src/tsconfig.app.json" 66 | }, 67 | { 68 | "project": "src/tsconfig.spec.json" 69 | }, 70 | { 71 | "project": "e2e/tsconfig.e2e.json" 72 | } 73 | ], 74 | "test": { 75 | "karma": { 76 | "config": "./karma.conf.js" 77 | } 78 | }, 79 | "defaults": { 80 | "styleExt": "scss", 81 | "component": {} 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = 0 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "db-diagrams" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/.htmlhintrc: -------------------------------------------------------------------------------- 1 | { 2 | "tagname-lowercase": true, 3 | "attr-lowercase": false, 4 | "attr-value-double-quotes": true, 5 | "doctype-first": false, 6 | "tag-pair": true, 7 | "spec-char-escape": true, 8 | "id-unique": true, 9 | "src-not-empty": true, 10 | "attr-no-duplication": true, 11 | "title-require": true 12 | } 13 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "chrome", 6 | "request": "launch", 7 | "name": "Launch Chrome with ng serve", 8 | "url": "http://localhost:4200/#", 9 | "webRoot": "${workspaceRoot}", 10 | "userDataDir": "C:\\Users\\Alexander\\temp" 11 | }, 12 | { 13 | "type": "chrome", 14 | "request": "launch", 15 | "name": "Launch Chrome with ng test", 16 | "url": "http://localhost:9876/debug.html", 17 | "webRoot": "${workspaceRoot}" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/README.md: -------------------------------------------------------------------------------- 1 | # DbDiagrams 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.2.1. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | Before running the tests make sure you are serving the app via `ng serve`. 25 | 26 | ## Further help 27 | 28 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 29 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": "auth != null", 4 | ".write": "auth != null", 5 | "diagrams": { 6 | ".read": true, 7 | ".write": "auth != null", 8 | "$diagram_id": { 9 | ".validate": "newData.hasChildren(['diagram', 'authorUid', 'timestamp']) && newData.child('authorUid').isString() && newData.child('timestamp').isNumber()", 10 | 11 | "diagram": { ".validate": "newData.exists()" }, 12 | "authorUid": { ".validate": "newData.val() === auth.uid" }, 13 | "timestamp": { ".validate": "newData.val() <= now" } 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { DbDiagramsPage } from './app.po'; 2 | 3 | describe('db-diagrams App', () => { 4 | let page: DbDiagramsPage; 5 | 6 | beforeEach(() => { 7 | page = new DbDiagramsPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to efd!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class DbDiagramsPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('efd-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "database": { 3 | "rules": "database.rules.json" 4 | }, 5 | "hosting": { 6 | "public": "dist", 7 | "rewrites": [ 8 | { 9 | "source": "**", 10 | "destination": "/index.html" 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/git-version.ts: -------------------------------------------------------------------------------- 1 | import fs = require('fs'); 2 | import { Observable } from 'rxjs/Observable'; 3 | import 'rxjs/add/observable/combineLatest'; 4 | 5 | const exec = require('child_process').exec; 6 | 7 | const revision = new Observable(s => { 8 | exec('git rev-parse --short HEAD', 9 | function (error: Error, stdout: Buffer, stderr: Buffer) { 10 | if (error !== null) { 11 | console.log('git error: ' + error + stderr); 12 | } 13 | s.next(stdout.toString().trim()); 14 | s.complete(); 15 | }); 16 | }); 17 | 18 | const branch = new Observable(s => { 19 | exec('git rev-parse --abbrev-ref HEAD', 20 | function (error: Error, stdout: Buffer, stderr: Buffer) { 21 | if (error !== null) { 22 | console.log('git error: ' + error + stderr); 23 | } 24 | s.next(stdout.toString().trim()); 25 | s.complete(); 26 | }); 27 | }); 28 | 29 | Observable 30 | .combineLatest(revision, branch) 31 | .subscribe(([revision, branch]) => { 32 | const fields = [ 33 | `version: '${process.env.npm_package_version}'`, 34 | `revision: '${revision}'`, 35 | `branch: '${branch}'`, 36 | `repository: '${process.env.npm_package_repository_url}'` 37 | ]; 38 | const indent = ' '; 39 | const versionsObj = '{\n' + indent + fields.join(',\n' + indent) + '\n}'; 40 | console.log(versionsObj); 41 | 42 | const content = 43 | `// this file is automatically generated by git-version.ts script 44 | export const versions = ${versionsObj}; 45 | `; 46 | 47 | fs.writeFileSync( 48 | 'src/environments/versions.ts', 49 | content, 50 | { encoding: 'utf8' } 51 | ); 52 | }); 53 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "db-diagrams", 3 | "version": "0.0.0", 4 | "repository": { 5 | "url": "https://github.com/EvAlex/ef-db-diagrams/", 6 | "type": "git" 7 | }, 8 | "license": "MIT", 9 | "scripts": { 10 | "ng": "ng", 11 | "start": "ng serve --app efd --port 4200 --host 0.0.0.0", 12 | "start-website": "ng serve --app efd-website --port 4201", 13 | "build": "ng build", 14 | "test": "ng test", 15 | "lint": "ng lint", 16 | "e2e": "ng e2e", 17 | "deploy-website": "ts-node git-version.ts && ng build --app efd-website --sourcemaps false && firebase deploy" 18 | }, 19 | "private": true, 20 | "dependencies": { 21 | "@angular/animations": "^4.2.6", 22 | "@angular/cdk": "^2.0.0-beta.8", 23 | "@angular/common": "^4.0.0", 24 | "@angular/compiler": "^4.0.0", 25 | "@angular/core": "^4.0.0", 26 | "@angular/forms": "^4.0.0", 27 | "@angular/http": "^4.0.0", 28 | "@angular/material": "^2.0.0-beta.8", 29 | "@angular/platform-browser": "^4.0.0", 30 | "@angular/platform-browser-dynamic": "^4.0.0", 31 | "@angular/router": "^4.0.0", 32 | "angularfire2": "^4.0.0-rc.1", 33 | "core-js": "^2.4.1", 34 | "firebase": "^4.2.0", 35 | "hammerjs": "^2.0.8", 36 | "material-design-icons": "^3.0.1", 37 | "rxjs": "^5.1.0", 38 | "showdown": "^1.7.1", 39 | "zone.js": "^0.8.4" 40 | }, 41 | "devDependencies": { 42 | "@angular/cli": "1.3.0-rc.5", 43 | "@angular/compiler-cli": "^4.0.0", 44 | "@angular/language-service": "^4.0.0", 45 | "@types/jasmine": "~2.5.53", 46 | "@types/jasminewd2": "~2.0.2", 47 | "@types/node": "~6.0.60", 48 | "codelyzer": "~3.0.1", 49 | "jasmine-core": "~2.6.2", 50 | "jasmine-spec-reporter": "~4.1.0", 51 | "karma": "~1.7.0", 52 | "karma-chrome-launcher": "~2.1.1", 53 | "karma-cli": "~1.0.1", 54 | "karma-coverage-istanbul-reporter": "^1.2.1", 55 | "karma-jasmine": "~1.1.0", 56 | "karma-jasmine-html-reporter": "^0.2.2", 57 | "protractor": "~5.1.2", 58 | "ts-node": "~3.0.4", 59 | "tslint": "~5.3.2", 60 | "typescript": "~2.3.3" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/_syntax-highlight.scss: -------------------------------------------------------------------------------- 1 | 2 | .hljs { 3 | background: none; 4 | 5 | &.cs { 6 | .hljs-keyword { 7 | font-weight: normal; 8 | color: #0000FF; 9 | } 10 | 11 | .hljs-number { 12 | color: #000000; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/_theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the common styles for Angular Material. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | // Be sure that you only ever include this mixin once! 7 | @include mat-core(); 8 | 9 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 10 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 11 | // hue. 12 | $efd-app-primary: mat-palette($mat-indigo); 13 | $efd-app-accent: mat-palette($mat-pink, A200, A100, A400); 14 | 15 | // The warn palette is optional (defaults to red). 16 | $efd-app-warn: mat-palette($mat-red); 17 | 18 | // Create the theme object (a Sass map containing all of the palettes). 19 | $efd-app-theme: mat-light-theme($efd-app-primary, $efd-app-accent, $efd-app-warn); 20 | 21 | // Include theme styles for core and each component used in your app. 22 | // Alternatively, you can import and @include the theme mixins for each component 23 | // that you are using. 24 | @include angular-material-theme($efd-app-theme); 25 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { HomepageComponent } from './components/homepage/homepage.component'; 4 | import { GuidesComponent } from './components/guides/guides.component'; 5 | import { GettingStartedComponent } from './components/getting-started/getting-started.component'; 6 | import { DemoComponent } from './components/demo/demo.component'; 7 | 8 | const routes: Routes = [ 9 | { 10 | path: '', 11 | component: HomepageComponent, 12 | children: [] 13 | }, 14 | { 15 | path: 'demo', 16 | component: DemoComponent, 17 | children: [] 18 | }, 19 | { 20 | path: 'guides', 21 | component: GuidesComponent, 22 | children: [] 23 | }, 24 | { 25 | path: 'guides/getting-started', 26 | component: GettingStartedComponent 27 | } 28 | ]; 29 | 30 | @NgModule({ 31 | imports: [RouterModule.forRoot(routes)], 32 | exports: [RouterModule] 33 | }) 34 | export class AppRoutingModule { } 35 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.component.scss -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | 4 | import { AppComponent } from './app.component'; 5 | 6 | describe('AppComponent', () => { 7 | beforeEach(async(() => { 8 | TestBed.configureTestingModule({ 9 | imports: [ 10 | RouterTestingModule 11 | ], 12 | declarations: [ 13 | AppComponent 14 | ], 15 | }).compileComponents(); 16 | })); 17 | 18 | it('should create the app', async(() => { 19 | const fixture = TestBed.createComponent(AppComponent); 20 | const app = fixture.debugElement.componentInstance; 21 | expect(app).toBeTruthy(); 22 | })); 23 | 24 | it(`should have as title 'efd'`, async(() => { 25 | const fixture = TestBed.createComponent(AppComponent); 26 | const app = fixture.debugElement.componentInstance; 27 | expect(app.title).toEqual('efd'); 28 | })); 29 | 30 | it('should render title in a h1 tag', async(() => { 31 | const fixture = TestBed.createComponent(AppComponent); 32 | fixture.detectChanges(); 33 | const compiled = fixture.debugElement.nativeElement; 34 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to efd!'); 35 | })); 36 | }); 37 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-website-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | } 10 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { 5 | MdButtonModule, 6 | MdToolbarModule, 7 | MdListModule, 8 | MdIconModule, 9 | MdTooltipModule 10 | } from '@angular/material'; 11 | import { AngularFireModule } from 'angularfire2'; 12 | import { AngularFireDatabaseModule } from 'angularfire2/database'; 13 | import { environment } from '../environments/environment'; 14 | 15 | import 'rxjs/add/operator/map'; 16 | 17 | import { AppRoutingModule } from './app-routing.module'; 18 | import { AppComponent } from './app.component'; 19 | import { NavbarComponent } from './components/navbar/navbar.component'; 20 | import { HomepageComponent } from './components/homepage/homepage.component'; 21 | import { GettingStartedComponent } from './components/getting-started/getting-started.component'; 22 | import { GuidesComponent } from './components/guides/guides.component'; 23 | import { MarkdownDirective } from './directives/markdown.directive'; 24 | import { SyntaxHighlightDirective } from './directives/syntax-highlight.directive'; 25 | import { FooterComponent } from './components/footer/footer.component'; 26 | import { DemoComponent } from './components/demo/demo.component'; 27 | import { HeaderComponent } from './components/header/header.component'; 28 | import { FirebaseBackendService } from './services/firebase-backend.service'; 29 | import { AngularFireAuthModule } from 'angularfire2/auth'; 30 | 31 | import { DbDiagramsAppModule } from '../../src/app/app.module'; 32 | import { ApiService } from '../../src/app/services/api.service'; 33 | 34 | @NgModule({ 35 | declarations: [ 36 | AppComponent, 37 | NavbarComponent, 38 | HomepageComponent, 39 | GettingStartedComponent, 40 | GuidesComponent, 41 | MarkdownDirective, 42 | SyntaxHighlightDirective, 43 | FooterComponent, 44 | DemoComponent, 45 | HeaderComponent 46 | ], 47 | imports: [ 48 | BrowserModule, 49 | BrowserAnimationsModule, 50 | AppRoutingModule, 51 | 52 | DbDiagramsAppModule, 53 | 54 | AngularFireModule.initializeApp(environment.firebase), 55 | AngularFireDatabaseModule, 56 | AngularFireAuthModule, 57 | 58 | MdButtonModule, 59 | MdToolbarModule, 60 | MdListModule, 61 | MdIconModule, 62 | MdTooltipModule 63 | ], 64 | providers: [ 65 | FirebaseBackendService, 66 | { 67 | provide: ApiService, 68 | useExisting: FirebaseBackendService 69 | } 70 | ], 71 | bootstrap: [AppComponent], 72 | schemas: [CUSTOM_ELEMENTS_SCHEMA] 73 | }) 74 | export class AppModule { } 75 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/demo/demo.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Live Demo

4 |
5 | 6 |
7 | 8 |
9 |

The actual app

10 |

11 | The live demo below is an actual app that you will see at /db-diagrams page 12 | after installing the library to your project. It will automatically display your DbContext's 13 | model. 14 |

15 | 16 |

View in fullscreen

17 |

18 | Fullscreen mode lets you see how actual app looks like. 19 | Some features are available only in fullscreen mode, e.g. minimap and toolbar expand-collapse. 20 |

21 | 29 |
30 | 31 | 32 | 33 | 43 | 44 |
45 |

Latest version here

46 |

47 | The latest version of the app always will be published here. Current version is 48 | {{commitSha}} 49 |

50 |

51 | Found a bug? Try exporting your diagram and importing it here to see whether the issue is 52 | already resolved in the latest version. If not - create an issue. 53 |

54 |

55 | Have a feature proposal? Try this version first to see, whether the feature is already 56 | implemented. If not - create an issue. 57 |

58 |
59 |
60 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/demo/demo.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | 3 | main { 4 | 5 | &.fullscreen { 6 | section { 7 | display: none; 8 | } 9 | efd-root { 10 | position: fixed; 11 | top: 0; 12 | right: 0; 13 | bottom: 0; 14 | left: 0; 15 | background: white; 16 | margin: 0; 17 | } 18 | 19 | .toggle-fullscreen-btn { 20 | position: fixed; 21 | bottom: 32%; 22 | right: 32px; 23 | 24 | @media (max-width: 570px) { 25 | left: calc(25% - 28px); 26 | bottom: 28px; 27 | } 28 | } 29 | } 30 | 31 | section { 32 | margin: 20px auto; 33 | max-width: 940px; 34 | 35 | @media (max-width: 972px) { 36 | margin: 16px; 37 | } 38 | 39 | h2 { 40 | margin-top: 40px; 41 | font-size: 24px; 42 | font-weight: 400; 43 | } 44 | 45 | p { 46 | font-size: 16px; 47 | line-height: 28px; 48 | } 49 | } 50 | 51 | efd-root { 52 | @include mat-elevation(4); 53 | margin: 32px 0; 54 | width: 100%; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/demo/demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DemoComponent } from './demo.component'; 4 | 5 | describe('DemoComponent', () => { 6 | let component: DemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/demo/demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ElementRef, ViewChild } from '@angular/core'; 2 | 3 | import { DiagramLayoutService } from '../../../../src/app/services/diagram-layout.service'; 4 | import { DbModelLayout } from '../../../../src/app/models/db-model-layout'; 5 | import { AppComponent } from '../../../../src/app/components/app/app.component'; 6 | import { environment } from '../../../../src/environments/environment'; 7 | 8 | @Component({ 9 | selector: 'efd-demo', 10 | templateUrl: './demo.component.html', 11 | styleUrls: ['./demo.component.scss'] 12 | }) 13 | export class DemoComponent implements OnInit { 14 | 15 | fullscreen = false; 16 | 17 | commitSha = environment.versions.revision; 18 | commitLink = `${environment.versions.repository}commit/${environment.versions.revision}`; 19 | 20 | @ViewChild('viewInFullscreen') 21 | viewInFullscreen: ElementRef; 22 | 23 | constructor(private readonly _diagramLayout: DiagramLayoutService) { 24 | } 25 | 26 | ngOnInit() { 27 | } 28 | 29 | toggleFullscreen() { 30 | this.fullscreen = !this.fullscreen; 31 | 32 | for (const modelLayout of this._diagramLayout.getModelLayouts()) { 33 | modelLayout.showMinimap = this.fullscreen; 34 | } 35 | 36 | if (!this.fullscreen) { 37 | setTimeout(() => this.scrollToViewInFullscreen(), 16); 38 | } 39 | } 40 | 41 | private scrollToViewInFullscreen() { 42 | const viewInFullscreen = this.viewInFullscreen.nativeElement as HTMLElement; 43 | window.document.body.scrollTop = viewInFullscreen.getBoundingClientRect().top - 8; 44 | } 45 | 46 | onDiagramLoaded(modelLayout: DbModelLayout) { 47 | modelLayout.showMinimap = this.fullscreen; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/footer/footer.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 18 |
19 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/footer/footer.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | 4 | footer { 5 | margin-top: 40px; 6 | padding: 12px; 7 | font-size: 12px; 8 | background: mat-color(map-get($efd-app-theme, primary)); 9 | color: rgba(255, 255, 255, 0.87); 10 | 11 | .footer-list { 12 | display: flex; 13 | padding: 8px; 14 | flex-flow: row wrap; 15 | align-items: center; 16 | 17 | @media (max-width: 488px) { 18 | padding: 0; 19 | } 20 | 21 | .footer-logo { 22 | border-radius: 50%; 23 | overflow: hidden; 24 | flex: 0 0 auto; 25 | 26 | img { 27 | height: 50px; 28 | } 29 | } 30 | 31 | .footer-links { 32 | min-width: 180px; 33 | 34 | @media (max-width: 488px) { 35 | // flex-basis: 80%; 36 | } 37 | 38 | ul { 39 | list-style: none; 40 | margin: 0 40px; 41 | padding: 0; 42 | 43 | @media (max-width: 488px) { 44 | margin: 0 0 0 16px; 45 | } 46 | 47 | a { 48 | font-size: 16px; 49 | padding: 0; 50 | text-decoration: none; 51 | color: rgba(255, 255, 255, 0.87); 52 | 53 | &:hover { 54 | text-decoration: underline; 55 | } 56 | } 57 | } 58 | } 59 | 60 | .footer-copyright { 61 | flex: 1; 62 | display: flex; 63 | justify-content: flex-end; 64 | min-width: 180px; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/footer/footer.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FooterComponent } from './footer.component'; 4 | 5 | describe('FooterComponent', () => { 6 | let component: FooterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FooterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FooterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/footer/footer.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-footer', 5 | templateUrl: './footer.component.html', 6 | styleUrls: ['./footer.component.scss'] 7 | }) 8 | export class FooterComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/getting-started/getting-started.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Getting Started

4 |
5 | 6 |
7 |
8 | 9 |

Step 1: Install EntityFrameworkCore.Diagrams

10 |
dotnet add package EntityFrameworkCore.Diagrams
11 | 12 |

Step 2: Add middleware

13 |

Use AddEfDiagrams() extension method in Configure() method of your Startup class to add middleware. Specify your DbContext type instead of ApplicationDbContext in the following example:

14 |
app.AddEfDiagrams<ApplicationDbContext>();
15 |

Here's how your Configure() method might look like after this step (notice highlighted line):

16 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
17 | {
18 |     loggerFactory.AddConsole(Configuration.GetSection("Logging"));
19 |     loggerFactory.AddDebug();
20 | 
21 |     if (env.IsDevelopment())
22 |     {
23 |         app.UseDeveloperExceptionPage();
24 |         app.UseBrowserLink();
25 |         app.AddEfDiagrams<ApplicationDbContext>();
26 |     }
27 |     else
28 |     {
29 |         app.UseExceptionHandler("/Home/Error");
30 |     }
31 | 
32 |     app.UseStaticFiles();
33 | 
34 |     app.UseMvc(routes =>
35 |     {
36 |         routes.MapRoute(
37 |             name: "default",
38 |             template: "{controller=Home}/{action=Index}/{id?}");
39 |     });
40 | }
41 |

Notice that the middleware is added only in Development mode. This is important! Otherwise, any user in Production will se your model structure, which may not be desireable. This is not the case if you are developing public API, though.

42 | 43 |

Step 3: Start your app and see the diagram

44 |

Start your app and browse to /db-diagrams page. You should see the diagram now.

45 |
46 |
47 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/getting-started/getting-started.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | 4 | main { 5 | 6 | .getting-started-steps { 7 | margin: 20px auto; 8 | max-width: 940px; 9 | 10 | @media (max-width: 972px) { 11 | margin: 16px; 12 | } 13 | 14 | h2 { 15 | margin-top: 40px; 16 | font-size: 24px; 17 | font-weight: 400; 18 | } 19 | 20 | p { 21 | font-size: 16px; 22 | line-height: 28px; 23 | } 24 | 25 | h2, p { 26 | color: rgba(0, 0, 0, 0.87); 27 | overflow-x: auto; 28 | } 29 | 30 | pre, 31 | code { 32 | font-family: Roboto Mono,monospace; 33 | white-space: pre; 34 | } 35 | 36 | pre { 37 | background: rgba(0, 0, 0, 0.01); 38 | border: 0.5px solid rgba(0, 0, 0, 0.03); 39 | border-radius: 5px; 40 | margin: 16px auto; 41 | display: block; 42 | padding: 20px; 43 | white-space: pre-wrap; 44 | font-size: 14px; 45 | overflow-x: auto; 46 | 47 | code { 48 | font-size: 90%; 49 | 50 | strong { 51 | padding: 2px 8px; 52 | margin: 0 -8px; 53 | background: rgba(mat-color(map-get($efd-app-theme, primary)), 0.16); 54 | border-radius: 2px; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/getting-started/getting-started.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { GettingStartedComponent } from './getting-started.component'; 4 | 5 | describe('GettingStartedComponent', () => { 6 | let component: GettingStartedComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ GettingStartedComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(GettingStartedComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/getting-started/getting-started.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-getting-started', 5 | templateUrl: './getting-started.component.html', 6 | styleUrls: ['./getting-started.component.scss'] 7 | }) 8 | export class GettingStartedComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/guides/guides.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Guides

4 |
5 | 6 |
7 | 8 | Getting started 9 | 10 |
11 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/guides/guides.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | 4 | main { 5 | min-height: 300px; 6 | 7 | md-nav-list { 8 | margin: 50px; 9 | 10 | @media (max-width: 488px) { 11 | margin: 16px; 12 | } 13 | 14 | [md-list-item] { 15 | color: mat-color(map-get($efd-app-theme, primary)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/guides/guides.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { GuidesComponent } from './guides.component'; 4 | 5 | describe('GuidesComponent', () => { 6 | let component: GuidesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ GuidesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(GuidesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/guides/guides.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-guides', 5 | templateUrl: './guides.component.html', 6 | styleUrls: ['./guides.component.scss'] 7 | }) 8 | export class GuidesComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/header/header.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/header/header.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | 4 | header { 5 | background: mat-color(map-get($efd-app-theme, primary)); 6 | color: rgba(255, 255, 255, 0.87); 7 | padding: 0 20px; 8 | box-sizing: border-box; 9 | 10 | ::ng-deep { 11 | h1 { 12 | font-size: 30px; 13 | font-weight: 300; 14 | margin: 0; 15 | padding: 50px; 16 | 17 | @media (max-width: 488px) { 18 | padding: 32px; 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/header/header.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HeaderComponent } from './header.component'; 4 | 5 | describe('HeaderComponent', () => { 6 | let component: HeaderComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HeaderComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HeaderComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/header/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-header', 5 | templateUrl: './header.component.html', 6 | styleUrls: ['./header.component.scss'] 7 | }) 8 | export class HeaderComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/homepage/homepage.component.html: -------------------------------------------------------------------------------- 1 |
2 |

Database Diagrams

3 |

Entity Framework Core model visualization

4 | Get Started 5 |
6 | 7 |
8 |
9 |
10 | 11 |
12 |
13 |

14 | No preinstall, zero config 15 |

16 |

17 | Visualize Model layer of your application as a ER-like diagram without any 3-rd party tools. 18 | View the diagram in any browser, no plug-ins needed. 19 |

20 |
21 |
22 | 23 |
24 |
25 |

Better than the docs

26 |

27 | Use database diagram instead of writing and constantly updating your documentation 28 | on the Model layer in your app. The diagram is perfect for both analysis and new 29 | features design. New developers in your project will love it, and your team meetings 30 | will be much more productive! 31 |

32 |
33 |
34 | 35 |
36 |
37 |

Collaborate

38 |

39 | Use import/export to save your diagram as a JSON file and share it with your team. 40 |

41 |
42 |
43 | 44 |
45 |
46 |

Print

47 |

48 | Print the diagram if you prefer working with paper. Will be useful on meetings. 49 |

50 |
51 |
52 |
53 |
54 | 55 |
56 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/homepage/homepage.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | 4 | $header-breakpoint: 503px; 5 | 6 | header { 7 | background: mat-color(map-get($efd-app-theme, primary)); 8 | color: rgba(255, 255, 255, 0.87); 9 | height: 300px; 10 | box-sizing: border-box; 11 | margin-bottom: 30px; 12 | padding: 60px 0 0; 13 | text-align: center; 14 | 15 | @media (max-width: $header-breakpoint) { 16 | padding-top: 8px; 17 | } 18 | 19 | h1 { 20 | font-size: 56px; 21 | font-weight: 300; 22 | line-height: 56px; 23 | margin: 15px 0 15px 0; 24 | 25 | @media (max-width: $header-breakpoint) { 26 | margin-top: 0; 27 | } 28 | } 29 | 30 | h2 { 31 | font-size: 18px; 32 | font-weight: 300; 33 | line-height: 28px; 34 | margin: 15px 0 25px 0; 35 | } 36 | 37 | a { 38 | margin-top: 36px; 39 | 40 | @media (max-width: $header-breakpoint) { 41 | margin-top: 16px; 42 | } 43 | } 44 | } 45 | 46 | main { 47 | min-height: 300px; 48 | 49 | article { 50 | margin: 0 16px; 51 | 52 | .rows { 53 | overflow: hidden; 54 | color: rgba(0, 0, 0, 0.87); 55 | 56 | .row { 57 | max-width: 920px; 58 | margin: 32px auto; 59 | display: flex; 60 | align-items: center; 61 | justify-content: center; 62 | box-sizing: border-box; 63 | 64 | @media (max-width: 952px) { 65 | margin: 16px; 66 | } 67 | 68 | .text-container { 69 | max-width: 450px; 70 | margin: auto; 71 | 72 | h2 { 73 | font-size: 25px; 74 | font-weight: 400; 75 | margin: 0 0 16px 0; 76 | padding: 0; 77 | } 78 | 79 | p { 80 | font-size: 16px; 81 | font-weight: 400; 82 | line-height: 28px; 83 | margin: 0 0 24px 0; 84 | padding: 0; 85 | } 86 | 87 | } 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/homepage/homepage.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { HomepageComponent } from './homepage.component'; 4 | 5 | describe('HomepageComponent', () => { 6 | let component: HomepageComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ HomepageComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(HomepageComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/homepage/homepage.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-homepage', 5 | templateUrl: './homepage.component.html', 6 | styleUrls: ['./homepage.component.scss'] 7 | }) 8 | export class HomepageComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DB Diagrams 6 | 7 | 8 | Demo 9 | Guides 10 | 11 | 12 | 13 | 17 | 18 | GitHub 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/navbar/navbar.component.scss: -------------------------------------------------------------------------------- 1 | 2 | md-toolbar { 3 | overflow-x: hidden; 4 | 5 | .title { 6 | flex: 0 0 160px; 7 | padding-left: 16px; 8 | text-decoration: none; 9 | margin-top: 1px; 10 | 11 | img { 12 | height: 32px; 13 | vertical-align: middle; 14 | margin-right: 8px; 15 | } 16 | 17 | span { 18 | vertical-align: middle; 19 | } 20 | 21 | @media (max-width: 600px) { 22 | flex: 0 0 120px; 23 | } 24 | } 25 | 26 | .content { 27 | flex: 1; 28 | } 29 | 30 | .github-logo { 31 | 32 | @media (max-width: 488px) { 33 | display: none; 34 | } 35 | 36 | &-sm { 37 | display: none; 38 | 39 | @media (max-width: 488px) { 40 | display: inline-block; 41 | } 42 | } 43 | } 44 | 45 | .github-logo, 46 | .github-logo-sm { 47 | img { 48 | height: 22px; 49 | margin: 0 6px; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavbarComponent } from './navbar.component'; 4 | 5 | describe('NavbarComponent', () => { 6 | let component: NavbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavbarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavbarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/components/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-navbar', 5 | templateUrl: './navbar.component.html', 6 | styleUrls: ['./navbar.component.scss'] 7 | }) 8 | export class NavbarComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/directives/markdown.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownDirective } from './markdown.directive'; 2 | 3 | describe('MarkdownDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new MarkdownDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/directives/markdown.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Renderer2, AfterContentInit } from '@angular/core'; 2 | import { Converter } from 'showdown'; 3 | 4 | @Directive({ 5 | selector: '[efdMarkdown]' 6 | }) 7 | export class MarkdownDirective implements AfterContentInit { 8 | 9 | constructor(private _el: ElementRef, private _renderer: Renderer2) { 10 | } 11 | 12 | ngAfterContentInit() { 13 | const converter = new Converter(); 14 | /*converter.addExtension([], 'codehighlight') 15 | 16 | showdown.extension('codehighlight', function() { 17 | function htmlunencode(text) { 18 | return ( 19 | text 20 | .replace(/&/g, '&') 21 | .replace(/</g, '<') 22 | .replace(/>/g, '>') 23 | ); 24 | } 25 | return [ 26 | { 27 | type: 'output', 28 | filter: function (text, converter, options) { 29 | // use new shodown's regexp engine to conditionally parse codeblocks 30 | var left = '
]*>',
31 |                         right = '
', 32 | flags = 'g', 33 | replacement = function (wholeMatch, match, left, right) { 34 | // unescape match to prevent double escaping 35 | match = htmlunencode(match); 36 | return left + hljs.highlightAuto(match).value + right; 37 | }; 38 | return showdown.helper.replaceRecursiveRegExp(text, replacement, left, right, flags); 39 | } 40 | } 41 | ]; 42 | }); */ 43 | 44 | const text = this._el.nativeElement.innerHTML 45 | const html = new Converter().makeHtml(text); 46 | this._renderer.setProperty(this._el.nativeElement, 'innerHTML', html); 47 | } 48 | 49 | } 50 | 51 | /* function showdownHighlight() { 52 | return [ 53 | { 54 | type: "output" 55 | , filter (text, converter, options) { 56 | let left = "
]*>"
57 |                   , right = "
" 58 | , flags = "g" 59 | , replacement = (wholeMatch, match, left, right) => { 60 | match = decodeHtml(match); 61 | return left + hljs.highlightAuto(match).value + right; 62 | } 63 | ; 64 | 65 | return showdown.helper.replaceRecursiveRegExp(text, replacement, left, right, flags); 66 | } 67 | } 68 | ]; 69 | } */ 70 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/directives/syntax-highlight.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { SyntaxHighlightDirective } from './syntax-highlight.directive'; 2 | 3 | describe('SyntaxHighlightDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new SyntaxHighlightDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/directives/syntax-highlight.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, OnInit, Input, ElementRef, Renderer2 } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[efdSyntaxHighlight]', 5 | }) 6 | export class SyntaxHighlightDirective implements OnInit { 7 | 8 | @Input() 9 | efdSyntaxHighlight: string; 10 | 11 | constructor(private readonly _el: ElementRef, private readonly _renderer: Renderer2) { 12 | } 13 | 14 | ngOnInit() { 15 | if ('hljs' in window) { 16 | const hljs = window['hljs']; 17 | const element = this._el.nativeElement as Element; 18 | const codeElement = element.nodeName === 'CODE' 19 | ? element 20 | : element.querySelector('code') || element; 21 | if (this.efdSyntaxHighlight) { 22 | codeElement.classList.add(this.efdSyntaxHighlight); 23 | } 24 | hljs.highlightBlock(codeElement); 25 | } else { 26 | console.error('Highlight.js API not available') 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/services/firebase-backend.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { FirebaseBackendService } from './firebase-backend.service'; 4 | 5 | describe('FirebaseBackendService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [FirebaseBackendService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([FirebaseBackendService], (service: FirebaseBackendService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/app/services/firebase-backend.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { AngularFireDatabase } from 'angularfire2/database'; 4 | import { AngularFireAuth } from 'angularfire2/auth'; 5 | import * as firebase from 'firebase'; 6 | 7 | import { DbModel } from '../../../src/app/models/db-model'; 8 | import { DbDiagramDto } from '../../../src/app/models/dto/db-diagram-dto'; 9 | 10 | @Injectable() 11 | export class FirebaseBackendService { 12 | 13 | constructor( 14 | private readonly _db: AngularFireDatabase, 15 | private readonly _auth: AngularFireAuth 16 | ) { 17 | /* this.addDiagram( 18 | 'evalexhimself@gmail.com', 19 | '19895923', 20 | { 21 | 22 | } 23 | ); */ 24 | } 25 | 26 | queryDbModel(): Observable { 27 | return this._db.list('/diagrams', { 28 | query: { 29 | limitToFirst: 1, 30 | orderByPriority: true 31 | } 32 | }).map(items => items.length > 0 ? DbDiagramDto.fromJSON(items[0].diagram).model : null); 33 | } 34 | 35 | addDiagram(email, password, diargam) { 36 | this._auth.auth.signInWithEmailAndPassword(email, password) 37 | .then(user => { 38 | this._db.list('/diagrams').push({ 39 | diagram: diargam, 40 | authorUid: user.uid, 41 | timestamp: firebase.database.ServerValue.TIMESTAMP 42 | }); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/assets/.gitkeep -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/assets/github-circle-white-transparent.svg: -------------------------------------------------------------------------------- 1 | github-circle-white-transparent -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/assets/icon.png -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | import { firebaseConfig } from './firebase-config'; 2 | 3 | export const environment = { 4 | production: true, 5 | firebase: firebaseConfig 6 | }; 7 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | import { firebaseConfig } from './firebase-config'; 6 | 7 | export const environment = { 8 | production: false, 9 | firebase: firebaseConfig 10 | }; 11 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/environments/firebase-config.ts: -------------------------------------------------------------------------------- 1 | 2 | export const firebaseConfig = { 3 | apiKey: "AIzaSyDDSseuMfdgCFfztSsnAhCIgi-itsfPEvM", 4 | authDomain: "db-diagrams.firebaseapp.com", 5 | databaseURL: "https://db-diagrams.firebaseio.com", 6 | projectId: "db-diagrams", 7 | storageBucket: "db-diagrams.appspot.com", 8 | messagingSenderId: "290073738630" 9 | }; 10 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/favicon.ico -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DbDiagramsWebsite 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import 'theme'; 4 | @import 'syntax-highlight'; 5 | 6 | html { 7 | font-family: Roboto; 8 | 9 | body { 10 | margin: 0; 11 | 12 | [md-button], 13 | [md-raised-button] { 14 | text-transform: uppercase; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src-website/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/_mixins.scss: -------------------------------------------------------------------------------- 1 | @function easing-fn() { 2 | @return cubic-bezier(0.4, 0.0, 0.2, 1); 3 | } 4 | 5 | @function transition-property($property: all, $delay: 0ms, $duration: 200ms) { 6 | @return #{$property} #{$duration} easing-fn() #{$delay}; 7 | } 8 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/_theme.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | // Plus imports for other components in your app. 3 | 4 | // Include the common styles for Angular Material. We include this here so that you only 5 | // have to load a single css file for Angular Material in your app. 6 | // Be sure that you only ever include this mixin once! 7 | @include mat-core(); 8 | 9 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 10 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 11 | // hue. 12 | $efd-app-primary: mat-palette($mat-indigo); 13 | $efd-app-accent: mat-palette($mat-pink, A200, A100, A400); 14 | 15 | // The warn palette is optional (defaults to red). 16 | $efd-app-warn: mat-palette($mat-red); 17 | 18 | // Create the theme object (a Sass map containing all of the palettes). 19 | $efd-app-theme: mat-light-theme($efd-app-primary, $efd-app-accent, $efd-app-warn); 20 | 21 | // Include theme styles for core and each component used in your app. 22 | // Alternatively, you can import and @include the theme mixins for each component 23 | // that you are using. 24 | @include angular-material-theme($efd-app-theme); 25 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpModule } from '@angular/http'; 5 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 6 | import { MaterialModule } from '@angular/material'; 7 | import { CdkTableModule } from '@angular/cdk'; 8 | 9 | import 'rxjs/add/operator/map'; 10 | import 'rxjs/add/operator/filter'; 11 | import 'rxjs/add/operator/do'; 12 | import 'rxjs/add/operator/mergeMap'; 13 | import 'rxjs/add/operator/takeUntil'; 14 | import 'rxjs/add/operator/debounceTime'; 15 | import 'rxjs/add/operator/debounce'; 16 | import 'rxjs/add/operator/throttleTime'; 17 | import 'rxjs/add/operator/combineLatest'; 18 | import 'rxjs/add/observable/of'; 19 | import 'rxjs/add/observable/timer'; 20 | import 'rxjs/add/observable/fromEvent'; 21 | 22 | import 'hammerjs'; 23 | 24 | import { AppComponent } from './components/app/app.component'; 25 | import { DbDiagramComponent } from './components/db-diagram/db-diagram.component'; 26 | import { ApiService } from './services/api.service'; 27 | import { DiagramLayoutService } from './services/diagram-layout.service'; 28 | import { DbEntityDiagramFigureComponent } from './components/db-entity-diagram-figure/db-entity-diagram-figure.component'; 29 | import { ClrTypeComponent } from './components/clr-type/clr-type.component'; 30 | import { ScalableDirective } from './directives/scalable.directive'; 31 | import { DbRelationConnectorComponent } from './components/db-relation-connector/db-relation-connector.component'; 32 | import { DraggableDirective } from './directives/draggable.directive'; 33 | import { ScalingToolbarComponent } from './components/scaling-toolbar/scaling-toolbar.component'; 34 | import { MouseEdgePanDirective } from './directives/mouse-edge-pan.directive'; 35 | import { MinimapComponent } from './components/minimap/minimap.component'; 36 | import { ScrollbarWidthDirective } from './directives/scrollbar-width.directive'; 37 | import { DownloadDataDirective } from './directives/download-data.directive'; 38 | import { ExportDialogComponent } from './components/export-dialog/export-dialog.component'; 39 | import { DevicemotionScrollDirective } from './directives/devicemotion-scroll.directive'; 40 | 41 | @NgModule({ 42 | declarations: [ 43 | AppComponent, 44 | DbDiagramComponent, 45 | DbEntityDiagramFigureComponent, 46 | ClrTypeComponent, 47 | ScalableDirective, 48 | DbRelationConnectorComponent, 49 | DraggableDirective, 50 | ScalingToolbarComponent, 51 | MouseEdgePanDirective, 52 | MinimapComponent, 53 | ScrollbarWidthDirective, 54 | DownloadDataDirective, 55 | ExportDialogComponent, 56 | DevicemotionScrollDirective, 57 | ], 58 | entryComponents: [ExportDialogComponent], 59 | imports: [ 60 | BrowserModule, 61 | HttpModule, 62 | FormsModule, 63 | BrowserAnimationsModule, 64 | MaterialModule, 65 | CdkTableModule 66 | ], 67 | providers: [ApiService, DiagramLayoutService], 68 | bootstrap: [AppComponent], 69 | schemas: [CUSTOM_ELEMENTS_SCHEMA], 70 | exports: [ 71 | AppComponent 72 | ] 73 | }) 74 | export class DbDiagramsAppModule { 75 | } 76 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | }).compileComponents(); 12 | })); 13 | 14 | it('should create the app', async(() => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.debugElement.componentInstance; 17 | expect(app).toBeTruthy(); 18 | })); 19 | 20 | it(`should have as title 'efd'`, async(() => { 21 | const fixture = TestBed.createComponent(AppComponent); 22 | const app = fixture.debugElement.componentInstance; 23 | expect(app.title).toEqual('efd'); 24 | })); 25 | 26 | it('should render title in a h1 tag', async(() => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | fixture.detectChanges(); 29 | const compiled = fixture.debugElement.nativeElement; 30 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to efd!'); 31 | })); 32 | }); 33 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/clr-type/clr-type.component.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | {{ token.value }} 9 | 10 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/clr-type/clr-type.component.scss: -------------------------------------------------------------------------------- 1 | 2 | :host { 3 | // font-family: Roboto monospace; 4 | display: inline-flex; 5 | } 6 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/clr-type/clr-type.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ClrTypeComponent } from './clr-type.component'; 4 | 5 | describe('ClrTypeComponent', () => { 6 | let component: ClrTypeComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ClrTypeComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ClrTypeComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-diagram/db-diagram.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Database model is empty

5 | 6 |
13 | 14 |
15 | 16 | 29 | 31 | 32 | 33 | 34 | 35 | 36 |
58 |
59 |
60 | 61 |
62 | 63 | 64 | 65 |
66 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-diagram/db-diagram.component.scss: -------------------------------------------------------------------------------- 1 | @import '~@angular/material/theming'; 2 | @import '../../../theme'; 3 | @import '../../../mixins'; 4 | 5 | $relation-color-normal: #cacaca; 6 | 7 | :host { 8 | display: block; 9 | width: 100%; 10 | height: 100%; 11 | position: relative; 12 | 13 | .diagram-scroll-container { 14 | height: 100%; 15 | width: 100%; 16 | overflow: auto; 17 | // border: 1px solid black; 18 | position: relative; 19 | 20 | @media print { 21 | overflow: hidden; 22 | } 23 | 24 | .diagram-container { 25 | margin: 16px; 26 | position: relative; 27 | height: 100%; 28 | } 29 | } 30 | 31 | efd-db-entity-diagram-figure { 32 | } 33 | 34 | .relation-path-line { 35 | position: absolute; 36 | background: $relation-color-normal; 37 | // transition: transition-property(background); 38 | // @include mat-elevation(2); 39 | 40 | @media print { 41 | -webkit-print-color-adjust: exact; 42 | } 43 | 44 | &.hover { 45 | background: mat-color(map-get($efd-app-theme, accent)); 46 | 47 | &.connected-before:before, 48 | &.connected-after:after { 49 | background: mat-color(map-get($efd-app-theme, accent)); 50 | } 51 | } 52 | 53 | &.connected-before:before, 54 | &.connected-after:after { 55 | content: ' '; 56 | display: block; 57 | width: 4px; 58 | height: 4px; 59 | position: absolute; 60 | background: $relation-color-normal; 61 | border-radius: 2px; 62 | } 63 | 64 | &.horizontal { 65 | &.connected-before:before { 66 | left: -2px; 67 | } 68 | &.connected-after:after { 69 | right: -2px; 70 | } 71 | } 72 | 73 | &.vertical { 74 | &.connected-before:before { 75 | top: -2px; 76 | } 77 | &.connected-after:after { 78 | bottom: -2px; 79 | } 80 | } 81 | } 82 | 83 | efd-minimap { 84 | z-index: 20; 85 | 86 | &.hidden { 87 | display: none; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-diagram/db-diagram.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DbDiagramComponent } from './db-diagram.component'; 4 | 5 | describe('DbDiagramComponent', () => { 6 | let component: DbDiagramComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DbDiagramComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DbDiagramComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-diagram/db-diagram.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, AfterViewInit, HostListener, Renderer2, NgZone } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { Subject } from 'rxjs/Subject'; 4 | 5 | import { DiagramLayoutService } from '../../services/diagram-layout.service'; 6 | import { DbModel } from '../../models/db-model'; 7 | import { DbEntity } from '../../models/db-entity'; 8 | import { DbEntityLayout } from '../../models/db-entity-layout'; 9 | import { DbEntityRelationLayout } from '../../models/db-entity-relation-layout'; 10 | import { Line } from '../../models/line'; 11 | import { EventDebouncer } from '../../core/event-debouncer'; 12 | import { IScaleInfo, DEFAULT_SCALE } from '../../directives/scalable.directive'; 13 | 14 | @Component({ 15 | selector: 'efd-db-diagram', 16 | templateUrl: './db-diagram.component.html', 17 | styleUrls: ['./db-diagram.component.scss'] 18 | }) 19 | export class DbDiagramComponent implements OnInit, AfterViewInit { 20 | private mouseOverListener: Function; 21 | 22 | @Input() 23 | model: DbModel; 24 | 25 | currentScale: IScaleInfo = { scale: DEFAULT_SCALE }; 26 | 27 | get hoveredRelation() { return this._diagramLayout.hoveredRelation; } 28 | 29 | get modelLayout() { return this._diagramLayout.getModelLayout(this.model); } 30 | 31 | constructor(private readonly _diagramLayout: DiagramLayoutService, renderer: Renderer2, zone: NgZone) { 32 | const debouncer = new EventDebouncer(zone, renderer); 33 | this.mouseOverListener = debouncer.listen('window', 'mouseover', e => { 34 | zone.run(() => { 35 | this._diagramLayout.hoveredRelation = null; 36 | this._diagramLayout.hoveredEntity = null; 37 | }); 38 | e.stopPropagation(); 39 | }); 40 | } 41 | 42 | ngOnInit() { 43 | } 44 | 45 | ngAfterViewInit() { 46 | // NOTE: need to delay it, because view is already rendered, and data-bound values are applied. 47 | Observable.timer(1) 48 | .subscribe(() => this._diagramLayout.arrangeLayout(this.model)); 49 | } 50 | 51 | getEntityKey(index: number, entity: DbEntityLayout) { 52 | return entity.key; 53 | } 54 | 55 | onEntityDragStart(entity: DbEntity) { 56 | const entityLayout = this._diagramLayout.getEntityLayout(this.model, entity); 57 | this._diagramLayout.draggedEntity = entityLayout; 58 | } 59 | 60 | onEntityDragEnd(entity: DbEntity) { 61 | const entityLayout = this._diagramLayout.getEntityLayout(this.model, entity); 62 | this._diagramLayout.draggedEntity = null; 63 | } 64 | 65 | onEntityDrag(entity: DbEntity, { top, left }: { top: number, left: number }) { 66 | this._diagramLayout.moveEntity(this.model, entity, left, top); 67 | } 68 | 69 | onMouseOverRelation(e: MouseEvent, relation: DbEntityRelationLayout) { 70 | this._diagramLayout.hoveredRelation = relation; 71 | e.stopPropagation(); 72 | } 73 | 74 | onRelationLineDrag(relation: DbEntityRelationLayout, line: Line, { top, left }: { top: number, left: number }) { 75 | relation.moveLine(line, left, top); 76 | } 77 | 78 | onRelationLineDragStart(relation: DbEntityRelationLayout, line: Line) { 79 | 80 | } 81 | 82 | onRelationLineDragEnd(relation: DbEntityRelationLayout, line: Line) { 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-entity-diagram-figure/db-entity-diagram-figure.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{ modelLayout.entitiesTableSettings.getColumn('key').name }} 17 | 18 | 19 | vpn_key 20 | FK 21 | 22 | 23 | 24 | 25 | 26 | 27 | {{ modelLayout.entitiesTableSettings.getColumn('name').name }} 28 | 29 | {{ row.name }} 30 | 31 | 32 | 33 | 34 | 35 | {{ modelLayout.entitiesTableSettings.getColumn('clrType').name }} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {{ modelLayout.entitiesTableSettings.getColumn('nullable').name }} 46 | 47 | 48 | NULL 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-entity-diagram-figure/db-entity-diagram-figure.component.scss: -------------------------------------------------------------------------------- 1 | @import '../../../mixins'; 2 | 3 | :host { 4 | position: absolute; 5 | display: inline-block; 6 | font-family: Roboto monospace; 7 | min-width: 320px; 8 | transition: transition-property(top) transition-property(left); 9 | 10 | md-card { 11 | padding: 24px; 12 | 13 | @media print { 14 | border: 1px solid rgba(0, 0, 0, 0.2); 15 | box-shadow: none; 16 | } 17 | } 18 | 19 | md-card-title { 20 | display: flex; 21 | align-items: center; 22 | 23 | span { 24 | flex: 1; 25 | } 26 | } 27 | 28 | 29 | md-card-content { 30 | margin: 0 -24px -24px; 31 | overflow: hidden; 32 | 33 | &.collapsed { 34 | display: none; 35 | } 36 | 37 | .mat-table { 38 | display: table; 39 | width: 100%; 40 | 41 | > .mat-header-row, > .mat-row { 42 | display: table-row; 43 | padding: 0; 44 | border: none; 45 | 46 | > .mat-header-cell, > .mat-cell { 47 | display: table-cell; 48 | height: 48px; 49 | vertical-align: middle; 50 | 51 | border-bottom: 1px solid rgba(0, 0, 0, 0.12); 52 | } 53 | } 54 | 55 | > .mat-row:last-child > .mat-cell { 56 | border-bottom: none; 57 | } 58 | } 59 | 60 | md-table { 61 | 62 | .mat-column-key { 63 | width: 36px; 64 | padding: 0 16px; 65 | 66 | &.mat-cell { 67 | color: rgba(0, 0, 0, 0.4); 68 | } 69 | 70 | md-icon { 71 | font-size: 16px; 72 | height: 16px; 73 | } 74 | } 75 | 76 | .mat-column-clrType { 77 | padding: 0 16px; 78 | } 79 | 80 | .mat-column-nullable { 81 | width: 60px; 82 | padding: 0 16px; 83 | 84 | &.mat-cell { 85 | color: rgba(0, 0, 0, 0.4); 86 | } 87 | } 88 | 89 | /* @media print { 90 | } 91 | 92 | @media print and (orientation: landscape) { 93 | margin: 0 -23px; 94 | } 95 | 96 | @media print and (orientation: portrait) { 97 | margin: 0 -15px; 98 | } */ 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-entity-diagram-figure/db-entity-diagram-figure.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DbEntityDiagramFigureComponent } from './db-entity-diagram-figure.component'; 4 | 5 | describe('DbEntityDiagramFigureComponent', () => { 6 | let component: DbEntityDiagramFigureComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DbEntityDiagramFigureComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DbEntityDiagramFigureComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-relation-connector/db-relation-connector.component.html: -------------------------------------------------------------------------------- 1 | 2 |
8 |
9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-relation-connector/db-relation-connector.component.scss: -------------------------------------------------------------------------------- 1 | 2 | :host { 3 | position: relative; 4 | display: block; 5 | top: 0; 6 | right: 0; 7 | bottom: 0; 8 | left: 0; 9 | height: 100%; 10 | 11 | .relation-path-line { 12 | position: absolute; 13 | background: black; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-relation-connector/db-relation-connector.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DbRelationConnectorComponent } from './db-relation-connector.component'; 4 | 5 | describe('DbRelationConnectorComponent', () => { 6 | let component: DbRelationConnectorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DbRelationConnectorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DbRelationConnectorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/db-relation-connector/db-relation-connector.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | 3 | import { DbEntityRelationLayout } from '../../models/db-entity-relation-layout'; 4 | 5 | @Component({ 6 | selector: 'efd-db-relation-connector', 7 | templateUrl: './db-relation-connector.component.html', 8 | styleUrls: ['./db-relation-connector.component.scss'] 9 | }) 10 | export class DbRelationConnectorComponent implements OnInit { 11 | 12 | @Input() 13 | relation: DbEntityRelationLayout; 14 | 15 | constructor() { } 16 | 17 | ngOnInit() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/export-dialog/export-dialog.component.html: -------------------------------------------------------------------------------- 1 | 2 |

Export

3 | 4 | 5 | 6 | 7 | Layout only 8 | Model with Layout 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | file_download 17 | Export 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/export-dialog/export-dialog.component.scss: -------------------------------------------------------------------------------- 1 | 2 | :host { 3 | min-width: 320px; 4 | min-height: 180px; 5 | display: flex; 6 | flex-direction: column; 7 | 8 | md-dialog-content { 9 | flex: 1; 10 | padding: 16px; 11 | 12 | md-radio-button { 13 | display: block; 14 | margin-bottom: 16px; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/export-dialog/export-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ExportDialogComponent } from './export-dialog.component'; 4 | 5 | describe('ExportDialogComponent', () => { 6 | let component: ExportDialogComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ExportDialogComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ExportDialogComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/export-dialog/export-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject } from '@angular/core'; 2 | import { MD_DIALOG_DATA, MdDialogRef } from '@angular/material'; 3 | 4 | import { DbModel } from '../../models/db-model'; 5 | import { DiagramLayoutService } from '../../services/diagram-layout.service'; 6 | 7 | enum ExportType { 8 | Layout, 9 | ModelWithLayout 10 | } 11 | 12 | @Component({ 13 | selector: 'efd-export-dialog', 14 | templateUrl: './export-dialog.component.html', 15 | styleUrls: ['./export-dialog.component.scss'] 16 | }) 17 | export class ExportDialogComponent implements OnInit { 18 | 19 | get exportTypeLayout() { return ExportType.Layout; } 20 | get exportTypeModelWithLayout() { return ExportType.ModelWithLayout; } 21 | 22 | get selectedExportType() { return this._selectedExportType; } 23 | set selectedExportType(value: ExportType) { 24 | this._selectedExportType = value; 25 | this.updateExportData(); 26 | } 27 | private _selectedExportType = ExportType.Layout; 28 | 29 | exportData; 30 | exportFilename = 'data.json'; 31 | 32 | constructor( 33 | private readonly _diagramLayout: DiagramLayoutService, 34 | private readonly _dialog: MdDialogRef, 35 | @Inject(MD_DIALOG_DATA) public model: DbModel 36 | ) { 37 | } 38 | 39 | ngOnInit() { 40 | this.updateExportData(); 41 | } 42 | 43 | updateExportData() { 44 | switch (this.selectedExportType) { 45 | case ExportType.Layout: 46 | this.exportData = this._diagramLayout.exportLayout(this.model); 47 | this.exportFilename = `db-diagram_layout_${this.getTimestamp()}.json`; 48 | break; 49 | case ExportType.ModelWithLayout: 50 | this.exportData = this._diagramLayout.exportModelWithLayout(this.model); 51 | this.exportFilename = `db-diagram_model-and-layout_${this.getTimestamp()}.json`; 52 | break; 53 | default: 54 | throw new Error('Export type not supported: ' + this.selectedExportType); 55 | } 56 | } 57 | 58 | close() { 59 | this._dialog.close(); 60 | } 61 | 62 | private getTimestamp() { 63 | const now = new Date(); 64 | const year = now.getFullYear(); 65 | const month = now.getMonth() + 1; 66 | const date = now.getDate(); 67 | const hours = now.getHours(); 68 | const min = now.getMinutes(); 69 | const sec = now.getSeconds(); 70 | const timestamp = `${year}-${month}-${date}_${hours}_${min}_${sec}`; 71 | return timestamp; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/minimap/minimap.component.html: -------------------------------------------------------------------------------- 1 | 2 |
9 |
10 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/minimap/minimap.component.scss: -------------------------------------------------------------------------------- 1 | 2 | :host { 3 | position: fixed; 4 | display: block; 5 | border: 1px solid rgba(black, 0.4); 6 | border-radius: 2px; 7 | 8 | @media print { 9 | display: none; 10 | } 11 | 12 | &:hover { 13 | .viewport { 14 | background: rgba(black, 0.1); 15 | } 16 | } 17 | 18 | .viewport { 19 | border: 1px solid rgba(black, 0.4); 20 | border-radius: 2px; 21 | 22 | &:hover { 23 | background: rgba(black, 0.2); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/minimap/minimap.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MinimapComponent } from './minimap.component'; 4 | 5 | describe('MinimapComponent', () => { 6 | let component: MinimapComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ MinimapComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MinimapComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/scaling-toolbar/scaling-toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/scaling-toolbar/scaling-toolbar.component.scss: -------------------------------------------------------------------------------- 1 | 2 | @media (max-width: 800px) { 3 | .mat-button { 4 | min-width: 48px; 5 | } 6 | } 7 | 8 | @media (max-width: 700px) { 9 | .mat-icon-button { 10 | width: 24px; 11 | height: 24px; 12 | line-height: 24px; 13 | } 14 | } 15 | 16 | @media (max-width: 570px) { 17 | :host { 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/scaling-toolbar/scaling-toolbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ScalingToolbarComponent } from './scaling-toolbar.component'; 4 | 5 | describe('ScalingToolbarComponent', () => { 6 | let component: ScalingToolbarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ScalingToolbarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ScalingToolbarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should be created', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/components/scaling-toolbar/scaling-toolbar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'efd-scaling-toolbar', 5 | templateUrl: './scaling-toolbar.component.html', 6 | styleUrls: ['./scaling-toolbar.component.scss'] 7 | }) 8 | export class ScalingToolbarComponent implements OnInit { 9 | 10 | @Input() 11 | defaultScale = 1; 12 | 13 | @Input() 14 | minScale = 0.1; 15 | 16 | @Input() 17 | maxScale = 10; 18 | 19 | @Input() 20 | scale: number = null; 21 | 22 | @Output() 23 | scaleChange = new EventEmitter(); 24 | 25 | constructor() { } 26 | 27 | ngOnInit() { 28 | if (this.scale === null) { 29 | this.scale = this.defaultScale; 30 | } 31 | } 32 | 33 | zoomIn() { 34 | this.scale += this.getStep(); 35 | this.scaleChange.emit(this.scale); 36 | } 37 | 38 | zoomOut() { 39 | this.scale -= this.getStep(); 40 | this.scaleChange.emit(this.scale); 41 | } 42 | 43 | resetZoom() { 44 | this.scale = this.defaultScale; 45 | this.scaleChange.emit(this.scale); 46 | } 47 | 48 | private getStep(): number { 49 | return this.scale <= this.defaultScale 50 | ? (this.defaultScale - this.minScale) / 20 51 | : (this.maxScale - this.defaultScale) / 20; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/core/event-debouncer.ts: -------------------------------------------------------------------------------- 1 | import { Renderer2, NgZone } from '@angular/core'; 2 | import { Subject } from 'rxjs/Subject'; 3 | 4 | export class EventDebouncer { 5 | constructor(private _zone: NgZone, private _renderer: Renderer2) { 6 | } 7 | 8 | listen( 9 | target: 'window' | 'document' | 'body' | any, 10 | eventName: string, 11 | listener: (event: any) => boolean | void, 12 | debounceTime: number = 40 13 | ): () => void { 14 | return this._zone.runOutsideAngular(() => { 15 | const tempSubj = new Subject(); 16 | const result = this._renderer.listen(target, eventName, (e: MouseEvent) => { 17 | tempSubj.next(e); 18 | e.preventDefault(); 19 | e.stopPropagation(); 20 | }); 21 | tempSubj.debounceTime(debounceTime).subscribe(e => this._zone.run(() => listener(e))); 22 | return result; 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/devicemotion-scroll.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { DevicemotionScrollDirective } from './devicemotion-scroll.directive'; 2 | 3 | describe('DevicemotionScrollDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new DevicemotionScrollDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/download-data.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { DownloadDataDirective } from './download-data.directive'; 2 | 3 | describe('DownloadDataDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new DownloadDataDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/download-data.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, HostBinding, Input, OnInit, OnChanges } from '@angular/core'; 2 | import { DomSanitizer } from '@angular/platform-browser'; 3 | 4 | @Directive({ 5 | selector: 'a[efdDownloadData]' 6 | }) 7 | export class DownloadDataDirective implements OnInit, OnChanges { 8 | private _blob: Blob = null; 9 | private _url: string = null; 10 | 11 | @Input() 12 | efdDownloadData; 13 | 14 | @Input('efdDownloadFilename') 15 | @HostBinding('download') 16 | efdDownloadFilename = 'data.json'; 17 | 18 | @HostBinding('href') 19 | href; 20 | 21 | constructor(private readonly _el: ElementRef, private readonly _sanitizer: DomSanitizer) { 22 | } 23 | 24 | ngOnInit() { 25 | } 26 | 27 | ngOnChanges() { 28 | if (this._url !== null) { 29 | window.URL.revokeObjectURL(this._url); 30 | } 31 | const json = JSON.stringify(this.efdDownloadData); 32 | this._blob = new Blob([json], { type: 'application/json' }); 33 | this._url = window.URL.createObjectURL(this._blob); 34 | 35 | this.href = this._sanitizer.bypassSecurityTrustUrl(this._url); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/draggable.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { DraggableDirective } from './draggable.directive'; 2 | 3 | describe('DraggableDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new DraggableDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/mouse-edge-pan.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { MouseEdgePanDirective } from './mouse-edge-pan.directive'; 2 | 3 | describe('MouseEdgePanDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new MouseEdgePanDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/mouse-edge-pan.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, OnInit, OnDestroy, HostListener, ElementRef, Renderer2 } from '@angular/core'; 2 | import { Observable } from 'rxjs/Observable'; 3 | import { Subject } from 'rxjs/Subject'; 4 | 5 | const TRIGGER_TIME = 100; 6 | const HOTZONE_WIDTH = 0.1; 7 | const FEED_PAN_FREQ = 25; 8 | const PAN_STEP = 20; 9 | 10 | @Directive({ 11 | selector: '[efdMouseEdgePan]' 12 | }) 13 | export class MouseEdgePanDirective implements OnInit, OnDestroy { 14 | private readonly _pan = new Subject<{ dx: number, dy: number }>(); 15 | 16 | @HostListener('mousemove', ['$event']) 17 | onMouseMove(e: MouseEvent) { 18 | const element = this._el.nativeElement as Element; 19 | const rect = element.getBoundingClientRect(); 20 | const zoneWidth = rect.width * HOTZONE_WIDTH; 21 | const zoneHeight = rect.height * HOTZONE_WIDTH; 22 | const inLeftZone = e.clientX <= rect.left + zoneWidth; 23 | const inRightZone = e.clientX >= rect.right - zoneWidth; 24 | const inTopZone = e.clientY <= rect.top + zoneHeight; 25 | const inBottomZone = e.clientY >= rect.bottom - zoneHeight; 26 | 27 | const pan = { dx: 0, dy: 0 }; 28 | 29 | if (inLeftZone) { 30 | const multiplier = (zoneWidth - (e.clientX - rect.left)) / zoneWidth; 31 | pan.dx = -PAN_STEP * multiplier; 32 | } else if (inRightZone) { 33 | const multiplier = (zoneWidth - (rect.right - e.clientX)) / zoneWidth; 34 | pan.dx = PAN_STEP * multiplier; 35 | } 36 | 37 | if (inTopZone) { 38 | const multiplier = (zoneHeight - (e.clientY - rect.top)) / zoneHeight; 39 | pan.dy = -PAN_STEP * multiplier; 40 | } else if (inBottomZone) { 41 | const multiplier = (zoneHeight - (rect.bottom - e.clientY)) / zoneHeight; 42 | pan.dy = PAN_STEP * multiplier; 43 | } 44 | 45 | this._pan.next(pan); 46 | } 47 | 48 | @HostListener('mouseleave') 49 | onMouseLeave() { 50 | this._pan.next({ dx: 0, dy: 0 }); 51 | } 52 | 53 | constructor(private readonly _el: ElementRef) { 54 | } 55 | 56 | ngOnInit() { 57 | Observable.timer(0, FEED_PAN_FREQ) 58 | .combineLatest( 59 | // NOTE: hovering over hotzone for TRIGGER_TIME causes panning to start. But cancel should be immediate. 60 | this._pan.debounce(e => e.dx === e.dy && e.dx === 0 ? Observable.timer(0) : Observable.timer(TRIGGER_TIME)), 61 | (a, b) => b 62 | ) 63 | .filter(e => e.dx !== 0 || e.dy !== 0) 64 | .subscribe(e => { 65 | const element = this._el.nativeElement as Element; 66 | element.scrollLeft += e.dx; 67 | element.scrollTop += e.dy; 68 | }); 69 | 70 | } 71 | 72 | ngOnDestroy() { 73 | this._pan.next({ dx: 0, dy: 0 }); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/scalable.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ScalableDirective } from './scalable.directive'; 2 | 3 | describe('ScalableDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new ScalableDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/scrollbar-width.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { ScrollbarWidthDirective } from './scrollbar-width.directive'; 2 | 3 | describe('ScrollbarWidthDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new ScrollbarWidthDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/directives/scrollbar-width.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[efdScrollbarWidth]', 5 | exportAs: 'efdScrollbarWidth' 6 | }) 7 | export class ScrollbarWidthDirective { 8 | 9 | get horizontalScrollbarWidth() { 10 | const element = this._el.nativeElement as HTMLElement; 11 | return element.offsetWidth - element.clientWidth; 12 | } 13 | 14 | get verticalScrollbarWidth() { 15 | const element = this._el.nativeElement as HTMLElement; 16 | return element.offsetHeight - element.clientHeight; 17 | } 18 | 19 | constructor(private readonly _el: ElementRef) { 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/clr-type.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ClrType { 3 | namespace: string; 4 | name: string; 5 | assembly: string; 6 | genericTypeArguments: ClrType[] = []; 7 | 8 | get isGeneric() { return !this.genericTypeArguments || this.genericTypeArguments.length === 0; } 9 | 10 | get prettyName(): string { 11 | let result = this.name; 12 | switch (result) { 13 | case 'Int32': 14 | result = 'int'; 15 | break; 16 | case 'UInt32': 17 | result = 'uint'; 18 | break; 19 | case 'Int16': 20 | result = 'short'; 21 | break; 22 | case 'UInt16': 23 | result = 'ushort'; 24 | break; 25 | case 'Int64': 26 | result = 'long'; 27 | break; 28 | case 'UInt64': 29 | result = 'ulong'; 30 | break; 31 | case 'Byte': 32 | result = 'byte'; 33 | break; 34 | case 'SByte': 35 | result = 'sbyte'; 36 | break; 37 | case 'Char': 38 | result = 'char'; 39 | break; 40 | case 'Single': 41 | result = 'float'; 42 | break; 43 | case 'Double': 44 | result = 'double'; 45 | break; 46 | case 'Decimal': 47 | result = 'int'; 48 | break; 49 | case 'String': 50 | result = 'string'; 51 | break; 52 | case 'Boolean': 53 | result = 'bool'; 54 | break; 55 | } 56 | 57 | const index = result.indexOf('`'); 58 | if (index !== -1) { 59 | result = result.substring(0, index); 60 | } 61 | 62 | return result; 63 | } 64 | 65 | get isPrettyNameKeyword(): boolean { 66 | return this.name.indexOf('`') === -1 && this.prettyName !== this.name; 67 | } 68 | 69 | static fromJSON(obj: Object) { 70 | return Object.assign(new ClrType(), obj, { 71 | genericTypeArguments: obj['genericTypeArguments'] 72 | ? obj['genericTypeArguments'].map(e => ClrType.fromJSON(e)) 73 | : [] 74 | }); 75 | } 76 | 77 | equals(other: ClrType): boolean { 78 | return other instanceof ClrType && ( 79 | this === other 80 | || this.assembly === other.assembly 81 | && this.namespace === other.namespace 82 | && this.name === other.name 83 | && this.genericTypeArguments.length === other.genericTypeArguments.length 84 | && this.genericTypeArguments.every((e, i) => other.genericTypeArguments[i].equals(e)) 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/column-settings.ts: -------------------------------------------------------------------------------- 1 | 2 | export class ColumnSettings { 3 | 4 | static fromJSON(value: Object): ColumnSettings { 5 | return Object.assign( 6 | new ColumnSettings(null, null, null), 7 | value 8 | ); 9 | } 10 | 11 | constructor( 12 | public readonly key: string, 13 | public name: string, 14 | public visible: boolean, 15 | public readonly alwaysVisible = false 16 | ) { 17 | } 18 | 19 | toggleVisibility() { 20 | if (this.alwaysVisible) { 21 | throw new Error('Cannot toggle visibility for column ' + this.key); 22 | } 23 | this.visible = !this.visible; 24 | } 25 | 26 | canToggleVisibility() { 27 | return !this.alwaysVisible; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-foreign-key.ts: -------------------------------------------------------------------------------- 1 | import { DbEntity } from './db-entity'; 2 | import { DbEntityKey } from './db-entity-key'; 3 | import { DbEntityProperty } from './db-entity-property'; 4 | 5 | export class DbEntityForeignKey { 6 | principalEntity: DbEntity; 7 | principalKey: DbEntityKey; 8 | properties: DbEntityProperty[] = []; 9 | 10 | static fromJSON(obj: Object): DbEntityForeignKey { 11 | return Object.assign(new DbEntityForeignKey(), obj, { 12 | principalEntity: DbEntity.fromJSON(obj['principalEntity']), 13 | principalKey: DbEntityKey.fromJSON(obj['principalKey']), 14 | properties: (obj['properties'] || []).map(e => DbEntityProperty.fromJSON(e)), 15 | }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-index.ts: -------------------------------------------------------------------------------- 1 | import { DbEntityProperty } from './db-entity-property'; 2 | 3 | export class DbEntityIndex { 4 | properties: DbEntityProperty[] = []; 5 | isUnique: boolean; 6 | 7 | static fromJSON(obj: Object): DbEntityIndex { 8 | return Object.assign(new DbEntityIndex(), obj, { 9 | properties: (obj['properties'] || []).map(e => DbEntityProperty.fromJSON(e)) 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-key.ts: -------------------------------------------------------------------------------- 1 | import { DbEntityProperty } from './db-entity-property'; 2 | 3 | export class DbEntityKey { 4 | properties: DbEntityProperty[] = []; 5 | 6 | static fromJSON(obj: Object): DbEntityKey { 7 | return Object.assign(new DbEntityKey(), obj, { 8 | properties: (obj['properties'] || []).map(e => DbEntityProperty.fromJSON(e)) 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-layout.ts: -------------------------------------------------------------------------------- 1 | import { DbEntity } from './db-entity'; 2 | import { DbEntityPropertyLayout } from './db-entity-property-layout'; 3 | import { DbEntityProperty } from './db-entity-property'; 4 | import { Point } from './point'; 5 | import { DbEntityLayoutDto } from './dto/db-entity-layout-dto'; 6 | 7 | export class DbEntityLayout { 8 | x: number = null; 9 | y: number = null; 10 | width: number = null; 11 | height: number = null; 12 | zIndex = 0; 13 | 14 | get center(): Point { 15 | return new Point(this.x + this.width / 2, this.y + this.height / 2); 16 | } 17 | 18 | readonly properties: DbEntityPropertyLayout[] = []; 19 | 20 | collapsed = false; 21 | 22 | visible = true; 23 | 24 | /** 25 | * @param entity Decorated entity 26 | * @param key Identifier used in ngFor trackBy 27 | */ 28 | constructor(public readonly entity: DbEntity, public readonly key: number) { 29 | } 30 | 31 | getPropertyLayout(property: DbEntityProperty): DbEntityPropertyLayout { 32 | let result = this.properties.filter(e => e.property.equals(property))[0]; 33 | if (!result) { 34 | result = new DbEntityPropertyLayout(property); 35 | this.properties.push(result); 36 | } 37 | return result; 38 | } 39 | 40 | toDto(): DbEntityLayoutDto { 41 | const result = new DbEntityLayoutDto(); 42 | result.name = this.entity.name; 43 | result.type = this.entity.clrType; 44 | result.x = this.x; 45 | result.y = this.y; 46 | result.collapsed = this.collapsed; 47 | result.visible = this.visible; 48 | return result; 49 | } 50 | 51 | applyLayout(dto: DbEntityLayoutDto) { 52 | this.x = dto.x; 53 | this.y = dto.y; 54 | this.collapsed = dto.collapsed; 55 | this.visible = dto.visible; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-property-layout.ts: -------------------------------------------------------------------------------- 1 | import { DbEntityProperty } from './db-entity-property'; 2 | 3 | export class DbEntityPropertyLayout { 4 | x: number = null; 5 | y: number = null; 6 | width: number = null; 7 | height: number = null; 8 | 9 | constructor(public readonly property: DbEntityProperty) { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-property.ts: -------------------------------------------------------------------------------- 1 | import { ClrType } from './clr-type'; 2 | import { ValueGenerated } from './value-generated'; 3 | 4 | export class DbEntityProperty { 5 | name: string; 6 | clrType: ClrType; 7 | isConcurrencyToken: boolean; 8 | isNullable: boolean; 9 | isReadOnlyAfterSave: boolean; 10 | isReadOnlyBeforeSave: boolean; 11 | isShadowProperty: boolean; 12 | isStoreGeneratedAlways: boolean; 13 | requiresValueGenerator: boolean; 14 | valueGenerated: ValueGenerated; 15 | 16 | static fromJSON(obj: Object): DbEntityProperty { 17 | return Object.assign(new DbEntityProperty(), obj, { 18 | clrType: ClrType.fromJSON(obj['clrType']) 19 | }); 20 | } 21 | 22 | equals(other: DbEntityProperty): boolean { 23 | return other instanceof DbEntityProperty && ( 24 | this === other 25 | || this.name === other.name && this.clrType.equals(other.clrType) 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity-relation-connector.ts: -------------------------------------------------------------------------------- 1 | import { Line } from './line'; 2 | import { Point } from './point'; 3 | 4 | export class DbEntityRelationConnector { 5 | lines: Line[] = []; 6 | externalPoint: Point; 7 | 8 | get direction(): Direction { 9 | return this.lines.some(e => e.left.x < this.externalPoint.x) 10 | ? Direction.LeftToRight 11 | : Direction.RightToLeft; 12 | } 13 | 14 | static fromJSON(value: Object): DbEntityRelationConnector { 15 | return Object.assign( 16 | new DbEntityRelationConnector(), 17 | value, 18 | { 19 | lines: value['lines'].map(e => Line.fromJSON(e)), 20 | externalPoint: Point.fromJSON(value['externalPoint']), 21 | } 22 | ); 23 | } 24 | 25 | // directionOverride: Direction | null = null; 26 | } 27 | 28 | export enum Direction { 29 | LeftToRight, 30 | RightToLeft 31 | } 32 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-entity.ts: -------------------------------------------------------------------------------- 1 | import { ClrType } from './clr-type'; 2 | import { DbEntityProperty } from './db-entity-property'; 3 | import { DbEntityKey } from './db-entity-key'; 4 | import { DbEntityForeignKey } from './db-entity-foreign-key'; 5 | import { DbEntityIndex } from './db-entity-index'; 6 | 7 | export class DbEntity { 8 | name: string; 9 | clrType: ClrType; 10 | properties: DbEntityProperty[] = []; 11 | keys: DbEntityKey[] = []; 12 | foreignKeys: DbEntityForeignKey[] = []; 13 | indexes: DbEntityIndex[] = []; 14 | 15 | get shortName() { return this.name.split('.').reverse()[0]; } 16 | 17 | static fromJSON(obj: Object): DbEntity { 18 | return Object.assign(new DbEntity(), obj, { 19 | clrType: ClrType.fromJSON(obj['clrType']), 20 | properties: (obj['properties'] || []).map(e => DbEntityProperty.fromJSON(e)), 21 | keys: (obj['keys'] || []).map(e => DbEntityKey.fromJSON(e)), 22 | foreignKeys: (obj['foreignKeys'] || []).map(e => DbEntityForeignKey.fromJSON(e)), 23 | indexes: (obj['indexes'] || []).map(e => DbEntityIndex.fromJSON(e)), 24 | }); 25 | } 26 | 27 | equals(other: DbEntity): boolean { 28 | return other instanceof DbEntity && ( 29 | this === other 30 | || this.name === other.name 31 | && this.clrType.equals(other.clrType) 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/db-model.ts: -------------------------------------------------------------------------------- 1 | import { DbEntity } from './db-entity'; 2 | 3 | export class DbModel { 4 | entities: DbEntity[] = []; 5 | 6 | static fromJSON(obj: Object): DbModel { 7 | return Object.assign(new DbModel(), obj, { entities: obj['entities'].map(e => DbEntity.fromJSON(e) ) }); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/dto/db-diagram-dto.ts: -------------------------------------------------------------------------------- 1 | import { DbModelLayoutDto } from './db-model-layout-dto'; 2 | import { DbModel } from '../db-model'; 3 | 4 | export class DbDiagramDto { 5 | 6 | static fromJSON(value: Object): DbDiagramDto { 7 | return Object.assign( 8 | new DbDiagramDto(), 9 | value, 10 | { 11 | model: value['model'] ? DbModel.fromJSON(value['model']) : null, 12 | layout: value['layout'] ? DbModelLayoutDto.fromJSON(value['layout']) : null, 13 | } 14 | ); 15 | } 16 | 17 | constructor(public model: DbModel = null, public layout: DbModelLayoutDto = null) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/dto/db-entity-layout-dto.ts: -------------------------------------------------------------------------------- 1 | import { ClrType } from '../clr-type'; 2 | 3 | export class DbEntityLayoutDto { 4 | x: number; 5 | y: number; 6 | name: string; 7 | type: ClrType; 8 | collapsed: boolean; 9 | visible: boolean; 10 | 11 | static fromJSON(value: Object): DbEntityLayoutDto { 12 | return Object.assign( 13 | new DbEntityLayoutDto(), 14 | value, 15 | { 16 | type: ClrType.fromJSON(value['type']) 17 | } 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/dto/db-entity-relation-layout-dto.ts: -------------------------------------------------------------------------------- 1 | import { ClrType } from '../clr-type'; 2 | import { DbEntityRelationConnector } from '../db-entity-relation-connector'; 3 | import { Line } from '../line'; 4 | 5 | export class DbEntityRelationLayoutDto { 6 | principalEntityName: string; 7 | principalEntityType: ClrType; 8 | 9 | dependentEntityName: string; 10 | dependentEntityType: ClrType; 11 | 12 | principalConnector: DbEntityRelationConnector; 13 | dependentConnector: DbEntityRelationConnector; 14 | 15 | collapsedPrincipalConnector: DbEntityRelationConnector; 16 | collapsedDependentConnector: DbEntityRelationConnector; 17 | 18 | fullPath: Line[] = []; 19 | draggableLines: Line[] = []; 20 | 21 | static fromJSON(value: Object): DbEntityRelationLayoutDto { 22 | return Object.assign( 23 | new DbEntityRelationLayoutDto(), 24 | value, 25 | { 26 | principalEntityType: ClrType.fromJSON(value['principalEntityType']), 27 | dependentEntityType: ClrType.fromJSON(value['dependentEntityType']), 28 | principalConnector: DbEntityRelationConnector.fromJSON(value['principalConnector']), 29 | dependentConnector: DbEntityRelationConnector.fromJSON(value['dependentConnector']), 30 | collapsedPrincipalConnector: value['collapsedPrincipalConnector'] 31 | ? DbEntityRelationConnector.fromJSON(value['collapsedPrincipalConnector']) 32 | : value['collapsedPrincipalConnector'], 33 | collapsedDependentConnector: value['collapsedDependentConnector'] 34 | ? DbEntityRelationConnector.fromJSON(value['collapsedDependentConnector']) 35 | : value['collapsedDependentConnector'], 36 | fullPath: value['fullPath'].map(e => Line.fromJSON(e)), 37 | draggableLines: value['draggableLines'].map(e => Line.fromJSON(e)), 38 | } 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/dto/db-model-layout-dto.ts: -------------------------------------------------------------------------------- 1 | import { DbEntityLayoutDto } from './db-entity-layout-dto'; 2 | import { DbEntityRelationLayoutDto } from './db-entity-relation-layout-dto'; 3 | import { TableSettings } from '../table-settings'; 4 | 5 | export class DbModelLayoutDto { 6 | entities: DbEntityLayoutDto[] = []; 7 | relations: DbEntityRelationLayoutDto[] = []; 8 | entitiesTableSettings: TableSettings; 9 | showMinimap: boolean; 10 | 11 | static fromJSON(value: Object): DbModelLayoutDto { 12 | return Object.assign( 13 | new DbModelLayoutDto(), 14 | value, 15 | { 16 | entities: value['entities'].map(e => DbEntityLayoutDto.fromJSON(e)), 17 | relations: value['relations'].map(e => DbEntityRelationLayoutDto.fromJSON(e)), 18 | entitiesTableSettings: value['entitiesTableSettings'] 19 | ? TableSettings.fromJSON(value['entitiesTableSettings']) 20 | : value['entitiesTableSettings'] 21 | } 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/line.ts: -------------------------------------------------------------------------------- 1 | import { Point } from './point'; 2 | 3 | export class Line { 4 | width = 4; 5 | 6 | static fromJSON(value: Object): Line { 7 | return Object.assign( 8 | new Line(null, null), 9 | value, 10 | { 11 | p1: Point.fromJSON(value['p1']), 12 | p2: Point.fromJSON(value['p2']) 13 | } 14 | ); 15 | } 16 | 17 | constructor(public p1: Point, public p2: Point) { 18 | } 19 | 20 | get center(): Point { 21 | return new Point( 22 | Math.min(this.p1.x, this.p2.x) + Math.abs(this.p2.x - this.p1.x) / 2, 23 | Math.min(this.p1.y, this.p2.y) + Math.abs(this.p2.y - this.p1.y) / 2 24 | ); 25 | } 26 | 27 | get length(): number { 28 | return Math.sqrt( 29 | Math.pow(this.p1.x - this.p2.x, 2) + Math.pow(this.p1.y - this.p2.y, 2) 30 | ); 31 | } 32 | 33 | get dx() { return Math.abs(this.p1.x - this.p2.x); } 34 | 35 | get dy() { return Math.abs(this.p1.y - this.p2.y); } 36 | 37 | get top(): Point { 38 | return this.p1.y < this.p2.y ? this.p1 : this.p2; 39 | } 40 | 41 | get right(): Point { 42 | return this.p1.x < this.p2.x ? this.p2 : this.p1; 43 | } 44 | 45 | get bottom(): Point { 46 | return this.p1.y < this.p2.y ? this.p2 : this.p1; 47 | } 48 | 49 | get left(): Point { 50 | return this.p1.x < this.p2.x ? this.p1 : this.p2; 51 | } 52 | 53 | get isHorizontal() { return this.p1.y === this.p2.y; } 54 | 55 | get isVertical() { return this.p1.x === this.p2.x; } 56 | } 57 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/point.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Point { 3 | 4 | static fromJSON(value: Object): Point { 5 | return Object.assign( 6 | new Point(null, null), 7 | value 8 | ); 9 | } 10 | 11 | constructor(public x: number, public y: number) { 12 | } 13 | 14 | equals(other: Point): boolean { 15 | return !!other && this.x === other.x && this.y === other.y; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/table-settings.ts: -------------------------------------------------------------------------------- 1 | import { ColumnSettings } from './column-settings'; 2 | 3 | export class TableSettings { 4 | 5 | static fromJSON(value: Object): TableSettings { 6 | return Object.assign( 7 | new TableSettings(), 8 | value, 9 | { 10 | columns: value['columns'].map(e => ColumnSettings.fromJSON(e)) 11 | } 12 | ); 13 | } 14 | 15 | constructor( 16 | public readonly columns: ColumnSettings[] = [] 17 | ) { 18 | } 19 | 20 | getColumn(key: string) { 21 | const result = this.columns.filter(e => e.key === key)[0]; 22 | if (!result) { 23 | throw new Error('Current TableSettings instance does not contain ColumnSettings for key ' + key); 24 | } 25 | return result; 26 | } 27 | 28 | get displayedColumnsKeys() { 29 | return this.columns.filter(e => e.visible).map(e => e.key); 30 | } 31 | 32 | canMoveColumnUp(column: ColumnSettings) { 33 | this.assertForeignColumn(column); 34 | const index = this.columns.indexOf(column); 35 | return index > 0; 36 | } 37 | 38 | moveColumnUp(column: ColumnSettings) { 39 | if (!this.canMoveColumnUp(column)) { 40 | throw new Error('Cannot move column up. It is already at the top.'); 41 | } 42 | const index = this.columns.indexOf(column); 43 | this.columns.splice(index, 1); 44 | this.columns.splice(index - 1, 0, column); 45 | } 46 | 47 | canMoveColumnDown(column: ColumnSettings) { 48 | this.assertForeignColumn(column); 49 | const index = this.columns.indexOf(column); 50 | return index < this.columns.length - 1; 51 | } 52 | 53 | moveColumnDown(column: ColumnSettings) { 54 | if (!this.canMoveColumnDown(column)) { 55 | throw new Error('Cannot move column down. It is already at the bottom.'); 56 | } 57 | const index = this.columns.indexOf(column); 58 | this.columns.splice(index, 1); 59 | this.columns.splice(index + 1, 0, column); 60 | } 61 | 62 | private assertForeignColumn(column: ColumnSettings) { 63 | if (this.columns.indexOf(column) === -1) { 64 | throw new Error('Specified column does not belong to current TableSettings instance'); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/models/value-generated.ts: -------------------------------------------------------------------------------- 1 | 2 | export enum ValueGenerated { 3 | Never = 0, 4 | OnAdd = 1, 5 | OnAddOrUpdate = 2 6 | } 7 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/services/api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { ApiService } from './api.service'; 4 | 5 | describe('ApiService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [ApiService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([ApiService], (service: ApiService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/services/api.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Http } from '@angular/http'; 3 | import { Observable } from 'rxjs/Observable'; 4 | 5 | import { DbModel } from '../models/db-model'; 6 | import { environment } from '../../environments/environment'; 7 | 8 | @Injectable() 9 | export class ApiService { 10 | 11 | constructor(private readonly _http: Http) { 12 | 13 | } 14 | 15 | queryDbModel(): Observable { 16 | return environment.production 17 | ? this._http.get('/db-diagrams/model').map(r => DbModel.fromJSON(r.json())) 18 | : Observable.timer(4).map(() => DbModel.fromJSON(environment.dbModel)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/app/services/diagram-layout.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, inject } from '@angular/core/testing'; 2 | 3 | import { DiagramLayoutService } from './diagram-layout.service'; 4 | 5 | describe('DiagramLayoutService', () => { 6 | beforeEach(() => { 7 | TestBed.configureTestingModule({ 8 | providers: [DiagramLayoutService] 9 | }); 10 | }); 11 | 12 | it('should be created', inject([DiagramLayoutService], (service: DiagramLayoutService) => { 13 | expect(service).toBeTruthy(); 14 | })); 15 | }); 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | import { versions } from './versions'; 2 | 3 | export const environment = { 4 | production: true, 5 | dbModel: null, 6 | versions: versions 7 | }; 8 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | import { dbModel } from './test-data'; 7 | import { versions } from './versions'; 8 | 9 | export const environment = { 10 | production: false, 11 | dbModel: dbModel, 12 | versions: versions 13 | }; 14 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/environments/versions.ts: -------------------------------------------------------------------------------- 1 | // this file is automatically generated by git-version.ts script 2 | export const versions = { 3 | version: '0.0.0', 4 | revision: 'e0a5dd9', 5 | branch: 'feature/40_mobile', 6 | repository: 'https://github.com/EvAlex/ef-db-diagrams/' 7 | }; 8 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/favicon.ico -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DbDiagrams 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { DbDiagramsAppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(DbDiagramsAppModule); 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** Evergreen browsers require these. **/ 41 | import 'core-js/es6/reflect'; 42 | import 'core-js/es7/reflect'; 43 | 44 | 45 | /** 46 | * Required to support Web Animations `@angular/animation`. 47 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 48 | **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | /** 70 | * Need to import at least one locale-data with intl. 71 | */ 72 | // import 'intl/locale-data/jsonp/en'; 73 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | @import 'theme'; 4 | 5 | html { 6 | font-family: Roboto; 7 | 8 | body { 9 | margin: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare const __karma__: any; 17 | declare const require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2016", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.Frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "defaultSeverity": "warning", 6 | "rules": { 7 | "arrow-return-shorthand": true, 8 | "callable-types": true, 9 | "class-name": true, 10 | "comment-format": [ 11 | true, 12 | "check-space" 13 | ], 14 | "curly": true, 15 | "eofline": true, 16 | "forin": true, 17 | "import-blacklist": [ 18 | true, 19 | "rxjs" 20 | ], 21 | "import-spacing": true, 22 | "indent": [ 23 | true, 24 | "spaces" 25 | ], 26 | "interface-over-type-literal": true, 27 | "label-position": true, 28 | "max-line-length": [ 29 | true, 30 | 140 31 | ], 32 | "member-access": false, 33 | "member-ordering": [ 34 | true, 35 | { 36 | "order": [ 37 | "static-field", 38 | "instance-field", 39 | "static-method", 40 | "instance-method" 41 | ] 42 | } 43 | ], 44 | "no-arg": true, 45 | "no-bitwise": true, 46 | "no-console": [ 47 | true, 48 | "debug", 49 | "info", 50 | "time", 51 | "timeEnd", 52 | "trace" 53 | ], 54 | "no-construct": true, 55 | "no-debugger": true, 56 | "no-duplicate-super": true, 57 | "no-empty": false, 58 | "no-empty-interface": true, 59 | "no-eval": true, 60 | "no-inferrable-types": [ 61 | true, 62 | "ignore-params" 63 | ], 64 | "no-misused-new": true, 65 | "no-non-null-assertion": true, 66 | "no-shadowed-variable": true, 67 | "no-string-literal": false, 68 | "no-string-throw": true, 69 | "no-switch-case-fall-through": true, 70 | "no-trailing-whitespace": true, 71 | "no-unnecessary-initializer": true, 72 | "no-unused-expression": true, 73 | "no-use-before-declare": true, 74 | "no-var-keyword": true, 75 | "object-literal-sort-keys": false, 76 | "one-line": [ 77 | true, 78 | "check-open-brace", 79 | "check-catch", 80 | "check-else", 81 | "check-whitespace" 82 | ], 83 | "prefer-const": true, 84 | "quotemark": [ 85 | true, 86 | "single" 87 | ], 88 | "radix": true, 89 | "semicolon": [ 90 | true, 91 | "always" 92 | ], 93 | "triple-equals": [ 94 | true, 95 | "allow-null-check" 96 | ], 97 | "typedef-whitespace": [ 98 | true, 99 | { 100 | "call-signature": "nospace", 101 | "index-signature": "nospace", 102 | "parameter": "nospace", 103 | "property-declaration": "nospace", 104 | "variable-declaration": "nospace" 105 | } 106 | ], 107 | "typeof-compare": true, 108 | "unified-signatures": true, 109 | "variable-name": false, 110 | "whitespace": [ 111 | true, 112 | "check-branch", 113 | "check-decl", 114 | "check-operator", 115 | "check-separator", 116 | "check-type" 117 | ], 118 | "directive-selector": [ 119 | true, 120 | "attribute", 121 | "efd", 122 | "camelCase" 123 | ], 124 | "component-selector": [ 125 | true, 126 | "element", 127 | "efd", 128 | "kebab-case" 129 | ], 130 | "use-input-property-decorator": true, 131 | "use-output-property-decorator": true, 132 | "use-host-property-decorator": true, 133 | "no-input-rename": true, 134 | "no-output-rename": true, 135 | "use-life-cycle-interface": true, 136 | "use-pipe-transform-interface": true, 137 | "component-class-suffix": true, 138 | "directive-class-suffix": true, 139 | "no-access-missing-member": true, 140 | "templates-use-public": true, 141 | "invoke-injectable": true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26430.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityFrameworkCore.Diagrams", "EntityFrameworkCore.Diagrams\EntityFrameworkCore.Diagrams.csproj", "{1EE8EEEE-EBD0-4A6C-8B75-D8814D0AF0C6}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFrameworkCore.Diagrams.Demo", "EntityFrameworkCore.Diagrams.Demo\EntityFrameworkCore.Diagrams.Demo.csproj", "{10F13BC4-60A8-41AE-88E4-3B47FF60B805}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1EE8EEEE-EBD0-4A6C-8B75-D8814D0AF0C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {1EE8EEEE-EBD0-4A6C-8B75-D8814D0AF0C6}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {1EE8EEEE-EBD0-4A6C-8B75-D8814D0AF0C6}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {1EE8EEEE-EBD0-4A6C-8B75-D8814D0AF0C6}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {10F13BC4-60A8-41AE-88E4-3B47FF60B805}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {10F13BC4-60A8-41AE-88E4-3B47FF60B805}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {10F13BC4-60A8-41AE-88E4-3B47FF60B805}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {10F13BC4-60A8-41AE-88E4-3B47FF60B805}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/ClrType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Dto 7 | { 8 | public class ClrType : IEquatable, IEquatable 9 | { 10 | public string Namespace { get; set; } 11 | 12 | public string Name { get; set; } 13 | 14 | public string Assembly { get; internal set; } 15 | 16 | public IEnumerable GenericTypeArguments { get; set; } 17 | 18 | public bool Equals(ClrType other) 19 | { 20 | return other is ClrType 21 | && Name == other.Name 22 | && Namespace == other.Name 23 | && Assembly == other.Assembly 24 | && GenericTypeArguments.Count() == other.GenericTypeArguments.Count() 25 | && GenericTypeArguments.All(e => other.GenericTypeArguments.Any(ee => e.Equals(ee))); 26 | } 27 | 28 | public bool Equals(Type other) 29 | { 30 | return other is Type 31 | && Name == other.Name 32 | && Namespace == other.Name 33 | && Assembly == other.AssemblyQualifiedName 34 | && GenericTypeArguments.Count() == other.GenericTypeArguments.Length 35 | && GenericTypeArguments.All(e => other.GenericTypeArguments.Any(ee => e.Equals(ee))); 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return $"{Namespace}.{Name}<{GenericTypeArguments.Select(e => e.ToString())}> [{Assembly}]"; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class DbEntity : DbEntityBase 6 | { 7 | public ClrType ClrType { get; set; } 8 | 9 | public IEnumerable Properties { get; set; } 10 | 11 | public IEnumerable Keys { get; set; } 12 | 13 | public IEnumerable ForeignKeys { get; set; } 14 | 15 | public IEnumerable Indexes { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityBase.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFrameworkCore.Diagrams.Dto 2 | { 3 | public abstract class DbEntityBase 4 | { 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityForeignKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | 4 | namespace EntityFrameworkCore.Diagrams.Dto 5 | { 6 | public class DbEntityForeignKey : DbEntityForeignKeyBase 7 | { 8 | public DbEntity PrincipalEntity { get; internal set; } 9 | 10 | public DbEntityKey PrincipalKey { get; internal set; } 11 | 12 | public IEnumerable Properties { get; internal set; } 13 | } 14 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityForeignKeyBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public abstract class DbEntityForeignKeyBase 6 | { 7 | public DeleteBehavior DeleteBehavior { get; internal set; } 8 | public bool IsRequired { get; internal set; } 9 | public bool IsUnique { get; internal set; } 10 | } 11 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityIndex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class DbEntityIndex : DbEntityIndexBase 6 | { 7 | public IEnumerable Properties { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityIndexBase.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFrameworkCore.Diagrams.Dto 2 | { 3 | public abstract class DbEntityIndexBase 4 | { 5 | public bool IsUnique { get; internal set; } 6 | } 7 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EntityFrameworkCore.Diagrams.Dto 6 | { 7 | public class DbEntityKey 8 | { 9 | public IEnumerable Properties { get; internal set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class DbEntityProperty : DbEntityPropertyBase 6 | { 7 | public ClrType ClrType { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbEntityPropertyBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace EntityFrameworkCore.Diagrams.Dto 7 | { 8 | public abstract class DbEntityPropertyBase 9 | { 10 | public string Name { get; set; } 11 | 12 | public bool IsConcurrencyToken { get; internal set; } 13 | 14 | public bool IsNullable { get; internal set; } 15 | 16 | public bool IsReadOnlyAfterSave { get; internal set; } 17 | 18 | public bool IsReadOnlyBeforeSave { get; internal set; } 19 | 20 | public bool IsShadowProperty { get; internal set; } 21 | 22 | public bool IsStoreGeneratedAlways { get; internal set; } 23 | 24 | public bool RequiresValueGenerator { get; internal set; } 25 | 26 | public ValueGenerated ValueGenerated { get; internal set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DbModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EntityFrameworkCore.Diagrams.Dto 6 | { 7 | public class DbModel 8 | { 9 | public IEnumerable Entities { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DistinctClrTypesComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace EntityFrameworkCore.Diagrams.Dto 6 | { 7 | public class DistinctClrTypesComparer : IEqualityComparer 8 | { 9 | public bool Equals(ClrType x, ClrType y) 10 | { 11 | return x is ClrType && y is ClrType && x.Equals(y); 12 | } 13 | 14 | public int GetHashCode(ClrType obj) 15 | { 16 | return obj.ToString().GetHashCode(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/DistinctEntitiesComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace EntityFrameworkCore.Diagrams.Dto 5 | { 6 | internal class DistinctEntitiesComparer : IEqualityComparer 7 | { 8 | private readonly DistinctClrTypesComparer _clrTypesComparer = new DistinctClrTypesComparer(); 9 | 10 | public bool Equals(DbEntity x, DbEntity y) 11 | { 12 | return x is DbEntity && y is DbEntity 13 | && x.Name == y.Name 14 | && _clrTypesComparer.Equals(x.ClrType, y.ClrType); 15 | } 16 | 17 | public int GetHashCode(DbEntity obj) 18 | { 19 | return $"{obj.Name} ({obj.ClrType})".GetHashCode(); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedClrType.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFrameworkCore.Diagrams.Dto 2 | { 3 | public class NormalizedClrType : ClrType 4 | { 5 | public int Id { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbEntity.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class NormalizedDbEntity : DbEntityBase 6 | { 7 | public int Id { get; set; } 8 | 9 | public int ClrTypeId { get; set; } 10 | 11 | public IEnumerable Properties { get; set; } 12 | 13 | public IEnumerable Keys { get; set; } 14 | 15 | public IEnumerable ForeignKeys { get; set; } 16 | 17 | public IEnumerable Indexes { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbEntityForeignKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class NormalizedDbEntityForeignKey : DbEntityForeignKeyBase 6 | { 7 | public int PrincipalEntityId { get; internal set; } 8 | 9 | public NormalizedDbEntityKey PrincipalKey { get; internal set; } 10 | 11 | public IEnumerable Properties { get; internal set; } 12 | } 13 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbEntityIndex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class NormalizedDbEntityIndex : DbEntityIndexBase 6 | { 7 | public IEnumerable Properties { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbEntityKey.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class NormalizedDbEntityKey 6 | { 7 | public IEnumerable Properties { get; internal set; } 8 | } 9 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbEntityProperty.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Metadata; 2 | 3 | namespace EntityFrameworkCore.Diagrams.Dto 4 | { 5 | public class NormalizedDbEntityProperty : DbEntityPropertyBase 6 | { 7 | public int ClrTypeId { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/Dto/NormalizedDbModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace EntityFrameworkCore.Diagrams.Dto 6 | { 7 | public class NormalizedDbModel 8 | { 9 | public IEnumerable EntitiesIds { get; set; } 10 | 11 | public IEnumerable AllEntities { get; set; } 12 | 13 | public IEnumerable AllClrTypes{ get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/EfDiagramsOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.FileProviders; 3 | using System; 4 | using System.IO; 5 | using System.Reflection; 6 | 7 | namespace Microsoft.Extensions.DependencyInjection 8 | { 9 | /// 10 | /// Options for . 11 | /// 12 | public class EfDiagramsOptions 13 | { 14 | /// 15 | /// Gets or sets of that 16 | /// will be used to extract data model for diagram. Only one context type is currently supported. 17 | /// This will change as soon as frontend app supports it. 18 | /// 19 | public Type DbContextType { get; set; } 20 | 21 | /// 22 | /// Gets or sets base path for all requests within current library 23 | /// 24 | public PathString RequestPath { get; set; } = new PathString("/db-diagrams"); 25 | 26 | public IFileProvider FrontendAppFilesProvider { get; set; } = new PhysicalFileProvider(GetEfDiagramsContentRoot()); 27 | 28 | internal static string GetEfDiagramsContentRoot() 29 | { 30 | var asm = typeof(EfDiagramsMiddleware).GetTypeInfo().Assembly; 31 | var dllPath = Path.GetDirectoryName(asm.Location); 32 | var nupkgRoot = Path.Combine(dllPath, "..", ".."); 33 | var contentRoot = Path.Combine(nupkgRoot, "content"); 34 | if (!Directory.Exists(contentRoot)) 35 | { 36 | // NOTE: this means that we are not installed as NuGet packange 37 | contentRoot = Path.Combine(dllPath, "..", "..", "..", "..", "EntityFrameworkCore.Diagrams"); 38 | } 39 | contentRoot = Path.Combine(contentRoot, "wwwroot", "db-diagrams"); 40 | return contentRoot; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/EfDiagramsServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.StaticFiles.Infrastructure; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.Extensions.FileProviders; 6 | using System; 7 | using System.IO; 8 | 9 | namespace Microsoft.Extensions.DependencyInjection 10 | { 11 | public static class EfDiagramsServiceCollectionExtensions 12 | { 13 | /// 14 | /// Adds with default 15 | /// to application's request pipeline. 16 | /// 17 | /// 18 | /// 19 | /// 20 | public static IApplicationBuilder AddEfDiagrams(this IApplicationBuilder app) 21 | where TDbContext: DbContext 22 | { 23 | var options = new EfDiagramsOptions { DbContextType = typeof(TDbContext) }; 24 | 25 | return app.AddEfDiagrams(options); 26 | } 27 | 28 | /// 29 | /// Adds with specified 30 | /// to application's request pipeline. 31 | /// 32 | /// 33 | /// 34 | /// 35 | public static IApplicationBuilder AddEfDiagrams(this IApplicationBuilder app, EfDiagramsOptions options) 36 | { 37 | var sharedOptions = new SharedOptions { FileProvider = options.FrontendAppFilesProvider, RequestPath = options.RequestPath }; 38 | app.UseDefaultFiles(new DefaultFilesOptions(sharedOptions) { DefaultFileNames = { "index.html" } }); 39 | app = app.UseStaticFiles(new StaticFileOptions(sharedOptions)); 40 | 41 | app = app.UseMiddleware(Options.Options.Create(options)); 42 | 43 | return app; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams/EntityFrameworkCore.Diagrams.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 0.4.2 5 | 6 | Visualize model created with EntityFramework Core in ASP.NET Core app. 7 | 8 | https://db-diagrams.firebaseapp.com/ 9 | 10 | 1. Install the NuGet package 11 | 2. Use AddEfDiagrams() extension method in Configure() method of your Startup class to add middleware. Specify your DbContext type instead of ApplicationDbContext in the following example: app.AddEfDiagrams<ApplicationDbContext>(); 12 | 3. Start your app and browse to /db-diagrams page. You should see the diagram now. 13 | 14 | Use the middleware is only in Development mode. This is important! Otherwise, any user in Production will se your model structure, which may not be desireable. This is not the case if you are developing public API, though. 15 | 16 | Feel free to report any bugs or feature requests - just create an issue. Contributions are appreciated! 17 | https://github.com/EvAlex/ef-db-diagrams 18 | 19 | https://github.com/EvAlex/ef-db-diagrams/raw/master/icon.png 20 | Alexander Pavlyuk (EvAlex) 21 | https://db-diagrams.firebaseapp.com/ 22 | aspnetcore;entityframeworkcore;ef;diagram;er; 23 | https://github.com/EvAlex/ef-db-diagrams 24 | git 25 | Copyright © Alexander Pavlyuk 2017 26 | https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/master/LICENSE.TXT 27 | 28 | - fix: misplaced relation connectors 29 | 30 | 31 | 32 | 33 | netstandard1.6 34 | True 35 | 0.4.2.0 36 | 0.4.2.0 37 | 38 | 39 | 40 | bin\Release\netstandard1.6\EntityFrameworkCore.Diagrams.xml 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | PreserveNewest 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) .NET Foundation and Contributors 4 | 5 | All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EntityFrameworkCore.Diargams 2 | 3 | Visualize model created with EntityFramework Core in ASP.NET Core app. 4 | 5 | https://db-diagrams.firebaseapp.com/ 6 | 7 | ## Installation 8 | 9 | 1. Install NuGet package `EntityFrameworkCore.Diagrams` 10 | 2. Use `AddEfDiagrams()` extension method in `Configure()` method of your Startup class to add middleware. Specify your DbContext type instead of ApplicationDbContext in the following example: `app.AddEfDiagrams();` 11 | Here's how your Configure() method might look like after this step (notice commented line): 12 | ```cs 13 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 14 | { 15 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 16 | loggerFactory.AddDebug(); 17 | 18 | if (env.IsDevelopment()) 19 | { 20 | app.UseDeveloperExceptionPage(); 21 | app.UseBrowserLink(); 22 | app.AddEfDiagrams(); // <-- Here's the config for EntityFrameworkCore.Diargams 23 | } 24 | else 25 | { 26 | app.UseExceptionHandler("/Home/Error"); 27 | } 28 | 29 | app.UseStaticFiles(); 30 | 31 | app.UseMvc(routes => 32 | { 33 | routes.MapRoute( 34 | name: "default", 35 | template: "{controller=Home}/{action=Index}/{id?}"); 36 | }); 37 | } 38 | ``` 39 | 3. Start your app and browse to `/db-diagrams page`. You should see the diagram now. 40 | 41 | Notice that the middleware is added only in Development mode. This is important! Otherwise, any user in Production will se your model structure, which may not be desireable. This is not the case if you are developing public API, though. 42 | 43 | 44 | ## Contributing 45 | Feel free to report any bugs or feature requests - just create an issue. Contributions are appreciated! 46 | 47 | ## License 48 | MIT -------------------------------------------------------------------------------- /icon.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/icon.pdn -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvAlex/ef-db-diagrams/36d4858bccbd9040e0b0290a2d188b2ba6a755d0/icon.png --------------------------------------------------------------------------------