Skip to content

Commit 46403fd

Browse files
committed
Added testing along with ef generation
1 parent e9f55a7 commit 46403fd

33 files changed

+3508
-2
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProductVersion>8.0.30703</ProductVersion>
8+
<SchemaVersion>2.0</SchemaVersion>
9+
<ProjectGuid>{F8F3F6D2-B7A6-4980-AC4B-9D4D8C5695BC}</ProjectGuid>
10+
<OutputType>Library</OutputType>
11+
<AppDesignerFolder>Properties</AppDesignerFolder>
12+
<RootNamespace>CodeFactory.Automation.Data.Sql.EF.Logic</RootNamespace>
13+
<AssemblyName>CodeFactory.Automation.Data.Sql.EF.Logic</AssemblyName>
14+
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
15+
<FileAlignment>512</FileAlignment>
16+
<StartAction>Program</StartAction>
17+
<StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
18+
</PropertyGroup>
19+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
20+
<DebugSymbols>true</DebugSymbols>
21+
<DebugType>full</DebugType>
22+
<Optimize>false</Optimize>
23+
<OutputPath>bin\Debug\</OutputPath>
24+
<DefineConstants>DEBUG;TRACE</DefineConstants>
25+
<ErrorReport>prompt</ErrorReport>
26+
<WarningLevel>4</WarningLevel>
27+
</PropertyGroup>
28+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
29+
<DebugType>pdbonly</DebugType>
30+
<Optimize>true</Optimize>
31+
<OutputPath>bin\Release\</OutputPath>
32+
<DefineConstants>TRACE</DefineConstants>
33+
<ErrorReport>prompt</ErrorReport>
34+
<WarningLevel>4</WarningLevel>
35+
</PropertyGroup>
36+
<ItemGroup>
37+
<PackageReference Include="CodeFactory.Automation.Standard.Logic">
38+
<Version>2.23159.1-PreRelease</Version>
39+
</PackageReference>
40+
<PackageReference Include="CodeFactory.Automation.Standard.NDF.Logic">
41+
<Version>2.23159.1-PreRelease</Version>
42+
</PackageReference>
43+
<PackageReference Include="CodeFactory.WinVs.SDK" Version="2.23160.1-PreRelease" />
44+
<Reference Include="System" />
45+
<Reference Include="System.Core" />
46+
<Reference Include="System.Xml.Linq" />
47+
<Reference Include="System.Data.DataSetExtensions" />
48+
<Reference Include="Microsoft.CSharp" />
49+
<Reference Include="System.Data" />
50+
<Reference Include="System.Xml" />
51+
<Reference Include="PresentationCore" />
52+
<Reference Include="PresentationFramework" />
53+
<Reference Include="System.Xaml" />
54+
<Reference Include="WindowsBase" />
55+
</ItemGroup>
56+
<ItemGroup>
57+
<Compile Include="DBContextAutomation.cs" />
58+
<Compile Include="EntityTransformAutomation.cs" />
59+
<Compile Include="ProjectExtensions.cs" />
60+
<Compile Include="Properties\AssemblyInfo.cs" />
61+
<Compile Include="RepositoryAutomation.cs" />
62+
<Compile Include="SharedData.cs" />
63+
</ItemGroup>
64+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
65+
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
66+
Other similar extension points exist, see Microsoft.Common.targets.
67+
<Target Name="BeforeBuild">
68+
</Target>
69+
<Target Name="AfterBuild">
70+
</Target> -->
71+
</Project>
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
using CodeFactory.WinVs.Models.CSharp;
2+
using CodeFactory.WinVs.Models.ProjectSystem;
3+
using CodeFactory.WinVs;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace CodeFactory.Automation.Data.Sql.EF.Logic
11+
{
12+
/// <summary>
13+
/// Automation that refreshes the DbContext to make sure the injection of a connection string is supported.
14+
/// </summary>
15+
public static class DbContextAutomation
16+
{
17+
/// <summary>
18+
/// Refreshes the implementation of the DbContext.
19+
/// </summary>
20+
/// <param name="source">CodeFactory automation</param>
21+
/// <param name="contextName">Name of the context class.</param>
22+
/// <param name="modelProject">The entity framework project hosting models.</param>
23+
/// <param name="modelFolder">Optional parameter that holds the target profile folders the models live in.</param>
24+
/// <returns>Refreshed instance of the DbContext</returns>
25+
/// <exception cref="CodeFactoryException">Raised if required data is missing.</exception>
26+
public static async Task<CsClass> RefreshDbContextAsync(this IVsActions source, string contextName,
27+
VsProject modelProject, VsProjectFolder modelFolder)
28+
{
29+
if (source == null)
30+
throw new CodeFactoryException("CodeFactory automation was not provided, cannot refresh the DbContext.");
31+
32+
if (string.IsNullOrEmpty(contextName))
33+
throw new CodeFactoryException(
34+
"The entity framework context name was not provided, cannot refresh the DbContext.");
35+
36+
if (modelProject == null)
37+
throw new CodeFactoryException(
38+
"The entity framework project was not provided, cannot refresh the DbContext.");
39+
40+
41+
var contextClass = (await modelProject.FindCSharpSourceByClassNameAsync(contextName))
42+
?.SourceCode?.Classes?.FirstOrDefault()
43+
?? throw new CodeFactoryException($"The entity framework context class '{contextName}' could not be loaded, cannot refresh the EF repository,");
44+
45+
CsSource contextSource = null;
46+
47+
if (modelFolder != null)
48+
{
49+
var folderChildren = await modelFolder.GetChildrenAsync(false, true);
50+
contextSource = folderChildren.Where(m => m.ModelType == VisualStudioModelType.CSharpSource).Cast<VsCSharpSource>().FirstOrDefault(s => s.Name == $"{contextName}.Load.cs")?.SourceCode;
51+
}
52+
else
53+
{
54+
var projectChildren = await modelProject.GetChildrenAsync(false, true);
55+
contextSource = projectChildren.Where(m => m.ModelType == VisualStudioModelType.CSharpSource).Cast<VsCSharpSource>().FirstOrDefault(s => s.Name == $"{contextName}.Load.cs")?.SourceCode;
56+
}
57+
58+
if(contextSource == null) contextSource = await
59+
CreateDbContextLoad(source,contextClass,modelProject,modelFolder);
60+
61+
var connectionStringInterface = (modelFolder != null
62+
? (await modelFolder.FindCSharpSourceByInterfaceNameAsync("IDBContextConnection"))?.SourceCode?.Interfaces?.FirstOrDefault()
63+
: (await modelProject.FindCSharpSourceByInterfaceNameAsync("IDBContextConnection"))?.SourceCode?.Interfaces?.FirstOrDefault())
64+
?? await source.CreateConnectionStringInterfaceAsync(modelProject, modelFolder);
65+
66+
var connectionStringClass = (modelFolder != null
67+
? (await modelFolder.FindCSharpSourceByClassNameAsync("DBContextConnection"))?.SourceCode?.Classes?.FirstOrDefault()
68+
: (await modelProject.FindCSharpSourceByClassNameAsync("DBContextConnection"))?.SourceCode?.Classes?.FirstOrDefault())
69+
?? await source.CreateConnectionStringClassAsync(modelProject, modelFolder);
70+
71+
return contextSource?.Classes?.FirstOrDefault();
72+
}
73+
74+
/// <summary>
75+
/// Adds the DbContext partial class file load which contains the constructor for passing a connection string.
76+
/// </summary>
77+
/// <param name="source">CodeFactory Automation</param>
78+
/// <param name="contextClass">The context class to be updated.</param>
79+
/// <param name="modelProject">The entity framework project hosting models.</param>
80+
/// <param name="modelFolder">Optional parameter that holds the target profile folders the models live in.</param>
81+
/// <returns>Updated class for the DbContext</returns>
82+
/// <exception cref="CodeFactoryException">Required information is missing.</exception>
83+
private static async Task<CsSource> CreateDbContextLoad(this IVsActions source,CsClass contextClass,
84+
VsProject modelProject, VsProjectFolder modelFolder = null)
85+
{
86+
SourceFormatter loadFormatter = new SourceFormatter();
87+
88+
loadFormatter.AppendCodeLine(0, "using System;");
89+
loadFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
90+
loadFormatter.AppendCodeLine(0, "using System.Linq;");
91+
loadFormatter.AppendCodeLine(0, "using System.Text;");
92+
loadFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
93+
loadFormatter.AppendCodeLine(0, "using Microsoft.EntityFrameworkCore;");
94+
loadFormatter.AppendCodeLine(0, $"namespace {contextClass.Namespace}");
95+
loadFormatter.AppendCodeLine(0, "{");
96+
loadFormatter.AppendCodeLine(1);
97+
loadFormatter.AppendCodeLine(1, $"public partial class {contextClass.Name}");
98+
loadFormatter.AppendCodeLine(1, "{");
99+
loadFormatter.AppendCodeLine(1);
100+
101+
if (!contextClass.Fields.Any(f => f.Name == "_connectionString"))
102+
{
103+
loadFormatter.AppendCodeLine(2, "private readonly string _connectionString;");
104+
loadFormatter.AppendCodeLine(2);
105+
}
106+
107+
if (!contextClass.Constructors.Any(m =>
108+
m.Parameters.Count() == 1 & m.Parameters.Any(p => p.Name == "connectionString")))
109+
{
110+
loadFormatter.AppendCodeLine(2, "/// <summary>");
111+
loadFormatter.AppendCodeLine(2, $"/// Creates a new instance of the context that injects the target connection string to use.");
112+
loadFormatter.AppendCodeLine(2, "/// </summary>");
113+
loadFormatter.AppendCodeLine(2, "/// <param name=\"connectionString\">connection string to use with the context.</param>");
114+
loadFormatter.AppendCodeLine(2, $"public {contextClass.Name}(string connectionString)");
115+
loadFormatter.AppendCodeLine(2, "{");
116+
loadFormatter.AppendCodeLine(3, "_connectionString = connectionString;");
117+
loadFormatter.AppendCodeLine(2, "}");
118+
loadFormatter.AppendCodeLine(2);
119+
}
120+
121+
122+
loadFormatter.AppendCodeLine(2, "protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)");
123+
loadFormatter.AppendCodeLine(2, "{");
124+
loadFormatter.AppendCodeLine(3, "if (!string.IsNullOrEmpty(_connectionString)) optionsBuilder.UseSqlServer(_connectionString);");
125+
loadFormatter.AppendCodeLine(2, "}");
126+
127+
loadFormatter.AppendCodeLine(1, "}");
128+
loadFormatter.AppendCodeLine(0, "}");
129+
130+
var doc = modelFolder != null ? await modelFolder.AddDocumentAsync($"{contextClass.Name}.Load.cs", loadFormatter.ReturnSource())
131+
: await modelProject.AddDocumentAsync($"{contextClass.Name}.Load.cs", loadFormatter.ReturnSource());
132+
133+
return doc == null
134+
? throw new CodeFactoryException(
135+
$"Failed to create the load logic for the DbContext '{contextClass.Name}' cannot upgrade the repository.")
136+
: await doc.GetCSharpSourceModelAsync();
137+
}
138+
139+
/// <summary>
140+
/// Creates the IDBContextConnection interface class is created.
141+
/// </summary>
142+
/// <param name="source">CodeFactory automation.</param>
143+
/// <param name="modelProject">The entity framework project hosting models.</param>
144+
/// <param name="modelFolder">Optional parameter that holds the target profile folders the models live in.</param>
145+
/// <returns>Created interface</returns>
146+
/// <exception cref="CodeFactoryException">Raised if required data is missing.</exception>
147+
private static async Task<CsInterface> CreateConnectionStringInterfaceAsync(this IVsActions source,
148+
VsProject modelProject, VsProjectFolder modelFolder = null)
149+
{
150+
SourceFormatter connectionFormatter = new SourceFormatter();
151+
152+
string targetNamespace = modelFolder != null
153+
? await modelFolder.GetCSharpNamespaceAsync()
154+
: modelProject.DefaultNamespace;
155+
156+
connectionFormatter.AppendCodeLine(0, "using System;");
157+
connectionFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
158+
connectionFormatter.AppendCodeLine(0, "using System.Linq;");
159+
connectionFormatter.AppendCodeLine(0, "using System.Text;");
160+
connectionFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
161+
connectionFormatter.AppendCodeLine(0);
162+
connectionFormatter.AppendCodeLine(0, $"namespace {targetNamespace}");
163+
connectionFormatter.AppendCodeLine(0, "{");
164+
connectionFormatter.AppendCodeLine(1, "/// <summary>");
165+
connectionFormatter.AppendCodeLine(1, "/// Contract for loading a connection string.");
166+
connectionFormatter.AppendCodeLine(1, "/// </summary>");
167+
connectionFormatter.AppendCodeLine(1, "/// <typeparam name=\"T\">The context the connection belongs to.</typeparam>");
168+
connectionFormatter.AppendCodeLine(1, $"public interface IDBContextConnection<T> where T : class");
169+
connectionFormatter.AppendCodeLine(1, "{");
170+
connectionFormatter.AppendCodeLine(2, "/// <summary>");
171+
connectionFormatter.AppendCodeLine(2, "/// Connection string to use with the DB context.");
172+
connectionFormatter.AppendCodeLine(2, "/// </summary>");
173+
connectionFormatter.AppendCodeLine(2, "public string ConnectionString { get;}");
174+
connectionFormatter.AppendCodeLine(1, "}");
175+
connectionFormatter.AppendCodeLine(0, "}");
176+
177+
var doc = modelFolder != null ? await modelFolder.AddDocumentAsync("IDBContextConnection.cs", connectionFormatter.ReturnSource())
178+
: await modelProject.AddDocumentAsync("IDBContextConnection.cs", connectionFormatter.ReturnSource());
179+
180+
return doc == null
181+
? throw new CodeFactoryException($"Failed to create the IDBContextConnection interface.")
182+
: (await doc.GetCSharpSourceModelAsync())?.Interfaces.FirstOrDefault();
183+
}
184+
185+
/// <summary>
186+
/// Creates the DBContextConnection class is created.
187+
/// </summary>
188+
/// <param name="source">CodeFactory automation.</param>
189+
/// <param name="modelProject">The entity framework project hosting models.</param>
190+
/// <param name="modelFolder">Optional parameter that holds the target profile folders the models live in.</param>
191+
/// <returns>Created interface</returns>
192+
/// <exception cref="CodeFactoryException">Raised if required data is missing.</exception>
193+
private static async Task<CsClass> CreateConnectionStringClassAsync(this IVsActions source,
194+
VsProject modelProject, VsProjectFolder modelFolder)
195+
{
196+
SourceFormatter connectionFormatter = new SourceFormatter();
197+
198+
string targetNamespace = modelFolder != null
199+
? await modelFolder.GetCSharpNamespaceAsync()
200+
: modelProject.DefaultNamespace;
201+
202+
connectionFormatter.AppendCodeLine(0, "using System;");
203+
connectionFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
204+
connectionFormatter.AppendCodeLine(0, "using System.Linq;");
205+
connectionFormatter.AppendCodeLine(0, "using System.Text;");
206+
connectionFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
207+
connectionFormatter.AppendCodeLine(0);
208+
connectionFormatter.AppendCodeLine(0, $"namespace {targetNamespace}");
209+
connectionFormatter.AppendCodeLine(0, "{");
210+
connectionFormatter.AppendCodeLine(1, "/// <summary>");
211+
connectionFormatter.AppendCodeLine(1, "/// Stores the connection string to be used with a target EF context.");
212+
connectionFormatter.AppendCodeLine(1, "/// </summary>");
213+
connectionFormatter.AppendCodeLine(1, "/// <typeparam name=\"T\">The EF context class the connection string is for.</typeparam>");
214+
connectionFormatter.AppendCodeLine(1, $"public class DBContextConnection<T>:IDBContextConnection<T> where T:class");
215+
connectionFormatter.AppendCodeLine(1, "{");
216+
connectionFormatter.AppendCodeLine(2, "/// <summary>");
217+
connectionFormatter.AppendCodeLine(2, "/// Backing field for the connection string property.");
218+
connectionFormatter.AppendCodeLine(2, "/// </summary>");
219+
connectionFormatter.AppendCodeLine(2, "private readonly string _connectionString;");
220+
connectionFormatter.AppendCodeLine(2);
221+
connectionFormatter.AppendCodeLine(2, "/// <summary>");
222+
connectionFormatter.AppendCodeLine(2, "/// Creates an instance that holds the connection string.");
223+
connectionFormatter.AppendCodeLine(2, "/// </summary>");
224+
connectionFormatter.AppendCodeLine(2, "public DBContextConnection(string connectionString)");
225+
connectionFormatter.AppendCodeLine(2, "{");
226+
connectionFormatter.AppendCodeLine(3, "_connectionString = connectionString;");
227+
connectionFormatter.AppendCodeLine(2, "}");
228+
connectionFormatter.AppendCodeLine(2);
229+
connectionFormatter.AppendCodeLine(2, "/// <summary>");
230+
connectionFormatter.AppendCodeLine(2, "/// Connection string to use with the DB context.");
231+
connectionFormatter.AppendCodeLine(2, "/// </summary>");
232+
connectionFormatter.AppendCodeLine(2, "public string ConnectionString => _connectionString;");
233+
connectionFormatter.AppendCodeLine(1, "}");
234+
connectionFormatter.AppendCodeLine(0, "}");
235+
236+
var doc = modelFolder != null ? await modelFolder.AddDocumentAsync("DBContextConnection.cs", connectionFormatter.ReturnSource())
237+
: await modelProject.AddDocumentAsync("DBContextConnection.cs", connectionFormatter.ReturnSource());
238+
239+
return doc == null
240+
? throw new CodeFactoryException($"Failed to create the DBContextConnection class.")
241+
: (await doc.GetCSharpSourceModelAsync())?.Classes.FirstOrDefault();
242+
}
243+
}
244+
245+
}

0 commit comments

Comments
 (0)