diff --git a/PowerTools.sln b/PowerTools.sln
new file mode 100644
index 00000000..71fd2bcf
--- /dev/null
+++ b/PowerTools.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 11
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2AE1B177-580C-44F1-9514-B3F23F4B1433}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerTools", "src\PowerTools\PowerTools.csproj", "{16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerTools.Test", "test\PowerTools.Test\PowerTools.Test.csproj", "{ADCD3A3D-2564-48F3-81A9-B0B532675808}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808} = {2AE1B177-580C-44F1-9514-B3F23F4B1433}
+ EndGlobalSection
+EndGlobal
diff --git a/packages/repositories.config b/packages/repositories.config
index a77aff03..fa9b2e99 100644
--- a/packages/repositories.config
+++ b/packages/repositories.config
@@ -3,4 +3,5 @@
+
\ No newline at end of file
diff --git a/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Context.tt b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Context.tt
new file mode 100644
index 00000000..389ed51f
--- /dev/null
+++ b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Context.tt
@@ -0,0 +1,47 @@
+<#@ template hostspecific="true" language="C#" #>
+<#@ include file="EF.Utility.CS.ttinclude" #><#@
+ output extension=".cs" #><#
+
+ var efHost = (EfTextTemplateHost)Host;
+ var code = new CodeGenerationTools(this);
+#>
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using <#= code.EscapeNamespace(efHost.MappingNamespace) #>;
+
+namespace <#= code.EscapeNamespace(efHost.Namespace) #>
+{
+ public class <#= efHost.EntityContainer.Name #> : DbContext
+ {
+ static <#= efHost.EntityContainer.Name #>()
+ {
+ Database.SetInitializer<<#= efHost.EntityContainer.Name #>>(null);
+ }
+
+ public <#= efHost.EntityContainer.Name #>()
+ : base("Name=<#= efHost.EntityContainer.Name #>")
+ {
+ }
+
+<#
+ foreach (var set in efHost.EntityContainer.BaseEntitySets.OfType())
+ {
+#>
+ public DbSet<<#= set.ElementType.Name #>> <#= set.Name #> { get; set; }
+<#
+ }
+#>
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+<#
+ foreach (var set in efHost.EntityContainer.BaseEntitySets.OfType())
+ {
+#>
+ modelBuilder.Configurations.Add(new <#= set.ElementType.Name #>Map());
+<#
+ }
+#>
+ }
+ }
+}
diff --git a/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Entity.tt b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Entity.tt
new file mode 100644
index 00000000..6588c736
--- /dev/null
+++ b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Entity.tt
@@ -0,0 +1,63 @@
+<#@ template hostspecific="true" language="C#" #>
+<#@ include file="EF.Utility.CS.ttinclude" #><#@
+ output extension=".cs" #><#
+
+ var efHost = (EfTextTemplateHost)Host;
+ var code = new CodeGenerationTools(this);
+#>
+using System;
+using System.Collections.Generic;
+
+namespace <#= code.EscapeNamespace(efHost.Namespace) #>
+{
+ public class <#= efHost.EntityType.Name #>
+ {
+<#
+ var collectionNavigations = efHost.EntityType.NavigationProperties.Where(
+ np => np.DeclaringType == efHost.EntityType
+ && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
+
+ // Add a ctor to initialize any collections
+ if (collectionNavigations.Any())
+ {
+#>
+ public <#= code.Escape(efHost.EntityType) #>()
+ {
+<#
+ foreach (var navProperty in collectionNavigations)
+ {
+#>
+ this.<#= code.Escape(navProperty) #> = new List<<#= code.Escape(navProperty.ToEndMember.GetEntityType()) #>>();
+<#
+ }
+#>
+ }
+
+<#
+ }
+
+ foreach (var property in efHost.EntityType.Properties)
+ {
+#>
+ <#= Accessibility.ForProperty(property) #> <#= code.Escape(property.TypeUsage) #> <#= code.Escape(property) #> { get; set; }
+<#
+ }
+
+ foreach (var navProperty in efHost.EntityType.NavigationProperties.Where(np => np.DeclaringType == efHost.EntityType))
+ {
+ if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
+ {
+#>
+ public virtual ICollection<<#= code.Escape(navProperty.ToEndMember.GetEntityType()) #>> <#= code.Escape(navProperty) #> { get; set; }
+<#
+ }
+ else
+ {
+#>
+ public virtual <#= code.Escape(navProperty.ToEndMember.GetEntityType()) #> <#= code.Escape(navProperty) #> { get; set; }
+<#
+ }
+ }
+#>
+ }
+}
diff --git a/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Mapping.tt b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Mapping.tt
new file mode 100644
index 00000000..30c427a3
--- /dev/null
+++ b/src/PowerTools/CodeTemplates/ReverseEngineerCodeFirst/Mapping.tt
@@ -0,0 +1,268 @@
+<#
+// Simplifying assumptions based on reverse engineer rules
+// - No complex types
+// - One entity container
+// - No inheritance
+// - Always have two navigation properties
+// - All associations expose FKs (except many:many)
+#>
+<#@ template hostspecific="true" language="C#" #>
+<#@ include file="EF.Utility.CS.ttinclude" #><#@
+ output extension=".cs" #><#
+
+ var efHost = (EfTextTemplateHost)Host;
+ var code = new CodeGenerationTools(this);
+
+ if (efHost.EntityFrameworkVersion >= new Version(4, 4))
+ {
+#>
+using System.ComponentModel.DataAnnotations.Schema;
+<#
+ }
+ else
+ {
+#>
+using System.ComponentModel.DataAnnotations;
+<#
+ }
+#>
+using System.Data.Entity.ModelConfiguration;
+
+namespace <#= code.EscapeNamespace(efHost.Namespace) #>
+{
+ public class <#= efHost.EntityType.Name #>Map : EntityTypeConfiguration<<#= efHost.EntityType.Name #>>
+ {
+ public <#= efHost.EntityType.Name #>Map()
+ {
+ // Primary Key
+<#
+ if (efHost.EntityType.KeyMembers.Count() == 1)
+ {
+#>
+ this.HasKey(t => t.<#= efHost.EntityType.KeyMembers.Single().Name #>);
+<#
+ }
+ else
+ {
+#>
+ this.HasKey(t => new { <#= string.Join(", ", efHost.EntityType.KeyMembers.Select(m => "t." + m.Name)) #> });
+<#
+ }
+#>
+
+ // Properties
+<#
+ foreach (var prop in efHost.EntityType.Properties)
+ {
+ var type = (PrimitiveType)prop.TypeUsage.EdmType;
+ var isKey = efHost.EntityType.KeyMembers.Contains(prop);
+ var storeProp = efHost.PropertyToColumnMappings[prop];
+ var sgpFacet = storeProp.TypeUsage.Facets.SingleOrDefault(f => f.Name == "StoreGeneratedPattern");
+ var storeGeneratedPattern = sgpFacet == null
+ ? StoreGeneratedPattern.None
+ : (StoreGeneratedPattern)sgpFacet.Value;
+
+ var configLines = new List();
+
+ if (type.ClrEquivalentType == typeof(int)
+ || type.ClrEquivalentType == typeof(decimal)
+ || type.ClrEquivalentType == typeof(short)
+ || type.ClrEquivalentType == typeof(long))
+ {
+ if (isKey && storeGeneratedPattern != StoreGeneratedPattern.Identity)
+ {
+ configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)");
+ }
+ else if ((!isKey || efHost.EntityType.KeyMembers.Count > 1) && storeGeneratedPattern == StoreGeneratedPattern.Identity)
+ {
+ configLines.Add(".HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)");
+ }
+ }
+
+ if (type.ClrEquivalentType == typeof(string)
+ || type.ClrEquivalentType == typeof(byte[]))
+ {
+ if (!prop.Nullable)
+ {
+ configLines.Add(".IsRequired()");
+ }
+
+ var unicodeFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "IsUnicode");
+ if(unicodeFacet != null && !(bool)unicodeFacet.Value)
+ {
+ configLines.Add(".IsUnicode(false)");
+ }
+
+ var fixedLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "FixedLength");
+ if (fixedLengthFacet != null && (bool)fixedLengthFacet.Value)
+ {
+ configLines.Add(".IsFixedLength()");
+ }
+
+ var maxLengthFacet = (Facet)prop.TypeUsage.Facets.SingleOrDefault(f => f.Name == "MaxLength");
+ if (maxLengthFacet != null && !maxLengthFacet.IsUnbounded)
+ {
+ configLines.Add(string.Format(".HasMaxLength({0})", maxLengthFacet.Value));
+
+ if (storeGeneratedPattern == StoreGeneratedPattern.Computed
+ && type.ClrEquivalentType == typeof(byte[])
+ && (int)maxLengthFacet.Value == 8)
+ {
+ configLines.Add(".IsRowVersion()");
+ }
+ }
+ }
+
+ if(configLines.Any())
+ {
+#>
+ this.Property(t => t.<#= prop.Name #>)
+ <#= string.Join("\r\n ", configLines) #>;
+
+<#
+ }
+ }
+
+ var tableSet = efHost.TableSet;
+ var tableName = (string)tableSet.MetadataProperties["Table"].Value
+ ?? tableSet.Name;
+ var schemaName = (string)tableSet.MetadataProperties["Schema"].Value;
+#>
+ // Table & Column Mappings
+<#
+ if (schemaName == "dbo" || string.IsNullOrWhiteSpace(schemaName))
+ {
+#>
+ this.ToTable("<#= tableName #>");
+<#
+ }
+ else
+ {
+#>
+ this.ToTable("<#= tableName #>", "<#= schemaName #>");
+<#
+ }
+
+ foreach (var property in efHost.EntityType.Properties)
+ {
+#>
+ this.Property(t => t.<#= property.Name #>).HasColumnName("<#= efHost.PropertyToColumnMappings[property].Name #>");
+<#
+ }
+
+ // Find m:m relationshipsto configure
+ var manyManyRelationships = efHost.EntityType.NavigationProperties
+ .Where(np => np.DeclaringType == efHost.EntityType
+ && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many
+ && np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many
+ && np.RelationshipType.RelationshipEndMembers.First() == np.FromEndMember); // <- ensures we only configure from one end
+
+ // Find FK relationships that this entity is the dependent of
+ var fkRelationships = efHost.EntityType.NavigationProperties
+ .Where(np => np.DeclaringType == efHost.EntityType
+ && ((AssociationType)np.RelationshipType).IsForeignKey
+ && ((AssociationType)np.RelationshipType).ReferentialConstraints.Single().ToRole == np.FromEndMember);
+
+ if(manyManyRelationships.Any() || fkRelationships.Any())
+ {
+#>
+
+ // Relationships
+<#
+ foreach (var navProperty in manyManyRelationships)
+ {
+ var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single();
+ var association = (AssociationType)navProperty.RelationshipType;
+ var mapping = efHost.ManyToManyMappings[association];
+ var item1 = mapping.Item1;
+ var mappingTableName = (string)mapping.Item1.MetadataProperties["Table"].Value
+ ?? item1.Name;
+ var mappingSchemaName = (string)item1.MetadataProperties["Schema"].Value;
+
+ // Need to ensure that FKs are decalred in the same order as the PK properties on each principal type
+ var leftType = (EntityType)navProperty.DeclaringType;
+ var leftKeyMappings = mapping.Item2[navProperty.FromEndMember];
+ var leftColumns = string.Join(", ", leftType.KeyMembers.Select(m => "\"" + leftKeyMappings[m] + "\""));
+ var rightType = (EntityType)otherNavProperty.DeclaringType;
+ var rightKeyMappings = mapping.Item2[otherNavProperty.FromEndMember];
+ var rightColumns = string.Join(", ", rightType.KeyMembers.Select(m => "\"" + rightKeyMappings[m] + "\""));
+#>
+ this.HasMany(t => t.<#= code.Escape(navProperty) #>)
+ .WithMany(t => t.<#= code.Escape(otherNavProperty) #>)
+ .Map(m =>
+ {
+<#
+ if (mappingSchemaName == "dbo" || string.IsNullOrWhiteSpace(mappingSchemaName))
+ {
+#>
+ m.ToTable("<#= mappingTableName #>");
+<#
+ }
+ else
+ {
+#>
+ m.ToTable("<#= mappingTableName #>", "<#= mappingSchemaName #>");
+<#
+ }
+#>
+ m.MapLeftKey(<#= leftColumns #>);
+ m.MapRightKey(<#= rightColumns #>);
+ });
+
+<#
+ }
+
+ foreach (var navProperty in fkRelationships)
+ {
+ var otherNavProperty = navProperty.ToEndMember.GetEntityType().NavigationProperties.Where(n => n.RelationshipType == navProperty.RelationshipType && n != navProperty).Single();
+ var association = (AssociationType)navProperty.RelationshipType;
+
+ if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
+ {
+#>
+ this.HasRequired(t => t.<#= code.Escape(navProperty) #>)
+<#
+ }
+ else
+ {
+#>
+ this.HasOptional(t => t.<#= code.Escape(navProperty) #>)
+<#
+ }
+
+ if(navProperty.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
+ {
+#>
+ .WithMany(t => t.<#= code.Escape(otherNavProperty) #>)
+<#
+ if(association.ReferentialConstraints.Single().ToProperties.Count == 1)
+ {
+#>
+ .HasForeignKey(d => d.<#= association.ReferentialConstraints.Single().ToProperties.Single().Name #>);
+<#
+ }
+ else
+ {
+#>
+ .HasForeignKey(d => new { <#= string.Join(", ", association.ReferentialConstraints.Single().ToProperties.Select(p => "d." + p.Name)) #> });
+<#
+ }
+ }
+ else
+ {
+ // NOTE: We can assume that this is a required:optional relationship
+ // as EDMGen will never create an optional:optional relationship
+ // because everything is one:many except PK-PK relationships which must be required
+#>
+ .WithOptional(t => t.<#= code.Escape(otherNavProperty) #>);
+<#
+ }
+ }
+#>
+
+<#
+ }
+#>
+ }
+ }
+}
diff --git a/src/PowerTools/DbContextPackage.cs b/src/PowerTools/DbContextPackage.cs
new file mode 100644
index 00000000..f5aa5f37
--- /dev/null
+++ b/src/PowerTools/DbContextPackage.cs
@@ -0,0 +1,527 @@
+namespace Microsoft.DbContextPackage
+{
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel.Design;
+ using System.Configuration;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Runtime.InteropServices;
+ using EnvDTE;
+ using EnvDTE80;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Handlers;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+ using Microsoft.VisualStudio.Shell;
+ using Microsoft.VisualStudio.Shell.Design;
+ using Microsoft.VisualStudio.Shell.Interop;
+ using Configuration = System.Configuration.Configuration;
+ using ConfigurationManager = System.Configuration.ConfigurationManager;
+
+ [PackageRegistration(UseManagedResourcesOnly = true)]
+ [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
+ [ProvideMenuResource("Menus.ctmenu", 1)]
+ [Guid(GuidList.guidDbContextPackagePkgString)]
+ [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
+ public sealed class DbContextPackage : Package
+ {
+ private readonly AddCustomTemplatesHandler _addCustomTemplatesHandler;
+ private readonly OptimizeContextHandler _optimizeContextHandler;
+ private readonly ReverseEngineerCodeFirstHandler _reverseEngineerCodeFirstHandler;
+ private readonly ViewContextHandler _viewContextHandler;
+ private readonly ViewDdlHandler _viewDdlHandler;
+
+ private DTE2 _dte2;
+
+ public DbContextPackage()
+ {
+ _addCustomTemplatesHandler = new AddCustomTemplatesHandler(this);
+ _optimizeContextHandler = new OptimizeContextHandler(this);
+ _reverseEngineerCodeFirstHandler = new ReverseEngineerCodeFirstHandler(this);
+ _viewContextHandler = new ViewContextHandler(this);
+ _viewDdlHandler = new ViewDdlHandler(this);
+ }
+
+ internal DTE2 DTE2
+ {
+ get { return _dte2; }
+ }
+
+ protected override void Initialize()
+ {
+ base.Initialize();
+
+ _dte2 = GetService(typeof(DTE)) as DTE2;
+
+ if (_dte2 == null)
+ {
+ return;
+ }
+
+ var oleMenuCommandService
+ = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
+
+ if (oleMenuCommandService != null)
+ {
+ var menuCommandID1 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidViewEntityDataModel);
+ var menuItem1 = new OleMenuCommand(OnItemContextMenuInvokeHandler, null, OnItemMenuBeforeQueryStatus, menuCommandID1);
+
+ oleMenuCommandService.AddCommand(menuItem1);
+
+ var menuCommandID2 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidViewEntityDataModelXml);
+ var menuItem2 = new OleMenuCommand(OnItemContextMenuInvokeHandler, null, OnItemMenuBeforeQueryStatus, menuCommandID2);
+
+ oleMenuCommandService.AddCommand(menuItem2);
+
+ var menuCommandID3 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidPrecompileEntityDataModelViews);
+ var menuItem3 = new OleMenuCommand(OnOptimizeContextInvokeHandler, null, OnOptimizeContextBeforeQueryStatus, menuCommandID3);
+
+ oleMenuCommandService.AddCommand(menuItem3);
+
+ var menuCommandID4 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidViewEntityModelDdl);
+ var menuItem4 = new OleMenuCommand(OnItemContextMenuInvokeHandler, null, OnItemMenuBeforeQueryStatus, menuCommandID4);
+
+ oleMenuCommandService.AddCommand(menuItem4);
+
+ var menuCommandID5 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidReverseEngineerCodeFirst);
+ var menuItem5 = new OleMenuCommand(OnProjectContextMenuInvokeHandler, null, OnProjectMenuBeforeQueryStatus, menuCommandID5);
+
+ oleMenuCommandService.AddCommand(menuItem5);
+
+ var menuCommandID6 = new CommandID(GuidList.guidDbContextPackageCmdSet, (int)PkgCmdIDList.cmdidCustomizeReverseEngineerTemplates);
+ var menuItem6 = new OleMenuCommand(OnProjectContextMenuInvokeHandler, null, OnProjectMenuBeforeQueryStatus, menuCommandID6);
+
+ oleMenuCommandService.AddCommand(menuItem6);
+ }
+ }
+
+ private void OnProjectMenuBeforeQueryStatus(object sender, EventArgs e)
+ {
+ var menuCommand = sender as MenuCommand;
+
+ if (menuCommand == null)
+ {
+ return;
+ }
+
+ if (_dte2.SelectedItems.Count != 1)
+ {
+ return;
+ }
+
+ var project = _dte2.SelectedItems.Item(1).Project;
+
+ if (project == null)
+ {
+ return;
+ }
+
+ menuCommand.Visible =
+ project.Kind == "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; // csproj
+ }
+
+ private void OnItemMenuBeforeQueryStatus(object sender, EventArgs e)
+ {
+ OnItemMenuBeforeQueryStatus(
+ sender,
+ new[] { FileExtensions.CSharp, FileExtensions.VisualBasic });
+ }
+
+ private void OnOptimizeContextBeforeQueryStatus(object sender, EventArgs e)
+ {
+ OnItemMenuBeforeQueryStatus(
+ sender,
+ new[] { FileExtensions.CSharp, FileExtensions.VisualBasic, FileExtensions.EntityDataModel });
+ }
+
+ private void OnItemMenuBeforeQueryStatus(object sender, IEnumerable supportedExtensions)
+ {
+ Contract.Requires(supportedExtensions != null);
+
+ var menuCommand = sender as MenuCommand;
+
+ if (menuCommand == null)
+ {
+ return;
+ }
+
+ if (_dte2.SelectedItems.Count != 1)
+ {
+ return;
+ }
+
+ var extensionValue = GetSelectedItemExtension();
+ menuCommand.Visible = supportedExtensions.Contains(extensionValue);
+ }
+
+ private string GetSelectedItemExtension()
+ {
+ var selectedItem = _dte2.SelectedItems.Item(1);
+
+ if ((selectedItem.ProjectItem == null)
+ || (selectedItem.ProjectItem.Properties == null))
+ {
+ return null;
+ }
+
+ var extension = selectedItem.ProjectItem.Properties.Item("Extension");
+
+ if (extension == null)
+ {
+ return null;
+ }
+
+ return (string)extension.Value;
+ }
+
+ private void OnProjectContextMenuInvokeHandler(object sender, EventArgs e)
+ {
+ var menuCommand = sender as MenuCommand;
+ if (menuCommand == null || _dte2.SelectedItems.Count != 1)
+ {
+ return;
+ }
+
+ var project = _dte2.SelectedItems.Item(1).Project;
+ if (project == null)
+ {
+ return;
+ }
+
+ if (menuCommand.CommandID.ID == PkgCmdIDList.cmdidReverseEngineerCodeFirst)
+ {
+ _reverseEngineerCodeFirstHandler.ReverseEngineerCodeFirst(project);
+ }
+ else if (menuCommand.CommandID.ID == PkgCmdIDList.cmdidCustomizeReverseEngineerTemplates)
+ {
+ _addCustomTemplatesHandler.AddCustomTemplates(project);
+ }
+ }
+
+ private void OnItemContextMenuInvokeHandler(object sender, EventArgs e)
+ {
+ var menuCommand = sender as MenuCommand;
+
+ if (menuCommand == null)
+ {
+ return;
+ }
+
+ if (_dte2.SelectedItems.Count != 1)
+ {
+ return;
+ }
+
+ Type systemContextType;
+ var context = DiscoverUserContextType(out systemContextType);
+
+ if (context != null)
+ {
+ if (menuCommand.CommandID.ID == PkgCmdIDList.cmdidPrecompileEntityDataModelViews)
+ {
+ _optimizeContextHandler.OptimizeContext(context);
+ }
+ else if (menuCommand.CommandID.ID == PkgCmdIDList.cmdidViewEntityModelDdl)
+ {
+ _viewDdlHandler.ViewDdl(context);
+ }
+ else
+ {
+ _viewContextHandler.ViewContext(menuCommand, context, systemContextType);
+ }
+ }
+ }
+
+ private void OnOptimizeContextInvokeHandler(object sender, EventArgs e)
+ {
+ if (_dte2.SelectedItems.Count != 1)
+ {
+ return;
+ }
+
+ var extensionValue = GetSelectedItemExtension();
+
+ if (extensionValue != FileExtensions.EntityDataModel)
+ {
+ OnItemContextMenuInvokeHandler(sender, e);
+
+ return;
+ }
+
+ _optimizeContextHandler.OptimizeEdmx(
+ (string)_dte2.SelectedItems.Item(1).ProjectItem.Properties.Item("FullPath").Value);
+ }
+
+ internal static dynamic GetObjectContext(dynamic context)
+ {
+ var objectContextAdapterType
+ = context.GetType().GetInterface("System.Data.Entity.Infrastructure.IObjectContextAdapter");
+
+ return objectContextAdapterType.InvokeMember("ObjectContext", BindingFlags.GetProperty, null, context, null);
+ }
+
+ internal void LogError(string statusMessage, Exception exception)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(statusMessage));
+ Contract.Requires(exception != null);
+
+ var edmSchemaErrorException = exception as EdmSchemaErrorException;
+ var compilerErrorException = exception as CompilerErrorException;
+
+ _dte2.StatusBar.Text = statusMessage;
+
+ var buildOutputWindow = _dte2.ToolWindows.OutputWindow.OutputWindowPanes.Item("Build");
+
+ buildOutputWindow.OutputString(Environment.NewLine);
+
+ if (edmSchemaErrorException != null)
+ {
+ buildOutputWindow.OutputString(edmSchemaErrorException.Message + Environment.NewLine);
+
+ foreach (var error in edmSchemaErrorException.Errors)
+ {
+ buildOutputWindow.OutputString(error + Environment.NewLine);
+ }
+ }
+ else if (compilerErrorException != null)
+ {
+ buildOutputWindow.OutputString(compilerErrorException.Message + Environment.NewLine);
+
+ foreach (var error in compilerErrorException.Errors)
+ {
+ buildOutputWindow.OutputString(error + Environment.NewLine);
+ }
+ }
+ else
+ {
+ buildOutputWindow.OutputString(exception + Environment.NewLine);
+ }
+
+ buildOutputWindow.Activate();
+ }
+
+ private dynamic DiscoverUserContextType(out Type systemContextType)
+ {
+ var project = _dte2.SelectedItems.Item(1).ProjectItem.ContainingProject;
+
+ if (!project.TryBuild())
+ {
+ _dte2.StatusBar.Text = Strings.BuildFailed;
+ systemContextType = null;
+
+ return null;
+ }
+
+ DynamicTypeService typeService;
+ IVsSolution solution;
+ using (var serviceProvider = new ServiceProvider((VisualStudio.OLE.Interop.IServiceProvider)_dte2.DTE))
+ {
+ typeService = (DynamicTypeService)serviceProvider.GetService(typeof(DynamicTypeService));
+ solution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
+ }
+
+ IVsHierarchy vsHierarchy;
+ var hr = solution.GetProjectOfUniqueName(_dte2.SelectedItems.Item(1).ProjectItem.ContainingProject.UniqueName, out vsHierarchy);
+
+ if (hr != ProjectExtensions.S_OK)
+ {
+ throw Marshal.GetExceptionForHR(hr);
+ }
+
+ var resolver = typeService.GetTypeResolutionService(vsHierarchy);
+
+ systemContextType = resolver.GetType("System.Data.Entity.DbContext");
+
+ if (systemContextType != null)
+ {
+ var codeElements = FindClassesInCodeModel(_dte2.SelectedItems.Item(1).ProjectItem.FileCodeModel.CodeElements);
+
+ if (codeElements.Any())
+ {
+ var contextInfoType = systemContextType.Assembly.GetType("System.Data.Entity.Infrastructure.DbContextInfo");
+ var startUpProject = GetStartUpProject() ?? project;
+ Configuration userConfig;
+
+ try
+ {
+ userConfig = GetUserConfig(startUpProject);
+ }
+ catch (Exception ex)
+ {
+ LogError(Strings.LoadConfigFailed, ex);
+
+ return null;
+ }
+
+ SetDataDirectory(startUpProject);
+
+ foreach (var codeElement in codeElements)
+ {
+ var userContextType = resolver.GetType(codeElement.FullName);
+
+ if (userContextType != null && systemContextType.IsAssignableFrom(userContextType))
+ {
+ dynamic contextInfo;
+
+ if (contextInfoType != null)
+ {
+ var constructor = contextInfoType.GetConstructor(new[] { typeof(Type), typeof(Configuration) });
+
+ if (constructor != null)
+ {
+ // Versions 4.3.0 and higher
+ contextInfo = constructor.Invoke(new object[] { userContextType, userConfig });
+ }
+ else
+ {
+ constructor = contextInfoType.GetConstructor(new[] { typeof(Type), typeof(ConnectionStringSettingsCollection) });
+ Contract.Assert(constructor != null);
+
+ // Versions 4.1.10715 through 4.2.0.0
+ contextInfo = constructor.Invoke(new object[] { userContextType, userConfig.ConnectionStrings.ConnectionStrings });
+ }
+ }
+ else
+ {
+ // Versions 4.1.10331.0 and lower
+ throw Error.UnsupportedVersion();
+ }
+
+ if (contextInfo.IsConstructible)
+ {
+ DisableDatabaseInitializer(userContextType, systemContextType);
+
+ try
+ {
+ return contextInfo.CreateInstance();
+ }
+ catch (Exception exception)
+ {
+ LogError(Strings.CreateContextFailed(userContextType.Name), exception);
+
+ return null;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ _dte2.StatusBar.Text = Strings.NoContext;
+
+ return null;
+ }
+
+ private Project GetStartUpProject()
+ {
+ var startupProjectPaths = (object[])_dte2.Solution.SolutionBuild.StartupProjects;
+
+ if (startupProjectPaths.Length == 1)
+ {
+ var startupProjectPath = (string)startupProjectPaths[0];
+
+ if (!Path.IsPathRooted(startupProjectPath))
+ {
+ var solutionPath = Path.GetDirectoryName((string)_dte2.Solution.Properties.Item("Path").Value);
+ startupProjectPath = Path.Combine(
+ solutionPath,
+ startupProjectPath);
+ }
+
+ return _dte2.Solution.Projects.Cast().Single(
+ p =>
+ {
+ string fullName;
+ try
+ {
+ fullName = p.FullName;
+ }
+ catch (NotImplementedException)
+ {
+ return false;
+ }
+
+ return fullName == startupProjectPath;
+ });
+ }
+
+ return null;
+ }
+
+ private static Configuration GetUserConfig(Project project)
+ {
+ Contract.Requires(project != null);
+
+ var userConfigFilename
+ = Path.Combine(
+ (string)project.Properties.Item("FullPath").Value,
+ project.IsWebProject()
+ ? "Web.config"
+ : "App.config");
+
+ return ConfigurationManager.OpenMappedExeConfiguration(
+ new ExeConfigurationFileMap { ExeConfigFilename = userConfigFilename },
+ ConfigurationUserLevel.None);
+ }
+
+ private static void SetDataDirectory(Project project)
+ {
+ Contract.Requires(project != null);
+
+ AppDomain.CurrentDomain.SetData(
+ "DataDirectory",
+ project.IsWebProject()
+ ? Path.Combine(project.GetProjectDir(), "App_Data")
+ : project.GetTargetDir());
+ }
+
+ private static IEnumerable FindClassesInCodeModel(CodeElements codeElements)
+ {
+ Contract.Requires(codeElements != null);
+
+ foreach (CodeElement codeElement in codeElements)
+ {
+ if (codeElement.Kind == vsCMElement.vsCMElementClass)
+ {
+ yield return codeElement;
+ }
+
+ foreach (var element in FindClassesInCodeModel(codeElement.Children))
+ {
+ yield return element;
+ }
+ }
+ }
+
+ private static void DisableDatabaseInitializer(Type userContextType, Type systemContextType)
+ {
+ Contract.Requires(userContextType != null);
+ Contract.Requires(systemContextType != null);
+
+ var databaseType = systemContextType.Assembly.GetType("System.Data.Entity.Database");
+
+ if (databaseType != null)
+ {
+ var setInitializerMethodInfo
+ = databaseType.GetMethod("SetInitializerInternal", BindingFlags.NonPublic | BindingFlags.Static);
+
+ if (setInitializerMethodInfo != null)
+ {
+ var boundSetInitializerMethodInfo
+ = setInitializerMethodInfo.MakeGenericMethod(userContextType);
+
+ boundSetInitializerMethodInfo.Invoke(null, new object[] { null, true });
+ }
+ }
+ }
+
+ internal T GetService()
+ where T : class
+ {
+ return (T)GetService(typeof(T));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/DbContextPackage.vsct b/src/PowerTools/DbContextPackage.vsct
new file mode 100644
index 00000000..84bf1385
--- /dev/null
+++ b/src/PowerTools/DbContextPackage.vsct
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/PowerTools/Extensions/CompilerErrorCollectionExtensions.cs b/src/PowerTools/Extensions/CompilerErrorCollectionExtensions.cs
new file mode 100644
index 00000000..7a11404c
--- /dev/null
+++ b/src/PowerTools/Extensions/CompilerErrorCollectionExtensions.cs
@@ -0,0 +1,22 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System.CodeDom.Compiler;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+
+ internal static class CompilerErrorCollectionExtensions
+ {
+ public static void HandleErrors(this CompilerErrorCollection errors, string message)
+ {
+ Contract.Requires(errors != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(message));
+
+ if (errors.HasErrors)
+ {
+ throw new CompilerErrorException(
+ message,
+ errors.Cast().ToList());
+ }
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/CompilerErrorException.cs b/src/PowerTools/Extensions/CompilerErrorException.cs
new file mode 100644
index 00000000..df295c5e
--- /dev/null
+++ b/src/PowerTools/Extensions/CompilerErrorException.cs
@@ -0,0 +1,58 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.Serialization;
+
+ [Serializable]
+ public class CompilerErrorException : Exception
+ {
+ private readonly IEnumerable _errors;
+
+ public CompilerErrorException()
+ {
+ }
+
+ public CompilerErrorException(string message)
+ : base(message)
+ {
+ }
+
+ public CompilerErrorException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public CompilerErrorException(string message, IEnumerable errors)
+ : base(message)
+ {
+ Contract.Requires(errors != null);
+
+ _errors = errors;
+ }
+
+ protected CompilerErrorException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ Contract.Requires(info != null);
+
+ _errors = (IEnumerable)info.GetValue("Errors", typeof(IEnumerable));
+ }
+
+ public IEnumerable Errors
+ {
+ get { return _errors; }
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ Contract.Requires(info != null);
+
+ info.AddValue("Errors", _errors);
+
+ base.GetObjectData(info, context);
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/EdmSchemaErrorException.cs b/src/PowerTools/Extensions/EdmSchemaErrorException.cs
new file mode 100644
index 00000000..c6d28942
--- /dev/null
+++ b/src/PowerTools/Extensions/EdmSchemaErrorException.cs
@@ -0,0 +1,58 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Data.Metadata.Edm;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.Serialization;
+
+ [Serializable]
+ public class EdmSchemaErrorException : Exception
+ {
+ private readonly IEnumerable _errors;
+
+ public EdmSchemaErrorException()
+ {
+ }
+
+ public EdmSchemaErrorException(string message)
+ : base(message)
+ {
+ }
+
+ public EdmSchemaErrorException(string message, IEnumerable errors)
+ : base(message)
+ {
+ Contract.Requires(errors != null);
+
+ _errors = errors;
+ }
+
+ public EdmSchemaErrorException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected EdmSchemaErrorException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ Contract.Requires(info != null);
+
+ _errors = (IEnumerable)info.GetValue("Errors", typeof(IEnumerable));
+ }
+
+ public IEnumerable Errors
+ {
+ get { return _errors; }
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ Contract.Requires(info != null);
+
+ info.AddValue("Errors", _errors);
+
+ base.GetObjectData(info, context);
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/IComponentModelExtensions.cs b/src/PowerTools/Extensions/IComponentModelExtensions.cs
new file mode 100644
index 00000000..98a6ee91
--- /dev/null
+++ b/src/PowerTools/Extensions/IComponentModelExtensions.cs
@@ -0,0 +1,19 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.Diagnostics.Contracts;
+ using Microsoft.VisualStudio.ComponentModelHost;
+
+ internal static class IComponentModelExtensions
+ {
+ public static object GetService(this IComponentModel componentModel, Type serviceType)
+ {
+ Contract.Requires(componentModel != null);
+ Contract.Requires(serviceType != null);
+
+ return typeof(IComponentModel).GetMethod("GetService")
+ .MakeGenericMethod(serviceType)
+ .Invoke(componentModel, null);
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/IEnumerableOfEdmSchemaErrorExtensions.cs b/src/PowerTools/Extensions/IEnumerableOfEdmSchemaErrorExtensions.cs
new file mode 100644
index 00000000..76848469
--- /dev/null
+++ b/src/PowerTools/Extensions/IEnumerableOfEdmSchemaErrorExtensions.cs
@@ -0,0 +1,27 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System.Collections.Generic;
+ using System.Data.Metadata.Edm;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+
+ internal static class IEnumerableOfEdmSchemaErrorExtensions
+ {
+ public static void HandleErrors(this IEnumerable errors, string message)
+ {
+ Contract.Requires(errors != null);
+
+ if (errors.HasErrors())
+ {
+ throw new EdmSchemaErrorException(message, errors);
+ }
+ }
+
+ private static bool HasErrors(this IEnumerable errors)
+ {
+ Contract.Requires(errors != null);
+
+ return errors.Any(e => e.Severity == EdmSchemaErrorSeverity.Error);
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/ProjectExtensions.cs b/src/PowerTools/Extensions/ProjectExtensions.cs
new file mode 100644
index 00000000..e6153644
--- /dev/null
+++ b/src/PowerTools/Extensions/ProjectExtensions.cs
@@ -0,0 +1,158 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using EnvDTE;
+ using Microsoft.VisualStudio.ComponentModelHost;
+ using Microsoft.VisualStudio.Shell;
+ using Microsoft.VisualStudio.Shell.Interop;
+ using IServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
+
+ internal static class ProjectExtensions
+ {
+ public const int S_OK = 0;
+ public const string WebApplicationProjectTypeGuid = "{349C5851-65DF-11DA-9384-00065B846F21}";
+ public const string WebSiteProjectTypeGuid = "{E24C65DC-7377-472B-9ABA-BC803B73C61A}";
+
+ public static ProjectItem AddNewFile(this Project project, string path, string contents)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(path));
+
+ if (string.IsNullOrWhiteSpace(contents))
+ {
+ return null;
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ project.DTE.SourceControl.CheckOutItemIfNeeded(path);
+ File.WriteAllText(path, contents);
+
+ return project.ProjectItems.AddFromFile(path);
+ }
+
+ public static string GetProjectDir(this Project project)
+ {
+ Contract.Requires(project != null);
+
+ return project.GetPropertyValue("FullPath");
+ }
+
+ public static string GetTargetDir(this Project project)
+ {
+ Contract.Requires(project != null);
+
+ var fullPath = project.GetProjectDir();
+ string outputPath;
+
+ outputPath = project.GetConfigurationPropertyValue("OutputPath");
+
+ return Path.Combine(fullPath, outputPath);
+ }
+
+ public static void InstallPackage(this Project project, string packageId)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(packageId));
+
+ var typeNuGetConstants = Type.GetType("NuGet.NuGetConstants, NuGet.VisualStudio", true);
+ var typeIVsPackageInstaller = Type.GetType("NuGet.VisualStudio.IVsPackageInstaller, NuGet.VisualStudio", true);
+ var typeSemanticVersion = Type.GetType("NuGet.SemanticVersion, NuGet.Core", true);
+
+ var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
+ var packageInstaller = componentModel.GetService(typeIVsPackageInstaller);
+ var source = (string)typeNuGetConstants.GetField("DefaultFeedUrl").GetValue(null);
+
+ typeIVsPackageInstaller.GetMethod(
+ "InstallPackage",
+ new[] { typeof(string), typeof(Project), typeof(string), typeSemanticVersion, typeof(bool) })
+ .Invoke(packageInstaller, new object[] { source, project, packageId, null, false });
+ }
+
+ public static bool IsWebProject(this Project project)
+ {
+ Contract.Requires(project != null);
+
+ return project.GetProjectTypes().Any(
+ g => g.EqualsIgnoreCase(WebApplicationProjectTypeGuid)
+ || g.EqualsIgnoreCase(WebSiteProjectTypeGuid));
+ }
+
+ public static bool TryBuild(this Project project)
+ {
+ Contract.Requires(project != null);
+
+ var dte = project.DTE;
+ var configuration = dte.Solution.SolutionBuild.ActiveConfiguration.Name;
+
+ dte.Solution.SolutionBuild.BuildProject(configuration, project.UniqueName, true);
+
+ return dte.Solution.SolutionBuild.LastBuildInfo == 0;
+ }
+
+ private static T GetPropertyValue(this Project project, string propertyName)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(propertyName));
+
+ var property = project.Properties.Item(propertyName);
+
+ if (property == null)
+ {
+ return default(T);
+ }
+
+ return (T)property.Value;
+ }
+
+ private static T GetConfigurationPropertyValue(this Project project, string propertyName)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(propertyName));
+
+ var property = project.ConfigurationManager.ActiveConfiguration.Properties.Item(propertyName);
+
+ if (property == null)
+ {
+ return default(T);
+ }
+
+ return (T)property.Value;
+ }
+
+ private static IEnumerable GetProjectTypes(this Project project)
+ {
+ Contract.Requires(project != null);
+
+ IVsSolution solution;
+ using (var serviceProvider = new ServiceProvider((IServiceProvider)project.DTE))
+ {
+ solution = (IVsSolution)serviceProvider.GetService(typeof(IVsSolution));
+ }
+
+ IVsHierarchy hierarchy;
+ var hr = solution.GetProjectOfUniqueName(project.UniqueName, out hierarchy);
+
+ if (hr != S_OK)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ string projectTypeGuidsString;
+
+ var aggregatableProject = (IVsAggregatableProject)hierarchy;
+ hr = aggregatableProject.GetAggregateProjectTypeGuids(out projectTypeGuidsString);
+
+ if (hr != S_OK)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ return projectTypeGuidsString.Split(';');
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Extensions/ProjectItemsExtensions.cs b/src/PowerTools/Extensions/ProjectItemsExtensions.cs
new file mode 100644
index 00000000..8b36382c
--- /dev/null
+++ b/src/PowerTools/Extensions/ProjectItemsExtensions.cs
@@ -0,0 +1,21 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using EnvDTE;
+
+ internal static class ProjectItemsExtensions
+ {
+ public static ProjectItem GetItem(this ProjectItems projectItems, string name)
+ {
+ Contract.Requires(projectItems != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(name));
+
+ return projectItems
+ .Cast()
+ .FirstOrDefault(
+ pi => string.Equals(pi.Name, name, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/SourceControlExtenstions.cs b/src/PowerTools/Extensions/SourceControlExtenstions.cs
new file mode 100644
index 00000000..1a0de588
--- /dev/null
+++ b/src/PowerTools/Extensions/SourceControlExtenstions.cs
@@ -0,0 +1,21 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System.Diagnostics.Contracts;
+ using EnvDTE;
+
+ internal static class SourceControlExtenstions
+ {
+ public static bool CheckOutItemIfNeeded(this SourceControl sourceControl, string itemName)
+ {
+ Contract.Requires(sourceControl != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(itemName));
+
+ if (sourceControl.IsItemUnderSCC(itemName) && !sourceControl.IsItemCheckedOut(itemName))
+ {
+ return sourceControl.CheckOutItem(itemName);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/PowerTools/Extensions/StringExtensions.cs b/src/PowerTools/Extensions/StringExtensions.cs
new file mode 100644
index 00000000..715eb00a
--- /dev/null
+++ b/src/PowerTools/Extensions/StringExtensions.cs
@@ -0,0 +1,12 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+
+ internal static class StringExtensions
+ {
+ public static bool EqualsIgnoreCase(this string s1, string s2)
+ {
+ return string.Equals(s1, s2, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Extensions/XContainerExtensions.cs b/src/PowerTools/Extensions/XContainerExtensions.cs
new file mode 100644
index 00000000..2c092fc6
--- /dev/null
+++ b/src/PowerTools/Extensions/XContainerExtensions.cs
@@ -0,0 +1,29 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Xml.Linq;
+
+ internal static class XContainerExtensions
+ {
+ ///
+ /// Gets the first (in document order) child element with the specified local name and one of the namespaces.
+ ///
+ /// The node containing the child elements.
+ /// A collection of namespaces used when searching for the child element.
+ /// The local (unqualified) name to match.
+ /// A that matches the specified name and namespace, or null.
+ public static XElement Element(this XContainer container, IEnumerable namespaces, string localName)
+ {
+ Contract.Requires(container != null);
+ Contract.Requires(namespaces != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(localName));
+
+ return container.Elements()
+ .FirstOrDefault(
+ e => e.Name.LocalName == localName
+ && namespaces.Contains(e.Name.Namespace));
+ }
+ }
+}
diff --git a/src/PowerTools/GlobalSuppressions.cs b/src/PowerTools/GlobalSuppressions.cs
new file mode 100644
index 00000000..746e3dae
--- /dev/null
+++ b/src/PowerTools/GlobalSuppressions.cs
@@ -0,0 +1,11 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project. Project-level
+// suppressions either have no target or are given a specific target
+// and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click "In Project
+// Suppression File". You do not need to add suppressions to this
+// file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
diff --git a/src/PowerTools/Guids.cs b/src/PowerTools/Guids.cs
new file mode 100644
index 00000000..dc33b9d7
--- /dev/null
+++ b/src/PowerTools/Guids.cs
@@ -0,0 +1,14 @@
+// Guids.cs
+// MUST match guids.h
+namespace Microsoft.DbContextPackage
+{
+ using System;
+
+ internal static class GuidList
+ {
+ public const string guidDbContextPackagePkgString = "2b119c79-9836-46e2-b5ed-eb766cebbf7c";
+ public const string guidDbContextPackageCmdSetString = "c769a05d-8d51-4919-bfe6-5f35a0eaf27e";
+
+ public static readonly Guid guidDbContextPackageCmdSet = new Guid(guidDbContextPackageCmdSetString);
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Handlers/AddCustomTemplatesHandler.cs b/src/PowerTools/Handlers/AddCustomTemplatesHandler.cs
new file mode 100644
index 00000000..7e04f8e4
--- /dev/null
+++ b/src/PowerTools/Handlers/AddCustomTemplatesHandler.cs
@@ -0,0 +1,51 @@
+namespace Microsoft.DbContextPackage.Handlers
+{
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using EnvDTE;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+
+ internal class AddCustomTemplatesHandler
+ {
+ private readonly DbContextPackage _package;
+
+ public AddCustomTemplatesHandler(DbContextPackage package)
+ {
+ Contract.Requires(package != null);
+
+ _package = package;
+ }
+
+ public void AddCustomTemplates(Project project)
+ {
+ Contract.Requires(project != null);
+
+ try
+ {
+ AddTemplate(project, Templates.ContextTemplate);
+ AddTemplate(project, Templates.EntityTemplate);
+ AddTemplate(project, Templates.MappingTemplate);
+ }
+ catch (Exception ex)
+ {
+ _package.LogError(Strings.AddTemplatesError, ex);
+ }
+ }
+
+ private static void AddTemplate(Project project, string templatePath)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(templatePath));
+
+ var projectDir = project.GetProjectDir();
+
+ var filePath = Path.Combine(projectDir, templatePath);
+ var contents = Templates.GetDefaultTemplate(templatePath);
+ var item = project.AddNewFile(filePath, contents);
+ item.Properties.Item("CustomTool").Value = null;
+ }
+ }
+}
diff --git a/src/PowerTools/Handlers/OptimizeContextHandler.cs b/src/PowerTools/Handlers/OptimizeContextHandler.cs
new file mode 100644
index 00000000..cd3b274e
--- /dev/null
+++ b/src/PowerTools/Handlers/OptimizeContextHandler.cs
@@ -0,0 +1,140 @@
+namespace Microsoft.DbContextPackage.Handlers
+{
+ using System;
+ using System.Data.Entity.Design;
+ using System.Data.Mapping;
+ using System.Data.Metadata.Edm;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Threading.Tasks;
+ using System.Windows.Forms;
+ using EnvDTE;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+ using Task = System.Threading.Tasks.Task;
+
+ internal class OptimizeContextHandler
+ {
+ private readonly DbContextPackage _package;
+
+ public OptimizeContextHandler(DbContextPackage package)
+ {
+ Contract.Requires(package != null);
+
+ _package = package;
+ }
+
+ public void OptimizeContext(dynamic context)
+ {
+ Type contextType = context.GetType();
+
+ try
+ {
+ var selectedItem = _package.DTE2.SelectedItems.Item(1);
+ var selectedItemExtension = (string)selectedItem.ProjectItem.Properties.Item("Extension").Value;
+ var languageOption = selectedItemExtension == FileExtensions.CSharp
+ ? LanguageOption.GenerateCSharpCode
+ : LanguageOption.GenerateVBCode;
+ var objectContext = DbContextPackage.GetObjectContext(context);
+ var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
+
+ OptimizeContextCore(languageOption, contextType.Name, mappingCollection);
+ }
+ catch (Exception ex)
+ {
+ _package.LogError(Strings.Optimize_ContextError(contextType.Name), ex);
+ }
+ }
+
+ public void OptimizeEdmx(string inputPath)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(inputPath));
+
+ var baseFileName = Path.GetFileNameWithoutExtension(inputPath);
+
+ try
+ {
+ var project = _package.DTE2.SelectedItems.Item(1).ProjectItem.ContainingProject;
+ var languageOption = project.CodeModel.Language == CodeModelLanguageConstants.vsCMLanguageCSharp
+ ? LanguageOption.GenerateCSharpCode
+ : LanguageOption.GenerateVBCode;
+ var mappingCollection = new EdmxUtility(inputPath).GetMappingCollection();
+
+ OptimizeContextCore(languageOption, baseFileName, mappingCollection);
+ }
+ catch (Exception ex)
+ {
+ _package.LogError(Strings.Optimize_EdmxError(baseFileName), ex);
+ }
+ }
+
+ private void OptimizeContextCore(LanguageOption languageOption, string baseFileName, StorageMappingItemCollection mappingCollection)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(baseFileName));
+ Contract.Requires(mappingCollection != null);
+
+ var progressTimer = new Timer { Interval = 1000 };
+
+ try
+ {
+ var selectedItem = _package.DTE2.SelectedItems.Item(1);
+ var selectedItemPath = (string)selectedItem.ProjectItem.Properties.Item("FullPath").Value;
+ var viewGenerator = new EntityViewGenerator(languageOption);
+ var viewsFileName = baseFileName
+ + ".Views"
+ + ((languageOption == LanguageOption.GenerateCSharpCode)
+ ? FileExtensions.CSharp
+ : FileExtensions.VisualBasic);
+ var viewsPath = Path.Combine(
+ Path.GetDirectoryName(selectedItemPath),
+ viewsFileName);
+
+ _package.DTE2.SourceControl.CheckOutItemIfNeeded(viewsPath);
+
+ var progress = 1;
+ progressTimer.Tick += (sender, e) =>
+ {
+ _package.DTE2.StatusBar.Progress(true, string.Empty, progress, 100);
+ progress = progress == 100 ? 1 : progress + 1;
+ _package.DTE2.StatusBar.Text = Strings.Optimize_Begin(baseFileName);
+ };
+
+ progressTimer.Start();
+
+ Task.Factory.StartNew(
+ () =>
+ {
+ var errors = viewGenerator.GenerateViews(mappingCollection, viewsPath);
+ errors.HandleErrors(Strings.Optimize_SchemaError(baseFileName));
+ })
+ .ContinueWith(
+ t =>
+ {
+ progressTimer.Stop();
+ _package.DTE2.StatusBar.Progress(false);
+
+ if (t.IsFaulted)
+ {
+ _package.LogError(Strings.Optimize_Error(baseFileName), t.Exception);
+
+ return;
+ }
+
+ selectedItem.ProjectItem.ContainingProject.ProjectItems.AddFromFile(viewsPath);
+ _package.DTE2.ItemOperations.OpenFile(viewsPath);
+
+ _package.DTE2.StatusBar.Text = Strings.Optimize_End(baseFileName, Path.GetFileName(viewsPath));
+ },
+ TaskScheduler.FromCurrentSynchronizationContext());
+ }
+ catch
+ {
+ progressTimer.Stop();
+ _package.DTE2.StatusBar.Progress(false);
+
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Handlers/ReverseEngineerCodeFirstHandler.cs b/src/PowerTools/Handlers/ReverseEngineerCodeFirstHandler.cs
new file mode 100644
index 00000000..67f4c18c
--- /dev/null
+++ b/src/PowerTools/Handlers/ReverseEngineerCodeFirstHandler.cs
@@ -0,0 +1,364 @@
+namespace Microsoft.DbContextPackage.Handlers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Configuration;
+ using System.Data.Common;
+ using System.Data.Entity.Design;
+ using System.Data.Entity.Design.PluralizationServices;
+ using System.Data.Metadata.Edm;
+ using System.Data.SqlClient;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using System.Xml;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+ using Microsoft.VisualStudio.Data.Core;
+ using Microsoft.VisualStudio.Data.Services;
+ using Microsoft.VisualStudio.Shell;
+ using Project = EnvDTE.Project;
+
+ internal class ReverseEngineerCodeFirstHandler
+ {
+ private static readonly IEnumerable _storeMetadataFilters = new[]
+ {
+ new EntityStoreSchemaFilterEntry(null, null, "EdmMetadata", EntityStoreSchemaFilterObjectTypes.Table, EntityStoreSchemaFilterEffect.Exclude),
+ new EntityStoreSchemaFilterEntry(null, null, "__MigrationHistory", EntityStoreSchemaFilterObjectTypes.Table, EntityStoreSchemaFilterEffect.Exclude)
+ };
+ private readonly DbContextPackage _package;
+
+ public ReverseEngineerCodeFirstHandler(DbContextPackage package)
+ {
+ Contract.Requires(package != null);
+
+ _package = package;
+ }
+
+ public void ReverseEngineerCodeFirst(Project project)
+ {
+ Contract.Requires(project != null);
+
+ try
+ {
+ // Show dialog with SqlClient selected by default
+ var dialogFactory = _package.GetService();
+ var dialog = dialogFactory.CreateConnectionDialog();
+ dialog.AddAllSources();
+ dialog.SelectedSource = new Guid("067ea0d9-ba62-43f7-9106-34930c60c528");
+ var dialogResult = dialog.ShowDialog(connect: true);
+
+ if (dialogResult != null)
+ {
+ // Find connection string and provider
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_LoadSchema;
+ var connection = (DbConnection)dialogResult.GetLockedProviderObject();
+ var connectionString = connection.ConnectionString;
+ var providerManager = (IVsDataProviderManager)Package.GetGlobalService(typeof(IVsDataProviderManager));
+ IVsDataProvider dp;
+ providerManager.Providers.TryGetValue(dialogResult.Provider, out dp);
+ var providerInvariant = (string)dp.GetProperty("InvariantName");
+
+ // Load store schema
+ var storeGenerator = new EntityStoreSchemaGenerator(providerInvariant, connectionString, "dbo");
+ storeGenerator.GenerateForeignKeyProperties = true;
+ var errors = storeGenerator.GenerateStoreMetadata(_storeMetadataFilters).Where(e => e.Severity == EdmSchemaErrorSeverity.Error);
+ errors.HandleErrors(Strings.ReverseEngineer_SchemaError);
+
+ // Generate default mapping
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_GenerateMapping;
+ var contextName = connection.Database.Replace(" ", string.Empty).Replace(".", string.Empty) + "Context";
+ var modelGenerator = new EntityModelSchemaGenerator(storeGenerator.EntityContainer, "DefaultNamespace", contextName);
+ modelGenerator.PluralizationService = PluralizationService.CreateService(new CultureInfo("en"));
+ modelGenerator.GenerateForeignKeyProperties = true;
+ modelGenerator.GenerateMetadata();
+
+ // Pull out info about types to be generated
+ var entityTypes = modelGenerator.EdmItemCollection.OfType().ToArray();
+ var mappings = new EdmMapping(modelGenerator, storeGenerator.StoreItemCollection);
+
+ // Find the project to add the code to
+ var vsProject = (VSLangProj.VSProject)project.Object;
+ var projectDirectory = new FileInfo(project.FileName).Directory;
+ var projectNamespace = (string)project.Properties.Item("RootNamespace").Value;
+ var references = vsProject.References.Cast();
+
+ if (!references.Any(r => r.Name == "EntityFramework"))
+ {
+ // Add EF References
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_InstallEntityFramework;
+
+ try
+ {
+ project.InstallPackage("EntityFramework");
+ }
+ catch (Exception ex)
+ {
+ _package.LogError(Strings.ReverseEngineer_InstallEntityFrameworkError, ex);
+ }
+ }
+
+ // Generate Entity Classes and Mappings
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_GenerateClasses;
+ var templateProcessor = new TemplateProcessor(project);
+ var modelsNamespace = projectNamespace + ".Models";
+ var modelsDirectory = Path.Combine(projectDirectory.FullName, "Models");
+ var mappingNamespace = modelsNamespace + ".Mapping";
+ var mappingDirectory = Path.Combine(modelsDirectory, "Mapping");
+ var entityFrameworkVersion = GetEntityFrameworkVersion(references);
+
+ foreach (var entityType in entityTypes)
+ {
+ // Generate the code file
+ var entityHost = new EfTextTemplateHost
+ {
+ EntityType = entityType,
+ EntityContainer = modelGenerator.EntityContainer,
+ Namespace = modelsNamespace,
+ ModelsNamespace = modelsNamespace,
+ MappingNamespace = mappingNamespace,
+ EntityFrameworkVersion = entityFrameworkVersion,
+ TableSet = mappings.EntityMappings[entityType].Item1,
+ PropertyToColumnMappings = mappings.EntityMappings[entityType].Item2,
+ ManyToManyMappings = mappings.ManyToManyMappings
+ };
+ var entityContents = templateProcessor.Process(Templates.EntityTemplate, entityHost);
+
+ var filePath = Path.Combine(modelsDirectory, entityType.Name + entityHost.FileExtension);
+ project.AddNewFile(filePath, entityContents);
+
+ var mappingHost = new EfTextTemplateHost
+ {
+ EntityType = entityType,
+ EntityContainer = modelGenerator.EntityContainer,
+ Namespace = mappingNamespace,
+ ModelsNamespace = modelsNamespace,
+ MappingNamespace = mappingNamespace,
+ EntityFrameworkVersion = entityFrameworkVersion,
+ TableSet = mappings.EntityMappings[entityType].Item1,
+ PropertyToColumnMappings = mappings.EntityMappings[entityType].Item2,
+ ManyToManyMappings = mappings.ManyToManyMappings
+ };
+ var mappingContents = templateProcessor.Process(Templates.MappingTemplate, mappingHost);
+
+ var mappingFilePath = Path.Combine(mappingDirectory, entityType.Name + "Map" + mappingHost.FileExtension);
+ project.AddNewFile(mappingFilePath, mappingContents);
+ }
+
+ // Generate Context
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_GenerateContext;
+ var contextHost = new EfTextTemplateHost
+ {
+ EntityContainer = modelGenerator.EntityContainer,
+ Namespace = modelsNamespace,
+ ModelsNamespace = modelsNamespace,
+ MappingNamespace = mappingNamespace,
+ EntityFrameworkVersion = entityFrameworkVersion
+ };
+ var contextContents = templateProcessor.Process(Templates.ContextTemplate, contextHost);
+
+ var contextFilePath = Path.Combine(modelsDirectory, modelGenerator.EntityContainer.Name + contextHost.FileExtension);
+ var contextItem = project.AddNewFile(contextFilePath, contextContents);
+ AddConnectionStringToConfigFile(project, connectionString, providerInvariant, modelGenerator.EntityContainer.Name);
+
+ if (contextItem != null)
+ {
+ // Open context class when done
+ _package.DTE2.ItemOperations.OpenFile(contextFilePath);
+ }
+
+ _package.DTE2.StatusBar.Text = Strings.ReverseEngineer_Complete;
+ }
+ }
+ catch (Exception exception)
+ {
+ _package.LogError(Strings.ReverseEngineer_Error, exception);
+ }
+ }
+
+ private static Version GetEntityFrameworkVersion(IEnumerable references)
+ {
+ var entityFrameworkReference = references.FirstOrDefault(r => r.Name == "EntityFramework");
+
+ if (entityFrameworkReference != null)
+ {
+ return new Version(entityFrameworkReference.Version);
+ }
+
+ return null;
+ }
+
+ private static void AddConnectionStringToConfigFile(Project project, string connectionString, string providerInvariant, string connectionStringName)
+ {
+ Contract.Requires(project != null);
+ Contract.Requires(!string.IsNullOrWhiteSpace(providerInvariant));
+ Contract.Requires(!string.IsNullOrWhiteSpace(connectionStringName));
+
+ // Find App.config or Web.config
+ var configFilePath = Path.Combine(
+ project.GetProjectDir(),
+ project.IsWebProject()
+ ? "Web.config"
+ : "App.config");
+
+ // Either load up the existing file or create a blank file
+ var config = ConfigurationManager.OpenMappedExeConfiguration(
+ new ExeConfigurationFileMap { ExeConfigFilename = configFilePath },
+ ConfigurationUserLevel.None);
+
+ // Find or create the connectionStrings section
+ var connectionStringSettings = config.ConnectionStrings
+ .ConnectionStrings
+ .Cast()
+ .FirstOrDefault(css => css.Name == connectionStringName);
+
+ if (connectionStringSettings == null)
+ {
+ connectionStringSettings = new ConnectionStringSettings
+ {
+ Name = connectionStringName
+ };
+
+ config.ConnectionStrings
+ .ConnectionStrings
+ .Add(connectionStringSettings);
+ }
+
+ // Add in the new connection string
+ connectionStringSettings.ProviderName = providerInvariant;
+ connectionStringSettings.ConnectionString = FixUpConnectionString(connectionString, providerInvariant);
+
+ project.DTE.SourceControl.CheckOutItemIfNeeded(configFilePath);
+ config.Save();
+
+ // Add any new file to the project
+ project.ProjectItems.AddFromFile(configFilePath);
+ }
+
+ private static string FixUpConnectionString(string connectionString, string providerName)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(providerName));
+
+ if (providerName != "System.Data.SqlClient")
+ {
+ return connectionString;
+ }
+
+ var builder = new SqlConnectionStringBuilder(connectionString)
+ {
+ MultipleActiveResultSets = true
+ };
+ builder.Remove("Pooling");
+
+ return builder.ToString();
+ }
+
+ private class EdmMapping
+ {
+ public EdmMapping(EntityModelSchemaGenerator mcGenerator, StoreItemCollection store)
+ {
+ Contract.Requires(mcGenerator != null);
+ Contract.Requires(store != null);
+
+ // Pull mapping xml out
+ var mappingDoc = new XmlDocument();
+ var mappingXml = new StringBuilder();
+
+ using (var textWriter = new StringWriter(mappingXml))
+ {
+ mcGenerator.WriteStorageMapping(new XmlTextWriter(textWriter));
+ }
+
+ mappingDoc.LoadXml(mappingXml.ToString());
+
+ var entitySets = mcGenerator.EntityContainer.BaseEntitySets.OfType();
+ var associationSets = mcGenerator.EntityContainer.BaseEntitySets.OfType();
+ var tableSets = store.GetItems().Single().BaseEntitySets.OfType();
+
+ this.EntityMappings = BuildEntityMappings(mappingDoc, entitySets, tableSets);
+ this.ManyToManyMappings = BuildManyToManyMappings(mappingDoc, associationSets, tableSets);
+ }
+
+ public Dictionary>> EntityMappings { get; set; }
+
+ public Dictionary>>> ManyToManyMappings { get; set; }
+
+ private static Dictionary>>> BuildManyToManyMappings(XmlDocument mappingDoc, IEnumerable associationSets, IEnumerable tableSets)
+ {
+ Contract.Requires(mappingDoc != null);
+ Contract.Requires(associationSets != null);
+ Contract.Requires(tableSets != null);
+
+ // Build mapping for each association
+ var mappings = new Dictionary>>>();
+ var namespaceManager = new XmlNamespaceManager(mappingDoc.NameTable);
+ namespaceManager.AddNamespace("ef", mappingDoc.ChildNodes[0].NamespaceURI);
+ foreach (var associationSet in associationSets.Where(a => !a.ElementType.AssociationEndMembers.Where(e => e.RelationshipMultiplicity != RelationshipMultiplicity.Many).Any()))
+ {
+ var setMapping = mappingDoc.SelectSingleNode(string.Format("//ef:AssociationSetMapping[@Name=\"{0}\"]", associationSet.Name), namespaceManager);
+ var tableName = setMapping.Attributes["StoreEntitySet"].Value;
+ var tableSet = tableSets.Single(s => s.Name == tableName);
+
+ var endMappings = new Dictionary>();
+ foreach (var end in associationSet.AssociationSetEnds)
+ {
+ var propertyToColumnMappings = new Dictionary();
+ var endMapping = setMapping.SelectSingleNode(string.Format("./ef:EndProperty[@Name=\"{0}\"]", end.Name), namespaceManager);
+ foreach (XmlNode fk in endMapping.ChildNodes)
+ {
+ var propertyName = fk.Attributes["Name"].Value;
+ var property = end.EntitySet.ElementType.Properties[propertyName];
+ var columnName = fk.Attributes["ColumnName"].Value;
+ propertyToColumnMappings.Add(property, columnName);
+ }
+
+ endMappings.Add(end.CorrespondingAssociationEndMember, propertyToColumnMappings);
+ }
+
+ mappings.Add(associationSet.ElementType, Tuple.Create(tableSet, endMappings));
+ }
+
+ return mappings;
+ }
+
+ private static Dictionary>> BuildEntityMappings(XmlDocument mappingDoc, IEnumerable entitySets, IEnumerable tableSets)
+ {
+ Contract.Requires(mappingDoc != null);
+ Contract.Requires(entitySets != null);
+ Contract.Requires(tableSets != null);
+
+ // Build mapping for each type
+ var mappings = new Dictionary>>();
+ var namespaceManager = new XmlNamespaceManager(mappingDoc.NameTable);
+ namespaceManager.AddNamespace("ef", mappingDoc.ChildNodes[0].NamespaceURI);
+ foreach (var entitySet in entitySets)
+ {
+ // Post VS2010 builds use a different structure for mapping
+ var setMapping = mappingDoc.ChildNodes[0].NamespaceURI == "http://schemas.microsoft.com/ado/2009/11/mapping/cs"
+ ? mappingDoc.SelectSingleNode(string.Format("//ef:EntitySetMapping[@Name=\"{0}\"]/ef:EntityTypeMapping/ef:MappingFragment", entitySet.Name), namespaceManager)
+ : mappingDoc.SelectSingleNode(string.Format("//ef:EntitySetMapping[@Name=\"{0}\"]", entitySet.Name), namespaceManager);
+
+ var tableName = setMapping.Attributes["StoreEntitySet"].Value;
+ var tableSet = tableSets.Single(s => s.Name == tableName);
+
+ var propertyMappings = new Dictionary();
+ foreach (var prop in entitySet.ElementType.Properties)
+ {
+ var propMapping = setMapping.SelectSingleNode(string.Format("./ef:ScalarProperty[@Name=\"{0}\"]", prop.Name), namespaceManager);
+ var columnName = propMapping.Attributes["ColumnName"].Value;
+ var columnProp = tableSet.ElementType.Properties[columnName];
+
+ propertyMappings.Add(prop, columnProp);
+ }
+
+ mappings.Add(entitySet.ElementType, Tuple.Create(tableSet, propertyMappings));
+ }
+
+ return mappings;
+ }
+ }
+ }
+}
diff --git a/src/PowerTools/Handlers/ViewContextHandler.cs b/src/PowerTools/Handlers/ViewContextHandler.cs
new file mode 100644
index 00000000..14123813
--- /dev/null
+++ b/src/PowerTools/Handlers/ViewContextHandler.cs
@@ -0,0 +1,72 @@
+namespace Microsoft.DbContextPackage.Handlers
+{
+ using System;
+ using System.ComponentModel.Design;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Reflection;
+ using System.Xml;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+
+ internal class ViewContextHandler
+ {
+ private readonly DbContextPackage _package;
+
+ public ViewContextHandler(DbContextPackage package)
+ {
+ Contract.Requires(package != null);
+
+ _package = package;
+ }
+
+ public void ViewContext(MenuCommand menuCommand, dynamic context, Type systemContextType)
+ {
+ Contract.Requires(menuCommand != null);
+ Contract.Requires(systemContextType != null);
+
+ Type contextType = context.GetType();
+
+ try
+ {
+ var filePath = Path.Combine(
+ Path.GetTempPath(),
+ contextType.Name
+ + (menuCommand.CommandID.ID == PkgCmdIDList.cmdidViewEntityDataModel
+ ? FileExtensions.EntityDataModel
+ : FileExtensions.Xml));
+
+ if (File.Exists(filePath))
+ {
+ File.SetAttributes(filePath, FileAttributes.Normal);
+ }
+
+ using (var fileStream = File.Create(filePath))
+ {
+ using (var xmlWriter = XmlWriter.Create(fileStream, new XmlWriterSettings { Indent = true }))
+ {
+ var edmxWriterType = systemContextType.Assembly.GetType("System.Data.Entity.Infrastructure.EdmxWriter");
+
+ if (edmxWriterType != null)
+ {
+ edmxWriterType.InvokeMember(
+ "WriteEdmx",
+ BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
+ null,
+ null,
+ new object[] { context, xmlWriter });
+ }
+ }
+ }
+
+ _package.DTE2.ItemOperations.OpenFile(filePath);
+
+ File.SetAttributes(filePath, FileAttributes.ReadOnly);
+ }
+ catch (Exception exception)
+ {
+ _package.LogError(Strings.ViewContextError(contextType.Name), exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Handlers/ViewDdlHandler.cs b/src/PowerTools/Handlers/ViewDdlHandler.cs
new file mode 100644
index 00000000..d09102a3
--- /dev/null
+++ b/src/PowerTools/Handlers/ViewDdlHandler.cs
@@ -0,0 +1,48 @@
+namespace Microsoft.DbContextPackage.Handlers
+{
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.DbContextPackage.Utilities;
+
+ internal class ViewDdlHandler
+ {
+ private readonly DbContextPackage _package;
+
+ public ViewDdlHandler(DbContextPackage package)
+ {
+ Contract.Requires(package != null);
+
+ _package = package;
+ }
+
+ public void ViewDdl(dynamic context)
+ {
+ Type contextType = context.GetType();
+
+ try
+ {
+ var filePath = Path.Combine(
+ Path.GetTempPath(),
+ contextType.Name + FileExtensions.Sql);
+
+ if (File.Exists(filePath))
+ {
+ File.SetAttributes(filePath, FileAttributes.Normal);
+ }
+
+ var objectContext = DbContextPackage.GetObjectContext(context);
+
+ File.WriteAllText(filePath, objectContext.CreateDatabaseScript());
+ File.SetAttributes(filePath, FileAttributes.ReadOnly);
+
+ _package.DTE2.ItemOperations.OpenFile(filePath);
+ }
+ catch (Exception exception)
+ {
+ _package.LogError(Strings.ViewDdlError(contextType.Name), exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/License.rtf b/src/PowerTools/License.rtf
new file mode 100644
index 00000000..9a0bd19a
Binary files /dev/null and b/src/PowerTools/License.rtf differ
diff --git a/src/PowerTools/PkgCmdID.cs b/src/PowerTools/PkgCmdID.cs
new file mode 100644
index 00000000..4a7c66ca
--- /dev/null
+++ b/src/PowerTools/PkgCmdID.cs
@@ -0,0 +1,14 @@
+// PkgCmdID.cs
+// MUST match PkgCmdID.h
+namespace Microsoft.DbContextPackage
+{
+ internal static class PkgCmdIDList
+ {
+ public const uint cmdidViewEntityDataModel = 0x100;
+ public const uint cmdidViewEntityDataModelXml = 0x200;
+ public const uint cmdidPrecompileEntityDataModelViews = 0x300;
+ public const uint cmdidViewEntityModelDdl = 0x400;
+ public const uint cmdidReverseEngineerCodeFirst = 0x001;
+ public const uint cmdidCustomizeReverseEngineerTemplates = 0x005;
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/PowerTools.csproj b/src/PowerTools/PowerTools.csproj
new file mode 100644
index 00000000..dc609117
--- /dev/null
+++ b/src/PowerTools/PowerTools.csproj
@@ -0,0 +1,261 @@
+
+
+
+ 11.0
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}
+ Library
+ Properties
+ Microsoft.DbContextPackage
+ EFPowerTools
+ v4.0
+ Program
+ $(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\11.0@InstallDir)devenv.exe
+ /rootsuffix Exp
+ 0
+ ..\Strict.ruleset
+ ..\..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;CODE_ANALYSIS;CONTRACTS_FULL
+ prompt
+ 4
+ True
+ False
+ True
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ True
+ False
+ False
+ False
+ True
+ False
+ False
+ False
+ EFPowerTools
+ Microsoft.DbContextPackage.Utilities.RuntimeFailureMethods
+
+
+
+
+ False
+ Full
+ DoNotBuild
+ 0
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;CODE_ANALYSIS;CONTRACTS_FULL
+ prompt
+ 4
+ True
+ True
+ True
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ False
+ True
+ False
+ False
+ False
+ True
+ False
+ False
+ False
+ EFPowerTools
+ Microsoft.DbContextPackage.Utilities.RuntimeFailureMethods
+
+
+
+
+ False
+ Full
+ DoNotBuild
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+ True
+
+
+
+
+ {80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}
+ 8
+ 0
+ 0
+ primary
+ False
+ False
+
+
+ {1A31287A-4D7D-413E-8E32-3B374931BD89}
+ 8
+ 0
+ 0
+ primary
+ False
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.tt
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+ true
+
+
+ Always
+ true
+
+
+ Always
+ true
+
+
+
+
+
+
+
+ Designer
+
+
+ true
+ VSPackage
+ Designer
+
+
+
+
+ TextTemplatingFileGenerator
+ Resources.cs
+ Microsoft.DbContextPackage
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+ Menus.ctmenu
+
+
+
+
+
+
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/PowerTools/Properties/AssemblyInfo.cs b/src/PowerTools/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..4ca92869
--- /dev/null
+++ b/src/PowerTools/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DbContextPackage")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("DbContextPackage")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: CLSCompliant(false)]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+
+
diff --git a/src/PowerTools/Properties/InternalsVisibleTo.cs b/src/PowerTools/Properties/InternalsVisibleTo.cs
new file mode 100644
index 00000000..3e937e3e
--- /dev/null
+++ b/src/PowerTools/Properties/InternalsVisibleTo.cs
@@ -0,0 +1,10 @@
+#if !INTERNALS_INVISIBLE
+
+using System.Runtime.CompilerServices;
+
+// For Moq
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
+
+[assembly: InternalsVisibleTo("EFPowerTools.Test")]
+
+#endif
\ No newline at end of file
diff --git a/src/PowerTools/Properties/Resources.cs b/src/PowerTools/Properties/Resources.cs
new file mode 100644
index 00000000..e5c269ee
--- /dev/null
+++ b/src/PowerTools/Properties/Resources.cs
@@ -0,0 +1,427 @@
+//
+namespace Microsoft.DbContextPackage.Resources
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Globalization;
+ using System.Resources;
+ using System.Threading;
+
+ ///
+ /// Strongly-typed and parameterized string resources.
+ ///
+ [GeneratedCode("Resources.tt", "1.0.0.0")]
+ internal static class Strings
+ {
+ ///
+ /// A string like "An error occurred while adding custom templates. See the Output window for details."
+ ///
+ internal static string AddTemplatesError
+ {
+ get { return EntityRes.GetString(EntityRes.AddTemplatesError); }
+ }
+
+ ///
+ /// A string like "The argument '{0}' cannot be null, empty or contain only white space."
+ ///
+ internal static string ArgumentIsNullOrWhitespace(object p0)
+ {
+ return EntityRes.GetString(EntityRes.ArgumentIsNullOrWhitespace, p0);
+ }
+
+ ///
+ /// A string like "Build failed. Unable to discover a DbContext class."
+ ///
+ internal static string BuildFailed
+ {
+ get { return EntityRes.GetString(EntityRes.BuildFailed); }
+ }
+
+ ///
+ /// A string like "An error occurred while trying to instantiate the DbContext type {0}. See the Output window for details."
+ ///
+ internal static string CreateContextFailed(object p0)
+ {
+ return EntityRes.GetString(EntityRes.CreateContextFailed, p0);
+ }
+
+ ///
+ /// A string like "The Entity Data Model '{0}' has one or more schema errors inside the {1} section."
+ ///
+ internal static string EdmSchemaError(object p0, object p1)
+ {
+ return EntityRes.GetString(EntityRes.EdmSchemaError, p0, p1);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to load the configuration file. See the Output window for details."
+ ///
+ internal static string LoadConfigFailed
+ {
+ get { return EntityRes.GetString(EntityRes.LoadConfigFailed); }
+ }
+
+ ///
+ /// A string like "A constructible type deriving from DbContext could not be found in the selected file."
+ ///
+ internal static string NoContext
+ {
+ get { return EntityRes.GetString(EntityRes.NoContext); }
+ }
+
+ ///
+ /// A string like "Performing EDM view pre-compilation for: {0}. This operation may take several minutes."
+ ///
+ internal static string Optimize_Begin(object p0)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_Begin, p0);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to initialize view generation for the DbContext type {0}. See the Output window for details."
+ ///
+ internal static string Optimize_ContextError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_ContextError, p0);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to initialize view generation for the Entity Data Model {0}. See the Output window for details."
+ ///
+ internal static string Optimize_EdmxError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_EdmxError, p0);
+ }
+
+ ///
+ /// A string like "Finished EDM view pre-compilation for: {0}! See file: {1}."
+ ///
+ internal static string Optimize_End(object p0, object p1)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_End, p0, p1);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to generate views for {0}. See the Output window for details."
+ ///
+ internal static string Optimize_Error(object p0)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_Error, p0);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to generate views for {0}."
+ ///
+ internal static string Optimize_SchemaError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.Optimize_SchemaError, p0);
+ }
+
+ ///
+ /// A string like "The precondition '{0}' failed. {1}"
+ ///
+ internal static string PreconditionFailed(object p0, object p1)
+ {
+ return EntityRes.GetString(EntityRes.PreconditionFailed, p0, p1);
+ }
+
+ ///
+ /// A string like "One or more errors occurred while processing template '{0}'."
+ ///
+ internal static string ProcessTemplateError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.ProcessTemplateError, p0);
+ }
+
+ ///
+ /// A string like "Reverse engineer complete."
+ ///
+ internal static string ReverseEngineer_Complete
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_Complete); }
+ }
+
+ ///
+ /// A string like "An error occurred while reverse engineering Code First. See the Output window for details."
+ ///
+ internal static string ReverseEngineer_Error
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_Error); }
+ }
+
+ ///
+ /// A string like "Generating entity and mapping classes..."
+ ///
+ internal static string ReverseEngineer_GenerateClasses
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_GenerateClasses); }
+ }
+
+ ///
+ /// A string like "Generating context..."
+ ///
+ internal static string ReverseEngineer_GenerateContext
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_GenerateContext); }
+ }
+
+ ///
+ /// A string like "Generating default mapping..."
+ ///
+ internal static string ReverseEngineer_GenerateMapping
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_GenerateMapping); }
+ }
+
+ ///
+ /// A string like "Installing EntityFramework package..."
+ ///
+ internal static string ReverseEngineer_InstallEntityFramework
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_InstallEntityFramework); }
+ }
+
+ ///
+ /// A string like "An error occurred while trying to install the EntityFramework package. See the Output window for details."
+ ///
+ internal static string ReverseEngineer_InstallEntityFrameworkError
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_InstallEntityFrameworkError); }
+ }
+
+ ///
+ /// A string like "Loading schema information..."
+ ///
+ internal static string ReverseEngineer_LoadSchema
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_LoadSchema); }
+ }
+
+ ///
+ /// A string like "One or more errors occurred while loading schema information."
+ ///
+ internal static string ReverseEngineer_SchemaError
+ {
+ get { return EntityRes.GetString(EntityRes.ReverseEngineer_SchemaError); }
+ }
+
+ ///
+ /// A string like "Cannot find processor for directive '{0}'."
+ ///
+ internal static string UnknownDirectiveProcessor(object p0)
+ {
+ return EntityRes.GetString(EntityRes.UnknownDirectiveProcessor, p0);
+ }
+
+ ///
+ /// A string like "You are using a version of the Entity Framework that is not supported by the Power Tools. Please upgrade to Entity Framework 4.2 or later."
+ ///
+ internal static string UnsupportedVersion
+ {
+ get { return EntityRes.GetString(EntityRes.UnsupportedVersion); }
+ }
+
+ ///
+ /// A string like "An error occurred while trying to build the model for {0}. See the Output window for details."
+ ///
+ internal static string ViewContextError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.ViewContextError, p0);
+ }
+
+ ///
+ /// A string like "An error occurred while trying to build the model for {0}. See the Output window for details."
+ ///
+ internal static string ViewDdlError(object p0)
+ {
+ return EntityRes.GetString(EntityRes.ViewDdlError, p0);
+ }
+ }
+
+ ///
+ /// Strongly-typed and parameterized exception factory.
+ ///
+ [GeneratedCode("Resources.tt", "1.0.0.0")]
+ internal static class Error
+ {
+ ///
+ /// ArgumentException with message like "The argument '{0}' cannot be null, empty or contain only white space."
+ ///
+ internal static Exception ArgumentIsNullOrWhitespace(object p0)
+ {
+ return new ArgumentException(Strings.ArgumentIsNullOrWhitespace(p0));
+ }
+
+ ///
+ /// ArgumentException with message like "The precondition '{0}' failed. {1}"
+ ///
+ internal static Exception PreconditionFailed(object p0, object p1)
+ {
+ return new ArgumentException(Strings.PreconditionFailed(p0, p1));
+ }
+
+ ///
+ /// InvalidOperationException with message like "Cannot find processor for directive '{0}'."
+ ///
+ internal static Exception UnknownDirectiveProcessor(object p0)
+ {
+ return new InvalidOperationException(Strings.UnknownDirectiveProcessor(p0));
+ }
+
+ ///
+ /// InvalidOperationException with message like "You are using a version of the Entity Framework that is not supported by the Power Tools. Please upgrade to Entity Framework 4.2 or later."
+ ///
+ internal static Exception UnsupportedVersion()
+ {
+ return new InvalidOperationException(Strings.UnsupportedVersion);
+ }
+ ///
+ /// The exception that is thrown when a null reference (Nothing in Visual Basic) is passed to a method that does not accept it as a valid argument.
+ ///
+ internal static Exception ArgumentNull(string paramName)
+ {
+ return new ArgumentNullException(paramName);
+ }
+
+ ///
+ /// The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.
+ ///
+ internal static Exception ArgumentOutOfRange(string paramName)
+ {
+ return new ArgumentOutOfRangeException(paramName);
+ }
+
+ ///
+ /// The exception that is thrown when the author has yet to implement the logic at this point in the program. This can act as an exception based TODO tag.
+ ///
+ internal static Exception NotImplemented()
+ {
+ return new NotImplementedException();
+ }
+
+ ///
+ /// The exception that is thrown when an invoked method is not supported, or when there is an attempt to read, seek, or write to a stream that does not support the invoked functionality.
+ ///
+ internal static Exception NotSupported()
+ {
+ return new NotSupportedException();
+ }
+ }
+
+ ///
+ /// AutoGenerated resource class. Usage:
+ ///
+ /// string s = EntityRes.GetString(EntityRes.MyIdenfitier);
+ ///
+ [GeneratedCode("Resources.tt", "1.0.0.0")]
+ internal sealed class EntityRes
+ {
+ internal const string AddTemplatesError = "AddTemplatesError";
+ internal const string ArgumentIsNullOrWhitespace = "ArgumentIsNullOrWhitespace";
+ internal const string BuildFailed = "BuildFailed";
+ internal const string CreateContextFailed = "CreateContextFailed";
+ internal const string EdmSchemaError = "EdmSchemaError";
+ internal const string LoadConfigFailed = "LoadConfigFailed";
+ internal const string NoContext = "NoContext";
+ internal const string Optimize_Begin = "Optimize_Begin";
+ internal const string Optimize_ContextError = "Optimize_ContextError";
+ internal const string Optimize_EdmxError = "Optimize_EdmxError";
+ internal const string Optimize_End = "Optimize_End";
+ internal const string Optimize_Error = "Optimize_Error";
+ internal const string Optimize_SchemaError = "Optimize_SchemaError";
+ internal const string PreconditionFailed = "PreconditionFailed";
+ internal const string ProcessTemplateError = "ProcessTemplateError";
+ internal const string ReverseEngineer_Complete = "ReverseEngineer_Complete";
+ internal const string ReverseEngineer_Error = "ReverseEngineer_Error";
+ internal const string ReverseEngineer_GenerateClasses = "ReverseEngineer_GenerateClasses";
+ internal const string ReverseEngineer_GenerateContext = "ReverseEngineer_GenerateContext";
+ internal const string ReverseEngineer_GenerateMapping = "ReverseEngineer_GenerateMapping";
+ internal const string ReverseEngineer_InstallEntityFramework = "ReverseEngineer_InstallEntityFramework";
+ internal const string ReverseEngineer_InstallEntityFrameworkError = "ReverseEngineer_InstallEntityFrameworkError";
+ internal const string ReverseEngineer_LoadSchema = "ReverseEngineer_LoadSchema";
+ internal const string ReverseEngineer_SchemaError = "ReverseEngineer_SchemaError";
+ internal const string UnknownDirectiveProcessor = "UnknownDirectiveProcessor";
+ internal const string UnsupportedVersion = "UnsupportedVersion";
+ internal const string ViewContextError = "ViewContextError";
+ internal const string ViewDdlError = "ViewDdlError";
+
+ static EntityRes loader = null;
+ ResourceManager resources;
+
+ private EntityRes()
+ {
+ resources = new ResourceManager("Microsoft.DbContextPackage.Properties.Resources", typeof(Microsoft.DbContextPackage.DbContextPackage).Assembly);
+ }
+
+ private static EntityRes GetLoader()
+ {
+ if (loader == null)
+ {
+ EntityRes sr = new EntityRes();
+ Interlocked.CompareExchange(ref loader, sr, null);
+ }
+ return loader;
+ }
+
+ private static CultureInfo Culture
+ {
+ get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; }
+ }
+
+ public static ResourceManager Resources
+ {
+ get
+ {
+ return GetLoader().resources;
+ }
+ }
+
+ public static string GetString(string name, params object[] args)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ string res = sys.resources.GetString(name, EntityRes.Culture);
+
+ if (args != null && args.Length > 0)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ String value = args[i] as String;
+ if (value != null && value.Length > 1024)
+ {
+ args[i] = value.Substring(0, 1024 - 3) + "...";
+ }
+ }
+ return String.Format(CultureInfo.CurrentCulture, res, args);
+ }
+ else
+ {
+ return res;
+ }
+ }
+
+ public static string GetString(string name)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ return sys.resources.GetString(name, EntityRes.Culture);
+ }
+
+ public static string GetString(string name, out bool usedFallback)
+ {
+ // always false for this version of gensr
+ usedFallback = false;
+ return GetString(name);
+ }
+
+ public static object GetObject(string name)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ return sys.resources.GetObject(name, EntityRes.Culture);
+ }
+ }
+}
diff --git a/src/PowerTools/Properties/Resources.resx b/src/PowerTools/Properties/Resources.resx
new file mode 100644
index 00000000..c8078e36
--- /dev/null
+++ b/src/PowerTools/Properties/Resources.resx
@@ -0,0 +1,208 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ An error occurred while adding custom templates. See the Output window for details.
+
+
+ The argument '{0}' cannot be null, empty or contain only white space.
+ ## ExceptionType=ArgumentException
+
+
+ Build failed. Unable to discover a DbContext class.
+
+
+ An error occurred while trying to instantiate the DbContext type {0}. See the Output window for details.
+
+
+ The Entity Data Model '{0}' has one or more schema errors inside the {1} section.
+
+
+ An error occurred while trying to load the configuration file. See the Output window for details.
+
+
+ A constructible type deriving from DbContext could not be found in the selected file.
+
+
+ Performing EDM view pre-compilation for: {0}. This operation may take several minutes.
+
+
+ An error occurred while trying to initialize view generation for the DbContext type {0}. See the Output window for details.
+
+
+ An error occurred while trying to initialize view generation for the Entity Data Model {0}. See the Output window for details.
+
+
+ Finished EDM view pre-compilation for: {0}! See file: {1}.
+
+
+ An error occurred while trying to generate views for {0}. See the Output window for details.
+
+
+ An error occurred while trying to generate views for {0}.
+
+
+ The precondition '{0}' failed. {1}
+ ## ExceptionType=ArgumentException
+
+
+ One or more errors occurred while processing template '{0}'.
+
+
+ Reverse engineer complete.
+
+
+ An error occurred while reverse engineering Code First. See the Output window for details.
+
+
+ Generating entity and mapping classes...
+
+
+ Generating context...
+
+
+ Generating default mapping...
+
+
+ Installing EntityFramework package...
+
+
+ An error occurred while trying to install the EntityFramework package. See the Output window for details.
+
+
+ Loading schema information...
+
+
+ One or more errors occurred while loading schema information.
+
+
+ Cannot find processor for directive '{0}'.
+ ## ExceptionType=InvalidOperationException
+
+
+ You are using a version of the Entity Framework that is not supported by the Power Tools. Please upgrade to Entity Framework 4.2 or later.
+ ## ExceptionType=InvalidOperationException
+
+
+ An error occurred while trying to build the model for {0}. See the Output window for details.
+
+
+ An error occurred while trying to build the model for {0}. See the Output window for details.
+
+
\ No newline at end of file
diff --git a/src/PowerTools/Properties/Resources.tt b/src/PowerTools/Properties/Resources.tt
new file mode 100644
index 00000000..32f65f8d
--- /dev/null
+++ b/src/PowerTools/Properties/Resources.tt
@@ -0,0 +1,234 @@
+<#@ template debug="true" hostspecific="true" language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ assembly name="System.Windows.Forms" #>
+<#@ import namespace="System" #>
+<#@ import namespace="System.Collections" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="System.IO" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Resources" #>
+<#@ import namespace="System.Text.RegularExpressions" #>
+<#@ output extension=".cs" #>
+<#
+
+var parameterMatcher = new Regex(@"\{(\d)\}");
+var lines = new List>();
+
+using (var resxReader = new ResXResourceReader(Path.ChangeExtension(Host.TemplateFile, "resx")))
+{
+ resxReader.UseResXDataNodes = true;
+
+ foreach (DictionaryEntry entry in resxReader)
+ {
+ var node = (ResXDataNode)entry.Value;
+ var value = (string)node.GetValue((System.ComponentModel.Design.ITypeResolutionService)null);
+
+ var matchedArgs
+ = parameterMatcher.Matches(value)
+ .Cast()
+ .Select(m => Convert.ToInt32(m.Groups[1].Value))
+ .ToArray();
+
+ var argGenerator
+ = new object[matchedArgs.Any() ? matchedArgs.Max() + 1 : 0];
+
+ lines.Add(Tuple.Create(
+ node.Name,
+ value,
+ node.Comment.StartsWith("## ExceptionType=") ? node.Comment.Substring(17) : null,
+ argGenerator.Any(),
+ string.Join(", ", argGenerator.Select((_, i) => "p" + i)),
+ "(" + string.Join(", ", argGenerator.Select((_, i) => "object p" + i)) + ")"
+ ));
+ }
+}
+
+string outputNamespace = Host.ResolveParameterValue("directiveId", "namespaceDirectiveProcessor", "namespaceHint") ?? string.Empty;
+#>
+//
+namespace <#= outputNamespace #>.Resources
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Globalization;
+ using System.Resources;
+ using System.Threading;
+
+ ///
+ /// Strongly-typed and parameterized string resources.
+ ///
+ [GeneratedCode("<#= Path.GetFileName(Host.TemplateFile) #>", "1.0.0.0")]
+ internal static class Strings
+ {<#
+ foreach (var line in lines)
+ {
+ #>
+
+ ///
+ /// A string like "<#= line.Item2 #>"
+ ///
+ internal static string <#= line.Item1 #><#= line.Item4 ? line.Item6 : string.Empty #>
+ {
+ <#
+ if (!line.Item4)
+ {
+ #>get { return EntityRes.GetString(EntityRes.<#= line.Item1 #>); }
+<#
+ }
+ else
+ {
+ #>return EntityRes.GetString(EntityRes.<#= line.Item1 #>, <#= line.Item5 #>);
+<#
+ }#>
+ }
+<#
+ }#>
+ }
+
+ ///
+ /// Strongly-typed and parameterized exception factory.
+ ///
+ [GeneratedCode("<#= Path.GetFileName(Host.TemplateFile) #>", "1.0.0.0")]
+ internal static class Error
+ {<#
+ foreach (var line in lines.Where(l => l.Item3 != null))
+ {
+ #>
+
+ ///
+ /// <#= line.Item3 #> with message like "<#= line.Item2 #>"
+ ///
+ internal static Exception <#= line.Item1 #><#= line.Item4 ? line.Item6 : "()" #>
+ {
+ return new <#= line.Item3 #>(Strings.<#= line.Item1 #><#= line.Item4 ? "(" + line.Item5 + ")" : string.Empty #>);
+ }
+<#
+ }#>
+ ///
+ /// The exception that is thrown when a null reference (Nothing in Visual Basic) is passed to a method that does not accept it as a valid argument.
+ ///
+ internal static Exception ArgumentNull(string paramName)
+ {
+ return new ArgumentNullException(paramName);
+ }
+
+ ///
+ /// The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.
+ ///
+ internal static Exception ArgumentOutOfRange(string paramName)
+ {
+ return new ArgumentOutOfRangeException(paramName);
+ }
+
+ ///
+ /// The exception that is thrown when the author has yet to implement the logic at this point in the program. This can act as an exception based TODO tag.
+ ///
+ internal static Exception NotImplemented()
+ {
+ return new NotImplementedException();
+ }
+
+ ///
+ /// The exception that is thrown when an invoked method is not supported, or when there is an attempt to read, seek, or write to a stream that does not support the invoked functionality.
+ ///
+ internal static Exception NotSupported()
+ {
+ return new NotSupportedException();
+ }
+ }
+
+ ///
+ /// AutoGenerated resource class. Usage:
+ ///
+ /// string s = EntityRes.GetString(EntityRes.MyIdenfitier);
+ ///
+ [GeneratedCode("<#= Path.GetFileName(Host.TemplateFile) #>", "1.0.0.0")]
+ internal sealed class EntityRes
+ {
+<#
+ foreach (var line in lines)
+ {#>
+ internal const string <#= line.Item1 #> = "<#= line.Item1 #>";
+<#
+ }#>
+
+ static EntityRes loader = null;
+ ResourceManager resources;
+
+ private EntityRes()
+ {
+ resources = new ResourceManager("Microsoft.DbContextPackage.Properties.<#= Path.GetFileNameWithoutExtension(Host.TemplateFile) #>", typeof(Microsoft.DbContextPackage.DbContextPackage).Assembly);
+ }
+
+ private static EntityRes GetLoader()
+ {
+ if (loader == null)
+ {
+ EntityRes sr = new EntityRes();
+ Interlocked.CompareExchange(ref loader, sr, null);
+ }
+ return loader;
+ }
+
+ private static CultureInfo Culture
+ {
+ get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; }
+ }
+
+ public static ResourceManager Resources
+ {
+ get
+ {
+ return GetLoader().resources;
+ }
+ }
+
+ public static string GetString(string name, params object[] args)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ string res = sys.resources.GetString(name, EntityRes.Culture);
+
+ if (args != null && args.Length > 0)
+ {
+ for (int i = 0; i < args.Length; i++)
+ {
+ String value = args[i] as String;
+ if (value != null && value.Length > 1024)
+ {
+ args[i] = value.Substring(0, 1024 - 3) + "...";
+ }
+ }
+ return String.Format(CultureInfo.CurrentCulture, res, args);
+ }
+ else
+ {
+ return res;
+ }
+ }
+
+ public static string GetString(string name)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ return sys.resources.GetString(name, EntityRes.Culture);
+ }
+
+ public static string GetString(string name, out bool usedFallback)
+ {
+ // always false for this version of gensr
+ usedFallback = false;
+ return GetString(name);
+ }
+
+ public static object GetObject(string name)
+ {
+ EntityRes sys = GetLoader();
+ if (sys == null)
+ return null;
+ return sys.resources.GetObject(name, EntityRes.Culture);
+ }
+ }
+}
diff --git a/src/PowerTools/Resources/1.png b/src/PowerTools/Resources/1.png
new file mode 100644
index 00000000..5e815a7c
Binary files /dev/null and b/src/PowerTools/Resources/1.png differ
diff --git a/src/PowerTools/Resources/2.png b/src/PowerTools/Resources/2.png
new file mode 100644
index 00000000..59e941d3
Binary files /dev/null and b/src/PowerTools/Resources/2.png differ
diff --git a/src/PowerTools/Resources/3.png b/src/PowerTools/Resources/3.png
new file mode 100644
index 00000000..1ff480d4
Binary files /dev/null and b/src/PowerTools/Resources/3.png differ
diff --git a/src/PowerTools/Resources/4.png b/src/PowerTools/Resources/4.png
new file mode 100644
index 00000000..3daedfe0
Binary files /dev/null and b/src/PowerTools/Resources/4.png differ
diff --git a/src/PowerTools/Resources/5.png b/src/PowerTools/Resources/5.png
new file mode 100644
index 00000000..04cc9e72
Binary files /dev/null and b/src/PowerTools/Resources/5.png differ
diff --git a/src/PowerTools/Resources/Package.ico b/src/PowerTools/Resources/Package.ico
new file mode 100644
index 00000000..ea3b23fe
Binary files /dev/null and b/src/PowerTools/Resources/Package.ico differ
diff --git a/src/PowerTools/Utilities/EdmxUtility.cs b/src/PowerTools/Utilities/EdmxUtility.cs
new file mode 100644
index 00000000..006c5457
--- /dev/null
+++ b/src/PowerTools/Utilities/EdmxUtility.cs
@@ -0,0 +1,148 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System.Collections.Generic;
+ using System.Data.Entity.Design;
+ using System.Data.Mapping;
+ using System.Data.Metadata.Edm;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Xml;
+ using System.Xml.Linq;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+
+ internal class EdmxUtility
+ {
+ private static readonly IEnumerable EDMX_NAMESPACES = new XNamespace[]
+ {
+ "http://schemas.microsoft.com/ado/2009/11/edmx",
+ "http://schemas.microsoft.com/ado/2008/10/edmx",
+ "http://schemas.microsoft.com/ado/2007/06/edmx"
+ };
+
+ private readonly string _edmxPath;
+
+ public EdmxUtility(string edmxPath)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(edmxPath));
+
+ _edmxPath = edmxPath;
+ }
+
+ public StorageMappingItemCollection GetMappingCollection()
+ {
+ IList errors;
+ var edmxFileName = Path.GetFileName(_edmxPath);
+
+ EdmItemCollection edmCollection;
+ using (var reader = CreateSectionReader(EdmxSection.Csdl))
+ {
+ edmCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(
+ new[] { reader },
+ out errors);
+ errors.HandleErrors(Strings.EdmSchemaError(edmxFileName, EdmxSection.Csdl.SectionName));
+ }
+
+ StoreItemCollection storeCollection;
+ using (var reader = CreateSectionReader(EdmxSection.Ssdl))
+ {
+ storeCollection = MetadataItemCollectionFactory.CreateStoreItemCollection(
+ new[] { reader },
+ out errors);
+ errors.HandleErrors(Strings.EdmSchemaError(edmxFileName, EdmxSection.Ssdl.SectionName));
+ }
+
+ StorageMappingItemCollection mappingCollection;
+ using (var reader = CreateSectionReader(EdmxSection.Msl))
+ {
+ mappingCollection = MetadataItemCollectionFactory.CreateStorageMappingItemCollection(
+ edmCollection,
+ storeCollection,
+ new[] { reader },
+ out errors);
+ errors.HandleErrors(Strings.EdmSchemaError(edmxFileName, EdmxSection.Msl.SectionName));
+ }
+
+ return mappingCollection;
+ }
+
+ private XmlReader CreateSectionReader(EdmxSection edmxSection)
+ {
+ Contract.Requires(edmxSection != null);
+
+ var edmxDocument = XElement.Load(_edmxPath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
+
+ var runtime = edmxDocument.Element(EDMX_NAMESPACES, "Runtime");
+ if (runtime == null)
+ {
+ return null;
+ }
+
+ var section = runtime.Element(EDMX_NAMESPACES, edmxSection.SectionName);
+ if (section == null)
+ {
+ return null;
+ }
+
+ var rootElement = section.Element(edmxSection.Namespaces, edmxSection.RootElementName);
+ if (rootElement == null)
+ {
+ return null;
+ }
+
+ return rootElement.CreateReader();
+ }
+
+ private sealed class EdmxSection
+ {
+ static EdmxSection()
+ {
+ Csdl = new EdmxSection
+ {
+ Namespaces = new XNamespace[]
+ {
+ "http://schemas.microsoft.com/ado/2009/11/edm",
+ "http://schemas.microsoft.com/ado/2008/09/edm",
+ "http://schemas.microsoft.com/ado/2006/04/edm"
+ },
+ SectionName = "ConceptualModels",
+ RootElementName = "Schema"
+ };
+ Msl = new EdmxSection
+ {
+ Namespaces = new XNamespace[]
+ {
+ "http://schemas.microsoft.com/ado/2009/11/mapping/cs",
+ "http://schemas.microsoft.com/ado/2008/09/mapping/cs",
+ "urn:schemas-microsoft-com:windows:storage:mapping:CS"
+ },
+ SectionName = "Mappings",
+ RootElementName = "Mapping"
+ };
+ Ssdl = new EdmxSection
+ {
+ Namespaces = new XNamespace[]
+ {
+ "http://schemas.microsoft.com/ado/2009/11/edm/ssdl",
+ "http://schemas.microsoft.com/ado/2009/02/edm/ssdl",
+ "http://schemas.microsoft.com/ado/2006/04/edm/ssdl"
+ },
+ SectionName = "StorageModels",
+ RootElementName = "Schema"
+ };
+ }
+
+ private EdmxSection()
+ {
+ }
+
+ public static EdmxSection Csdl { get; private set; }
+ public static EdmxSection Msl { get; private set; }
+ public static EdmxSection Ssdl { get; private set; }
+
+ public IEnumerable Namespaces { get; private set; }
+ public string SectionName { get; private set; }
+ public string RootElementName { get; private set; }
+ }
+ }
+}
diff --git a/src/PowerTools/Utilities/EfTextTemplateHost.cs b/src/PowerTools/Utilities/EfTextTemplateHost.cs
new file mode 100644
index 00000000..a870ef90
--- /dev/null
+++ b/src/PowerTools/Utilities/EfTextTemplateHost.cs
@@ -0,0 +1,192 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Collections.Generic;
+ using System.Data.Metadata.Edm;
+ using System.Data.Objects;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.VisualStudio.Shell;
+ using Microsoft.VisualStudio.Shell.Interop;
+ using Microsoft.VisualStudio.TextTemplating;
+
+ public class EfTextTemplateHost : ITextTemplatingEngineHost
+ {
+ public EntityType EntityType { get; set; }
+ public EntityContainer EntityContainer { get; set; }
+ public string Namespace { get; set; }
+ public string ModelsNamespace { get; set; }
+ public string MappingNamespace { get; set; }
+ public Version EntityFrameworkVersion { get; set; }
+ public EntitySet TableSet { get; set; }
+ public Dictionary PropertyToColumnMappings { get; set; }
+ public Dictionary>>> ManyToManyMappings { get; set; }
+
+ #region T4 plumbing
+
+ public CompilerErrorCollection Errors { get; set; }
+ public string FileExtension { get; set; }
+ public Encoding OutputEncoding { get; set; }
+ public string TemplateFile { get; set; }
+
+ public virtual string ResolveAssemblyReference(string assemblyReference)
+ {
+ if (File.Exists(assemblyReference))
+ {
+ return assemblyReference;
+ }
+
+ try
+ {
+ // TODO: This is failing to resolve partial assembly names (e.g. "System.Xml")
+ var assembly = Assembly.Load(assemblyReference);
+
+ if (assembly != null)
+ {
+ return assembly.Location;
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ }
+ catch (FileLoadException)
+ {
+ }
+ catch (BadImageFormatException)
+ {
+ }
+
+ return string.Empty;
+ }
+
+ IList ITextTemplatingEngineHost.StandardAssemblyReferences
+ {
+ get
+ {
+ return new[]
+ {
+ Assembly.GetExecutingAssembly().Location,
+ typeof(Uri).Assembly.Location,
+ typeof(Enumerable).Assembly.Location,
+ typeof(ObjectContext).Assembly.Location,
+
+ // HACK: Because of the issue in ResolveAssemblyReference, these are not being
+ // loaded but are required by the default templates
+ typeof(System.Data.AcceptRejectRule).Assembly.Location,
+ typeof(System.Data.Entity.Design.EdmToObjectNamespaceMap).Assembly.Location,
+ typeof(System.Xml.ConformanceLevel).Assembly.Location,
+ typeof(System.Xml.Linq.Extensions).Assembly.Location,
+ typeof(EnvDTE._BuildEvents).Assembly.Location
+ };
+ }
+ }
+
+ IList ITextTemplatingEngineHost.StandardImports
+ {
+ get
+ {
+ return new[]
+ {
+ "System",
+ "Microsoft.DbContextPackage.Utilities"
+ };
+ }
+ }
+
+ object ITextTemplatingEngineHost.GetHostOption(string optionName)
+ {
+ if (optionName == "CacheAssemblies")
+ {
+ return 1;
+ }
+
+ return null;
+ }
+
+ bool ITextTemplatingEngineHost.LoadIncludeText(string requestFileName, out string content, out string location)
+ {
+ location = ((ITextTemplatingEngineHost)this).ResolvePath(requestFileName);
+
+ if (File.Exists(location))
+ {
+ content = File.ReadAllText(location);
+
+ return true;
+ }
+
+ using (var rootKey = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration))
+ using (var includeFoldersKey = rootKey.OpenSubKey(@"TextTemplating\IncludeFolders\.tt"))
+ {
+ foreach (var valueName in includeFoldersKey.GetValueNames())
+ {
+ var includeFolder = includeFoldersKey.GetValue(valueName) as string;
+
+ if (includeFolder == null)
+ {
+ continue;
+ }
+
+ location = Path.Combine(includeFolder, requestFileName);
+
+ if (File.Exists(location))
+ {
+ content = File.ReadAllText(location);
+
+ return true;
+ }
+ }
+ }
+
+ location = string.Empty;
+ content = string.Empty;
+
+ return false;
+ }
+
+ void ITextTemplatingEngineHost.LogErrors(CompilerErrorCollection errors)
+ {
+ Errors = errors;
+ }
+
+ AppDomain ITextTemplatingEngineHost.ProvideTemplatingAppDomain(string content)
+ {
+ return AppDomain.CurrentDomain;
+ }
+
+ Type ITextTemplatingEngineHost.ResolveDirectiveProcessor(string processorName)
+ {
+ throw Error.UnknownDirectiveProcessor(processorName);
+ }
+
+ string ITextTemplatingEngineHost.ResolveParameterValue(string directiveId, string processorName, string parameterName)
+ {
+ return string.Empty;
+ }
+
+ string ITextTemplatingEngineHost.ResolvePath(string path)
+ {
+ if (!Path.IsPathRooted(path) && Path.IsPathRooted(TemplateFile))
+ {
+ return Path.Combine(Path.GetDirectoryName(TemplateFile), path);
+ }
+
+ return path;
+ }
+
+ void ITextTemplatingEngineHost.SetFileExtension(string extension)
+ {
+ FileExtension = extension;
+ }
+
+ void ITextTemplatingEngineHost.SetOutputEncoding(Encoding encoding, bool fromOutputDirective)
+ {
+ OutputEncoding = encoding;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PowerTools/Utilities/FileExtensions.cs b/src/PowerTools/Utilities/FileExtensions.cs
new file mode 100644
index 00000000..e0a66094
--- /dev/null
+++ b/src/PowerTools/Utilities/FileExtensions.cs
@@ -0,0 +1,11 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ internal static class FileExtensions
+ {
+ public const string CSharp = ".cs";
+ public const string VisualBasic = ".vb";
+ public const string EntityDataModel = ".edmx";
+ public const string Xml = ".xml";
+ public const string Sql = ".sql";
+ }
+}
diff --git a/src/PowerTools/Utilities/RuntimeFailureMethods.cs b/src/PowerTools/Utilities/RuntimeFailureMethods.cs
new file mode 100644
index 00000000..f2ae472b
--- /dev/null
+++ b/src/PowerTools/Utilities/RuntimeFailureMethods.cs
@@ -0,0 +1,44 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System.Diagnostics;
+ using System.Text.RegularExpressions;
+ using Microsoft.DbContextPackage.Resources;
+
+ ///
+ /// Code Contracts hook methods - Called when contracts fail. Here we detect the
+ /// most common preconditions so we can throw the correct exceptions. It also
+ /// means that we can write preconditions using the simplest Contract.Requires()
+ /// form.
+ ///
+ internal static class RuntimeFailureMethods
+ {
+ public static readonly Regex IsNotNull = new Regex(
+ @"^\s*(@?\w+)\s*\!\=\s*null\s*$",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static readonly Regex IsNullOrWhiteSpace = new Regex(
+ @"^\s*\!\s*string\s*\.\s*IsNullOrWhiteSpace\s*\(\s*(@?[\w]+)\s*\)\s*$",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ [DebuggerStepThrough]
+ public static void Requires(bool condition, string userMessage, string conditionText)
+ {
+ if (!condition)
+ {
+ Match match;
+
+ if (((match = IsNotNull.Match(conditionText)) != null) && match.Success)
+ {
+ throw Error.ArgumentNull(match.Groups[1].Value);
+ }
+
+ if (((match = IsNullOrWhiteSpace.Match(conditionText)) != null) && match.Success)
+ {
+ throw Error.ArgumentIsNullOrWhitespace(match.Groups[1].Value);
+ }
+
+ throw Error.PreconditionFailed(conditionText, userMessage);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PowerTools/Utilities/TemplateProcessor.cs b/src/PowerTools/Utilities/TemplateProcessor.cs
new file mode 100644
index 00000000..ce85fbc5
--- /dev/null
+++ b/src/PowerTools/Utilities/TemplateProcessor.cs
@@ -0,0 +1,94 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using EnvDTE;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+ using Microsoft.VisualStudio.Shell;
+ using Microsoft.VisualStudio.TextTemplating;
+ using Microsoft.VisualStudio.TextTemplating.VSHost;
+
+ internal class TemplateProcessor
+ {
+ private readonly Project _project;
+ private readonly IDictionary _templateCache;
+
+ public TemplateProcessor(Project project)
+ {
+ Contract.Requires(project != null);
+
+ _project = project;
+ _templateCache = new Dictionary();
+ }
+
+ public string Process(string templatePath, EfTextTemplateHost host)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(templatePath));
+ Contract.Requires(host != null);
+
+ host.TemplateFile = templatePath;
+
+ var output = GetEngine().ProcessTemplate(
+ GetTemplate(templatePath),
+ host);
+
+ host.Errors.HandleErrors(Strings.ProcessTemplateError(Path.GetFileName(templatePath)));
+
+ return output;
+ }
+
+ private string GetTemplate(string templatePath)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(templatePath));
+
+ if (_templateCache.ContainsKey(templatePath))
+ {
+ return _templateCache[templatePath];
+ }
+
+ var items = templatePath.Split('\\');
+ Contract.Assert(items.Length > 1);
+
+ var childProjectItem
+ = _project.ProjectItems
+ .GetItem(items[0]);
+
+ for (int i = 1; childProjectItem != null && i < items.Length; i++)
+ {
+ var item = items[i];
+
+ childProjectItem = childProjectItem.ProjectItems.GetItem(item);
+ }
+
+ string contents = null;
+
+ if (childProjectItem != null)
+ {
+ var path = (string)childProjectItem.Properties.Item("FullPath").Value;
+
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ contents = File.ReadAllText(path);
+ }
+ }
+
+ if (contents == null)
+ {
+ contents = Templates.GetDefaultTemplate(templatePath);
+ }
+
+ _templateCache.Add(templatePath, contents);
+
+ return contents;
+ }
+
+ private static ITextTemplatingEngine GetEngine()
+ {
+ var textTemplating = (ITextTemplatingComponents)Package.GetGlobalService(typeof(STextTemplating));
+
+ return textTemplating.Engine;
+ }
+ }
+}
diff --git a/src/PowerTools/Utilities/Templates.cs b/src/PowerTools/Utilities/Templates.cs
new file mode 100644
index 00000000..25d4772d
--- /dev/null
+++ b/src/PowerTools/Utilities/Templates.cs
@@ -0,0 +1,27 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Text;
+
+ internal static class Templates
+ {
+ public const string ContextTemplate = @"CodeTemplates\ReverseEngineerCodeFirst\Context.tt";
+ public const string EntityTemplate = @"CodeTemplates\ReverseEngineerCodeFirst\Entity.tt";
+ public const string MappingTemplate = @"CodeTemplates\ReverseEngineerCodeFirst\Mapping.tt";
+
+ public static string GetDefaultTemplate(string path)
+ {
+ Contract.Requires(!string.IsNullOrWhiteSpace(path));
+
+ var stream = typeof(Templates).Assembly.GetManifestResourceStream(
+ "Microsoft.DbContextPackage." + path.Replace('\\', '.'));
+ Contract.Assert(stream != null);
+
+ using (var reader = new StreamReader(stream, Encoding.UTF8))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+ }
+}
diff --git a/src/PowerTools/VSPackage.resx b/src/PowerTools/VSPackage.resx
new file mode 100644
index 00000000..1b25371f
--- /dev/null
+++ b/src/PowerTools/VSPackage.resx
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Entity Framework Power Tools
+
+
+ Adds useful design-time DbContext features to the Visual Studio Solution Explorer context menu.
+
+When right-clicking on a file containing a derived DbContext class, the following context menu functions are supported:
+
+1) View Entity Data Model - Displays the underlying Code First model in the Entity Framework designer.
+2) View Entity Data Model XML - Displays the EDMX XML representing the underlying Code First model.
+3) Generate Views - Generates pre-compiled views used by the EF runtime to improve start-up performance. Adds the generated views file to the containing project.
+
+
+
+ Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/src/PowerTools/db.png b/src/PowerTools/db.png
new file mode 100644
index 00000000..7ab63b04
Binary files /dev/null and b/src/PowerTools/db.png differ
diff --git a/src/PowerTools/menu.png b/src/PowerTools/menu.png
new file mode 100644
index 00000000..d6702f69
Binary files /dev/null and b/src/PowerTools/menu.png differ
diff --git a/src/PowerTools/source.extension.vsixmanifest b/src/PowerTools/source.extension.vsixmanifest
new file mode 100644
index 00000000..ad8cc973
--- /dev/null
+++ b/src/PowerTools/source.extension.vsixmanifest
@@ -0,0 +1,42 @@
+
+
+
+ Entity Framework Power Tools Beta 2
+ Microsoft
+ 0.6.0.0
+ Preview of useful design-time features for DbContext.
+
+When right-clicking on a C# project, the following context menu function is supported:
+1) Reverse Engineer Code First - Generates POCO classes, derived DbContext and Code First mapping for an existing database.
+
+When right-clicking on a file containing a derived DbContext class, the following context menu functions are supported:
+
+1) View Entity Data Model XML - Displays the EDMX XML representing the underlying Code First model.
+2) View Entity Data Model DDL SQL - Displays the DDL SQL corresponding to the SSDL in the underlying EDM Model.
+3) Generate Views - Generates pre-compiled views used by the EF runtime to improve start-up performance. Adds the generated views file to the containing project.
+ 1033
+ http://blogs.msdn.com/b/adonet
+ License.rtf
+ http://blogs.msdn.com/b/adonet
+ db.png
+ menu.png
+ false
+
+
+ Pro
+
+
+ Pro
+
+
+
+
+
+
+ Visual Studio MPF
+
+
+
+ |%CurrentProject%;PkgdefProjectOutputGroup|
+
+
diff --git a/test/PowerTools.Test/Extensions/CompilerErrorCollectionExtensionsTests.cs b/test/PowerTools.Test/Extensions/CompilerErrorCollectionExtensionsTests.cs
new file mode 100644
index 00000000..b1005e92
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/CompilerErrorCollectionExtensionsTests.cs
@@ -0,0 +1,36 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using Xunit;
+ using System.CodeDom.Compiler;
+ using System.Linq;
+
+ public class CompilerErrorCollectionExtensionsTests
+ {
+ [Fact]
+ public void HandleErrors_is_noop_when_no_errors()
+ {
+ var errors = new CompilerErrorCollection
+ {
+ new CompilerError { IsWarning = true }
+ };
+
+ errors.HandleErrors("Not used");
+ }
+
+ [Fact]
+ public void HandleErrors_throws_when_errors()
+ {
+ var error = new CompilerError { IsWarning = false };
+ var errors = new CompilerErrorCollection { error };
+ var message = "Some message";
+
+ var ex = Assert.Throws(
+ () => errors.HandleErrors(message));
+
+ Assert.Equal(message, ex.Message);
+ Assert.NotNull(ex.Errors);
+ Assert.Equal(1, ex.Errors.Count());
+ Assert.Same(error, ex.Errors.Single());
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Extensions/CompilerErrorExceptionTests.cs b/test/PowerTools.Test/Extensions/CompilerErrorExceptionTests.cs
new file mode 100644
index 00000000..afa411fb
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/CompilerErrorExceptionTests.cs
@@ -0,0 +1,54 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Runtime.Serialization.Formatters.Binary;
+ using Xunit;
+
+ public class CompilerErrorExceptionTests
+ {
+ [Fact]
+ public void Ctor_validates_preconditions()
+ {
+ IEnumerable errors = null;
+
+ var ex = Assert.Throws(
+ () => new CompilerErrorException("Not used", errors));
+
+ Assert.Equal("errors", ex.ParamName);
+ }
+
+ [Fact]
+ public void Ctor_sets_properties()
+ {
+ var message = "Some message";
+ var errors = new CompilerError[0];
+
+ var ex = new CompilerErrorException(message, errors);
+
+ Assert.Equal(message, ex.Message);
+ Assert.Same(errors, ex.Errors);
+ }
+
+ [Fact]
+ public void Is_serializable()
+ {
+ var message = "Some message";
+ var errors = new CompilerError[0];
+ var formatter = new BinaryFormatter();
+ CompilerErrorException ex;
+
+ using (var stream = new MemoryStream())
+ {
+ formatter.Serialize(stream, new CompilerErrorException(message, errors));
+ stream.Position = 0;
+ ex = (CompilerErrorException)formatter.Deserialize(stream);
+ }
+
+ Assert.Equal(message, ex.Message);
+ Assert.Equal(errors, ex.Errors);
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Extensions/EdmSchemaErrorExceptionTests.cs b/test/PowerTools.Test/Extensions/EdmSchemaErrorExceptionTests.cs
new file mode 100644
index 00000000..5bfeb9bd
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/EdmSchemaErrorExceptionTests.cs
@@ -0,0 +1,54 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Data.Metadata.Edm;
+ using System.IO;
+ using System.Runtime.Serialization.Formatters.Binary;
+ using Xunit;
+
+ public class EdmSchemaErrorExceptionTests
+ {
+ [Fact]
+ public void Ctor_validates_preconditions()
+ {
+ IEnumerable errors = null;
+
+ var ex = Assert.Throws(
+ () => new EdmSchemaErrorException("Not used", errors));
+
+ Assert.Equal("errors", ex.ParamName);
+ }
+
+ [Fact]
+ public void Ctor_sets_properties()
+ {
+ var message = "Some message";
+ var errors = new EdmSchemaError[0];
+
+ var ex = new EdmSchemaErrorException(message, errors);
+
+ Assert.Equal(message, ex.Message);
+ Assert.Same(errors, ex.Errors);
+ }
+
+ [Fact]
+ public void Is_serializable()
+ {
+ var message = "Some message";
+ var errors = new EdmSchemaError[0];
+ var formatter = new BinaryFormatter();
+ EdmSchemaErrorException ex;
+
+ using (var stream = new MemoryStream())
+ {
+ formatter.Serialize(stream, new EdmSchemaErrorException(message, errors));
+ stream.Position = 0;
+ ex = (EdmSchemaErrorException)formatter.Deserialize(stream);
+ }
+
+ Assert.Equal(message, ex.Message);
+ Assert.Equal(errors, ex.Errors);
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Extensions/IComponentModelExtensionsTests.cs b/test/PowerTools.Test/Extensions/IComponentModelExtensionsTests.cs
new file mode 100644
index 00000000..a5a14b34
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/IComponentModelExtensionsTests.cs
@@ -0,0 +1,20 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using Microsoft.VisualStudio.ComponentModelHost;
+ using Moq;
+ using Xunit;
+
+ public class IComponentModelExtensionsTests
+ {
+ [Fact]
+ public void GetService_calls_generic_version()
+ {
+ var componentModelMock = new Mock();
+ var componentModel = componentModelMock.Object;
+
+ componentModel.GetService(typeof(string));
+
+ componentModelMock.Verify(cm => cm.GetService(), Times.Once());
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Extensions/StringExtensionsTests.cs b/test/PowerTools.Test/Extensions/StringExtensionsTests.cs
new file mode 100644
index 00000000..93c0bf4e
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/StringExtensionsTests.cs
@@ -0,0 +1,17 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using Xunit;
+
+ public class StringExtensionsTests
+ {
+ [Fact]
+ public void EqualsIgnoreCase_tests_equality()
+ {
+ Assert.True(StringExtensions.EqualsIgnoreCase(null, null));
+ Assert.True("one".EqualsIgnoreCase("one"));
+ Assert.True("two".EqualsIgnoreCase("TWO"));
+ Assert.False("three".EqualsIgnoreCase("four"));
+ Assert.False("five".EqualsIgnoreCase(null));
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Extensions/XContainerExtensionsTests.cs b/test/PowerTools.Test/Extensions/XContainerExtensionsTests.cs
new file mode 100644
index 00000000..903db6dd
--- /dev/null
+++ b/test/PowerTools.Test/Extensions/XContainerExtensionsTests.cs
@@ -0,0 +1,46 @@
+namespace Microsoft.DbContextPackage.Extensions
+{
+ using System.Xml.Linq;
+ using Xunit;
+
+ public class XContainerExtensionsTests
+ {
+ private const string _elementName = "Element";
+ private readonly XNamespace _ns1 = "http://tempuri.org/1";
+ private readonly XNamespace _ns2 = "http://tempuri.org/2";
+ private readonly XElement _element1;
+ private readonly XContainer _container;
+
+ public XContainerExtensionsTests()
+ {
+ _element1 = new XElement(_ns1 + _elementName);
+ _container = new XElement(
+ "Container",
+ new XElement(_elementName),
+ _element1,
+ new XElement(_ns2 + _elementName));
+ }
+
+ [Fact]
+ public void Element_returns_first_match()
+ {
+ var element = _container.Element(
+ new[] { _ns1, _ns2 },
+ _elementName);
+
+ Assert.Same(_element1, element);
+ }
+
+ [Fact]
+ public void Element_returns_null_when_no_match()
+ {
+ XNamespace ns3 = "http://tempuri.org/3";
+
+ var element = _container.Element(
+ new[] { ns3 },
+ _elementName);
+
+ Assert.Null(element);
+ }
+ }
+}
diff --git a/test/PowerTools.Test/PowerTools.Test.csproj b/test/PowerTools.Test/PowerTools.Test.csproj
new file mode 100644
index 00000000..15037838
--- /dev/null
+++ b/test/PowerTools.Test/PowerTools.Test.csproj
@@ -0,0 +1,83 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {ADCD3A3D-2564-48F3-81A9-B0B532675808}
+ Library
+ Properties
+ Microsoft.DbContextPackage
+ EFPowerTools.Test
+ v4.0
+ 512
+ ..\..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll
+
+
+ ..\..\packages\xunit.1.9.0.1566\lib\xunit.dll
+
+
+
+
+ {16CAD3A8-FCE0-4BC1-901A-16957CF24BD6}
+ PowerTools
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/PowerTools.Test/Properties/AssemblyInfo.cs b/test/PowerTools.Test/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..dc9c6cb2
--- /dev/null
+++ b/test/PowerTools.Test/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PowerTools.Test")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PowerTools.Test")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("7d655301-2f25-4ca7-a8a8-82e3d02921e2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/test/PowerTools.Test/Utilities/EdmxUtilityTests.cs b/test/PowerTools.Test/Utilities/EdmxUtilityTests.cs
new file mode 100644
index 00000000..96c5b8a6
--- /dev/null
+++ b/test/PowerTools.Test/Utilities/EdmxUtilityTests.cs
@@ -0,0 +1,210 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Data.Mapping;
+ using System.IO;
+ using System.Linq;
+ using System.Text;
+ using Microsoft.DbContextPackage.Extensions;
+ using Microsoft.DbContextPackage.Resources;
+ using Xunit;
+
+ public class EdmxUtilityTests
+ {
+ [Fact]
+ public void GetMappingCollection_returns_mapping()
+ {
+ var edmxContents = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+ StorageMappingItemCollection mappingCollection;
+
+ using (var edmx = new TempFile(edmxContents))
+ {
+ mappingCollection = new EdmxUtility(edmx.FileName)
+ .GetMappingCollection();
+ }
+
+ Assert.True(mappingCollection.Contains("DatabaseEntities"));
+ }
+
+ [Fact]
+ public void GetMappingCollection_returns_mapping_for_v2_schema()
+ {
+ var edmxContents = @"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+";
+ StorageMappingItemCollection mappingCollection;
+
+ using (var edmx = new TempFile(edmxContents))
+ {
+ mappingCollection = new EdmxUtility(edmx.FileName)
+ .GetMappingCollection();
+ }
+
+ Assert.True(mappingCollection.Contains("DatabaseEntities"));
+ }
+
+ [Fact]
+ public void GetMappingCollection_throws_on_schema_errors()
+ {
+ var edmxContents = @"
+
+
+
+
+
+
+";
+
+ using (var edmx = new TempFile(edmxContents))
+ {
+ var ex = Assert.Throws(
+ () => new EdmxUtility(edmx.FileName).GetMappingCollection());
+
+ Assert.Equal(
+ Strings.EdmSchemaError(
+ Path.GetFileName(edmx.FileName),
+ "ConceptualModels"),
+ ex.Message);
+ }
+ }
+
+ private sealed class TempFile : IDisposable
+ {
+ private readonly string _fileName;
+ private bool _disposed;
+
+ public TempFile(string contents)
+ {
+ _fileName = Path.GetTempFileName();
+ File.WriteAllText(_fileName, contents);
+ }
+
+ ~TempFile()
+ {
+ Dispose(false);
+ }
+
+ public string FileName
+ {
+ get
+ {
+ HandleDisposed();
+
+ return _fileName;
+ }
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (!string.IsNullOrWhiteSpace(_fileName))
+ {
+ File.Delete(_fileName);
+ }
+
+ _disposed = true;
+ }
+ }
+
+ private void HandleDisposed()
+ {
+ if (_disposed)
+ {
+ throw new ObjectDisposedException(null);
+ }
+ }
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Utilities/EfTextTemplateHostTests.cs b/test/PowerTools.Test/Utilities/EfTextTemplateHostTests.cs
new file mode 100644
index 00000000..f0b7e53a
--- /dev/null
+++ b/test/PowerTools.Test/Utilities/EfTextTemplateHostTests.cs
@@ -0,0 +1,149 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System;
+ using System.CodeDom.Compiler;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Runtime.Serialization.Formatters.Binary;
+ using Microsoft.VisualStudio.TextTemplating;
+ using Xunit;
+
+ public class EfTextTemplateHostTests
+ {
+ public class ResolveAssemblyReference
+ {
+ [Fact]
+ public void Resolves_assembly_locations()
+ {
+ var host = new EfTextTemplateHost();
+ var assemblyLocation = typeof(Type).Assembly.Location;
+
+ var resolvedReference = host.ResolveAssemblyReference(
+ assemblyLocation);
+
+ Assert.True(File.Exists(resolvedReference));
+ Assert.Equal("mscorlib.dll", Path.GetFileName(resolvedReference));
+ }
+
+ [Fact]
+ public void Resolves_full_assembly_names()
+ {
+ var host = new EfTextTemplateHost();
+ var assemblyName = typeof(Type).Assembly.GetName();
+
+ var resolvedReference = host.ResolveAssemblyReference(
+ assemblyName.FullName);
+
+ Assert.True(File.Exists(resolvedReference));
+ Assert.Equal("mscorlib.dll", Path.GetFileName(resolvedReference));
+ }
+
+ [Fact]
+ public void Resolves_simple_assembly_names()
+ {
+ var host = new EfTextTemplateHost();
+ var assemblyName = typeof(Type).Assembly.GetName();
+
+ var resolvedReference = host.ResolveAssemblyReference(
+ assemblyName.Name);
+
+ Assert.True(File.Exists(resolvedReference));
+ Assert.Equal("mscorlib.dll", Path.GetFileName(resolvedReference));
+ }
+ }
+
+ [Fact]
+ public void StandardAssemblyReferences_includes_basic_references()
+ {
+ ITextTemplatingEngineHost host = new EfTextTemplateHost();
+ var powerToolsAssemblyName = typeof(EfTextTemplateHost)
+ .Assembly
+ .GetName()
+ .Name;
+
+ var references = host.StandardAssemblyReferences
+ .Select(r => Path.GetFileNameWithoutExtension(r))
+ .ToArray();
+
+ Assert.Contains(powerToolsAssemblyName, references);
+ Assert.Contains("System", references);
+ Assert.Contains("System.Core", references);
+ Assert.Contains("System.Data.Entity", references);
+ }
+
+ [Fact]
+ public void StandardImports_includes_basic_imports()
+ {
+ ITextTemplatingEngineHost host = new EfTextTemplateHost();
+ var hostNamespace = typeof(EfTextTemplateHost).Namespace;
+
+ var imports = host.StandardImports;
+
+ Assert.Contains("System", imports);
+ Assert.Contains(hostNamespace, imports);
+ }
+
+ [Fact]
+ public void LogErrors_sets_Errors()
+ {
+ var efHost = new EfTextTemplateHost();
+ var host = (ITextTemplatingEngineHost)efHost;
+ var errors = new CompilerErrorCollection();
+
+ host.LogErrors(errors);
+
+ Assert.Same(errors, efHost.Errors);
+ }
+
+ public class ResolvePath
+ {
+ [Fact]
+ public void Resolves_absolute_paths()
+ {
+ const string path = @"C:\File.ext";
+ ITextTemplatingEngineHost host = new EfTextTemplateHost();
+
+ var resolvedPath = host.ResolvePath(path);
+
+ Assert.Equal(path, resolvedPath);
+ }
+
+ [Fact]
+ public void Resolves_relative_paths_when_TemplateFile_absolute()
+ {
+ ITextTemplatingEngineHost host = new EfTextTemplateHost
+ {
+ TemplateFile = @"C:\Template.tt"
+ };
+
+ var resolvedPath = host.ResolvePath("File.ext");
+
+ Assert.Equal(@"C:\File.ext", resolvedPath);
+ }
+
+ [Fact]
+ public void Returns_original_path_when_unresolvable()
+ {
+ const string path = "File.ext";
+ ITextTemplatingEngineHost host = new EfTextTemplateHost();
+
+ var resolvedPath = host.ResolvePath(path);
+
+ Assert.Equal(path, resolvedPath);
+ }
+ }
+
+ [Fact]
+ public void SetFileExtension_sets_FileExtension()
+ {
+ const string extension = ".ext";
+ var efHost = new EfTextTemplateHost();
+ var host = (ITextTemplatingEngineHost)efHost;
+
+ host.SetFileExtension(extension);
+
+ Assert.Equal(extension, efHost.FileExtension);
+ }
+ }
+}
diff --git a/test/PowerTools.Test/Utilities/TemplatesTests.cs b/test/PowerTools.Test/Utilities/TemplatesTests.cs
new file mode 100644
index 00000000..bbc889ca
--- /dev/null
+++ b/test/PowerTools.Test/Utilities/TemplatesTests.cs
@@ -0,0 +1,21 @@
+namespace Microsoft.DbContextPackage.Utilities
+{
+ using System;
+ using Xunit;
+
+ public class TemplatesTests
+ {
+ [Fact]
+ public void GetDefaultTemplate_returns_default_templates()
+ {
+ var contextTemplate = Templates.GetDefaultTemplate(Templates.ContextTemplate);
+ Assert.False(string.IsNullOrWhiteSpace(contextTemplate));
+
+ var entityTemplate = Templates.GetDefaultTemplate(Templates.EntityTemplate);
+ Assert.False(string.IsNullOrWhiteSpace(entityTemplate));
+
+ var mappingTemplate = Templates.GetDefaultTemplate(Templates.MappingTemplate);
+ Assert.False(string.IsNullOrWhiteSpace(mappingTemplate));
+ }
+ }
+}
diff --git a/test/PowerTools.Test/packages.config b/test/PowerTools.Test/packages.config
new file mode 100644
index 00000000..28c4688a
--- /dev/null
+++ b/test/PowerTools.Test/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file