Skip to content

Commit

Permalink
Implementation of AspNetCore Identity. Works if you feed it proper cl…
Browse files Browse the repository at this point in the history
…asses (drag/drop). See issue #53
  • Loading branch information
ensemblebd committed Mar 5, 2019
1 parent 14b0382 commit d49079e
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 15 deletions.
20 changes: 19 additions & 1 deletion src/DslPackage/TextTemplates/EFCoreDesigner.ttinclude
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ void WriteDbContextEFCore(ModelRoot modelRoot)
Output("using System.Linq;");
Output("using System.ComponentModel.DataAnnotations.Schema;");
Output("using Microsoft.EntityFrameworkCore;");
if (IsAspNetCoreIdentity()) {
Output("using Microsoft.AspNetCore.Identity.EntityFrameworkCore;");
}
NL();

BeginNamespace(modelRoot.Namespace);
Expand All @@ -130,7 +133,9 @@ void WriteDbContextEFCore(ModelRoot modelRoot)
Output("/// <inheritdoc/>");
}

Output($"{modelRoot.EntityContainerAccess.ToString().ToLower()} partial class {modelRoot.EntityContainerName} : Microsoft.EntityFrameworkCore.DbContext");
String inherit_from = (IsAspNetCoreIdentity()?$"IdentityDbContext<{IdentityUserImpl},{IdentityRoleImpl},{IdentityTKey}>":"Microsoft.EntityFrameworkCore.DbContext");

Output($"{modelRoot.EntityContainerAccess.ToString().ToLower()} partial class {modelRoot.EntityContainerName} : {inherit_from}");
Output("{");

PluralizationService pluralizationService = ModelRoot.PluralizationService;
Expand Down Expand Up @@ -162,6 +167,9 @@ void WriteDbContextEFCore(ModelRoot modelRoot)
{
string dbSetName;

// don't output dbsets for already-included sets via base class of Identity.
if (isIdentityModel(modelClass.Name) || isIdentityBase(modelClass.Name)) continue;

if (!string.IsNullOrEmpty(modelClass.DbSetName))
dbSetName = modelClass.DbSetName;
else
Expand Down Expand Up @@ -208,11 +216,15 @@ void WriteDbContextEFCore(ModelRoot modelRoot)
Output("/// <inheritdoc />");
Output($"public {modelRoot.EntityContainerName}(DbContextOptions<{modelRoot.EntityContainerName}> options) : base(options)");
Output("{");
Output("CustomCreate(options);");
Output("}");
NL();

Output("partial void CustomInit(DbContextOptionsBuilder optionsBuilder);");
NL();
// added to support Owin context creation.. Cause err body loves Owen Wilson? :o
Output($"partial void CustomCreate(DbContextOptions<{modelRoot.EntityContainerName}> options);");
NL();

Output("/// <inheritdoc />");
Output($"protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)");
Expand Down Expand Up @@ -267,6 +279,11 @@ void WriteDbContextEFCore(ModelRoot modelRoot)

foreach (ModelClass modelClass in modelRoot.Classes.OrderBy(x => x.Name))
{
// don't output ef6 correlations for already-included items via base class of Identity.
if (isIdentityBase(modelClass.Name)) continue;
// also respect developer wishes to override generator and use custom correlations of their design.
if (shouldDisableModelImpl(modelClass.Name)) continue;

segments.Clear();
foreignKeyColumns.Clear();
NL();
Expand Down Expand Up @@ -305,6 +322,7 @@ void WriteDbContextEFCore(ModelRoot modelRoot)
// indexed properties
foreach (ModelAttribute indexed in modelClass.Attributes.Where(x => x.Indexed && !x.IsIdentity))
{

segments.Clear();
segments.Add($"modelBuilder.Entity<{modelClass.FullName}>().HasIndex(t => t.{indexed.Name})");
if (indexed.IndexedUnique) segments.Add("IsUnique()");
Expand Down
106 changes: 92 additions & 14 deletions src/DslPackage/TextTemplates/EFDesigner.ttinclude
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,66 @@
// Copyright (c) 2017-2019 Michael Sawczyn
// https://github.com/msawczyn/EFDesigner


#region Todo_Expose_As_MEF_Attributes_In_Designer

List<String> IdentityBaseTypes = new List<String>(){
"IdentityRole",
"IdentityRoleClaim",
"IdentityUser",
"IdentityUserClaim",
"IdentityUserLogin",
"IdentityUserRole",
"IdentityUserToken"
};
List<String> IdentityBaseTypesWithoutPrimary = new List<String>(){
"IdentityUserLogin",
"IdentityUserRole",
"IdentityUserToken"
};
List<String> IdentitySubTypes = new List<String>(){
"ApplicationRole",
"ApplicationRoleClaim",
"ApplicationUser",
"ApplicationUserClaim",
"ApplicationUserLogin",
"ApplicationUserRole",
"ApplicationUserToken"
};
List<String> IdentitySubTypesDisableModelImpl = new List<String>(){
"ApplicationDepartment",
"ApplicationGroup",
"ApplicationGroupDepartment",
"ApplicationGroupRole",
"ApplicationUserGroup"
};

bool isIdentityBase(String name) { // todo: ModelClass.IsIdentity (exposed property)
return IdentityBaseTypes.Any(x=> x.EndsWith(name, StringComparison.OrdinalIgnoreCase));
}
bool isIdentityBaseWithoutPK(String name) { // todo: ModelClass.NoPrimaryKey (exposed property)
return IdentityBaseTypesWithoutPrimary.Any(x=> x.EndsWith(name, StringComparison.OrdinalIgnoreCase));
}
bool isIdentityModel(String name) { // todo: ModelClass.IsIdentitySubClass (exposed property)
return IdentitySubTypes.Any(x=> x.EndsWith(name, StringComparison.OrdinalIgnoreCase));
}
bool shouldDisableModelImpl(String name) { // todo: ModelClass.DisableModelImpl (exposed property)
return IdentitySubTypesDisableModelImpl.Any(x=> x.EndsWith(name, StringComparison.OrdinalIgnoreCase));
}
bool shouldForceDefaultConstructorToPublic(String name) { // todo: ModelClass.DefaultConstructorIsPublic (exposed property)
return IdentitySubTypesDisableModelImpl.Any(x=> x.EndsWith(name, StringComparison.OrdinalIgnoreCase));
}
bool IsAspNetCoreIdentity() {return true;} // todo: ModelRoot.IsNetCoreIdentity (exposed property)

String IdentityUserImpl = "ApplicationUser";// todo: ModelRoot.IdentityUserClass (exposed property)
String IdentityRoleImpl = "ApplicationRole";// todo: ModelRoot.IdentityRoleClass (exposed property)
String IdentityTKey = "int";// todo: ModelRoot.IdentityPreferredKeyType (exposed property)

#endregion




void NL()
{
WriteLine(string.Empty);
Expand Down Expand Up @@ -251,6 +311,10 @@ void WriteEnum(ModelEnum modelEnum)

void WriteClass(ModelClass modelClass)
{
bool b_isIdentityBase=isIdentityBase(modelClass.Name);
bool b_isIdentityModel=isIdentityModel(modelClass.Name);
bool b_desiresPublicAccess = shouldForceDefaultConstructorToPublic(modelClass.Name);

Output("using System;");
Output("using System.Collections.Generic;");
Output("using System.Collections.ObjectModel;");
Expand All @@ -259,6 +323,9 @@ void WriteClass(ModelClass modelClass)
Output("using System.ComponentModel.DataAnnotations.Schema;");
Output("using System.Linq;");
Output("using System.Runtime.CompilerServices;");
if (b_isIdentityBase) {
Output("using Microsoft.AspNetCore.Identity;"); // required for drag/drop of Identity class derivations.
}
List<string> additionalUsings = GetAdditionalUsingStatementsEF6(modelClass.ModelRoot);
if (additionalUsings.Any())
Output(string.Join("\n", additionalUsings));
Expand Down Expand Up @@ -296,12 +363,16 @@ void WriteClass(ModelClass modelClass)
Output($"public {isAbstract}partial class {modelClass.Name}{baseClass}");
Output("{");

WriteConstructor(modelClass);
WritePersistentProperties(modelClass);
WriteCalculatedProperties(modelClass);
WritePersistentNavigationProperties(modelClass, true);
WriteCalculatedNavigationProperties(modelClass);
WriteNotifyPropertyChanged(modelClass);
if (!b_isIdentityBase) {
WriteConstructor(modelClass, b_isIdentityModel, b_desiresPublicAccess);
}
WritePersistentProperties(modelClass, b_isIdentityBase);
if (!b_isIdentityBase) {
WriteCalculatedProperties(modelClass);
WritePersistentNavigationProperties(modelClass, true);
WriteCalculatedNavigationProperties(modelClass);
WriteNotifyPropertyChanged(modelClass);
}

Output("}");

Expand Down Expand Up @@ -384,7 +455,7 @@ void WriteDefaultConstructorBody(ModelClass modelClass)
Output("Init();");
}

void WriteConstructor(ModelClass modelClass)
void WriteConstructor(ModelClass modelClass, bool b_isIdentityModel, bool b_desiresPublicAccess)
{
Output("partial void Init();");
NL();
Expand All @@ -395,7 +466,9 @@ void WriteConstructor(ModelClass modelClass)

bool hasRequiredParameters = GetRequiredParameters(modelClass, false).Any();
string visibility = hasRequiredParameters || modelClass.IsAbstract ? "protected" : "public";

// Error CS0310- <T> must be a non-abstract type with a public parameterless constructor in order to use it as parameter in the generic type or method
if (b_isIdentityModel || b_desiresPublicAccess) visibility = "public";

if (visibility == "public")
{
Output("/// <summary>");
Expand Down Expand Up @@ -562,7 +635,7 @@ string FullyQualified(ModelRoot modelRoot, string typeName)
return modelEnum != null ? $"{modelEnum.FullName}.{parts.Last()}" : typeName;
}

void WritePersistentProperties(ModelClass modelClass)
void WritePersistentProperties(ModelClass modelClass, bool b_isIdentityBase)
{
if (!modelClass.Attributes.Any(x => x.Persistent))
return;
Expand All @@ -574,8 +647,11 @@ void WritePersistentProperties(ModelClass modelClass)

List<string> segments = new List<string>();

bool b_attr_hasCustomKeyOverride = modelClass.Attributes.Any(x => x.Persistent && x.CustomAttributes.Contains("Key,"));

foreach (ModelAttribute modelAttribute in modelClass.Attributes.Where(x => x.Persistent))
{
if (isIdentityBaseWithoutPK(modelClass.Name) && modelAttribute.Name.ToLower() == "id") continue;
segments.Clear();

if (modelAttribute.IsIdentity)
Expand Down Expand Up @@ -634,18 +710,20 @@ void WritePersistentProperties(ModelClass modelClass)
modelAttribute.SetterVisibility == SetterAccessModifier.Internal ? "internal " :
string.Empty;

GeneratePropertyAnnotations(modelAttribute);
GeneratePropertyAnnotations(modelAttribute, b_attr_hasCustomKeyOverride);

String overrideText = (b_isIdentityBase)?"override ":"";

if (!string.IsNullOrWhiteSpace(modelAttribute.CustomAttributes))
Output($"[{modelAttribute.CustomAttributes.Trim('[',']')}]");

if (modelAttribute.IsConcurrencyToken || modelAttribute.AutoProperty)
{
Output($"public {modelAttribute.FQPrimitiveType}{nullable} {modelAttribute.Name} {{ get; {setterVisibility}set; }}");
Output($"public {overrideText}{modelAttribute.FQPrimitiveType}{nullable} {modelAttribute.Name} {{ get; {setterVisibility}set; }}");
}
else
{
Output($"public {modelAttribute.FQPrimitiveType}{nullable} {modelAttribute.Name}");
Output($"public {overrideText}{modelAttribute.FQPrimitiveType}{nullable} {modelAttribute.Name}");
Output("{");
Output("get");
Output("{");
Expand Down Expand Up @@ -682,9 +760,9 @@ void WritePersistentProperties(ModelClass modelClass)
}
}

void GeneratePropertyAnnotations(ModelAttribute modelAttribute, string namePrefix = null)
void GeneratePropertyAnnotations(ModelAttribute modelAttribute, bool b_attr_hasCustomKeyOverride=false, string namePrefix = null)
{
if (modelAttribute.IsIdentity) Output("[Key]");
if (!b_attr_hasCustomKeyOverride && modelAttribute.IsIdentity) Output("[Key]");
if (modelAttribute.Required) Output("[Required]");
if (modelAttribute.IsConcurrencyToken) Output("[ConcurrencyCheck]");
if (modelAttribute.FQPrimitiveType == "string")
Expand Down

0 comments on commit d49079e

Please sign in to comment.