Deleted database & migration file, can't make new migrations nor revert old migrations, what now? - asp.net-core

My workflow with my ASP.NET Core app: Whenever I make changes that affect the database, I revert the old migration, make a new migration, apply it. Because this is still early development I think it makes sense to not pollute the project with lots of migration files. Usually I call this temporary migration file Initial, hoping that one of them will actually become the initial migration file.
As a result, I typically have 3 files in my Data/Migrations subfolder:
00000000000000_CreateIdentitySchema.cs that was provided for me a long time ago by the project template;
somenumbers_Initial.cs;
ApplicationDbContextModelSnapshot.cs
So the commands I would type in this workflow would look like this, if I remember correctly:
Update-Database CreateIdentitySchema
Remove-Migration
Add-Migration Initial
Update-Database
This time I made a mistake... I don't remember what exactly happened, I typed something incorrect instead of the above commands and then I was silly enough that (a) manually removing the Initial migration file and (b) Dropping the database would be enough to restart the test db from scratch.
I was wrong.
My hopes were that Add-Migration Initial would create a valid migration file corresponding to the current state of the models and a subsequent Update-Database would recreate the db. Instead this is what happens:
PM> Add-Migration initial
Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.0.0 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
The name 'initial' is used by an existing migration.
??? Really? Well let's try to remove this migration:
Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.0.0 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
No file named '20190727172711_initial.cs' was found. You must manually remove the migration class 'initial'.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Literal(String value)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GeneratePropertyAnnotations(IProperty property, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateProperty(String builderName, IProperty property, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateProperties(String builderName, IEnumerable`1 properties, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateEntityType(String builderName, IEntityType entityType, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateEntityTypes(String builderName, IReadOnlyList`1 entityTypes, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.Generate(String builderName, IModel model, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateSnapshot(String modelSnapshotNamespace, Type contextType, String modelSnapshotName, IModel model)
at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.RemoveMigration(String projectDir, String rootNamespace, Boolean force, String language)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.RemoveMigration(String contextType, Boolean force)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.RemoveMigrationImpl(String contextType, Boolean force)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.RemoveMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Object reference not set to an instance of an object.
???? I must manually remove the migration class ?? Where is this migration class?
Let's do some text searching in the project dir... The only interesting file that shows up is myprojectname.csproj where I have stuff like this:
<Compile Remove="Data\Migrations\20190412182930_initial.cs" />
<Compile Remove="Data\Migrations\20190412182930_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190628205310_initial.cs" />
<Compile Remove="Data\Migrations\20190628205310_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190720205411_initial.cs" />
<Compile Remove="Data\Migrations\20190720205411_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190720205837_initial.cs" />
<Compile Remove="Data\Migrations\20190720205837_initial.Designer.cs" />
I don't think this is relevant here (the numbers strings do not match).
I looked into ApplicationDbCOntextModelSnapshot but there seems to be no such class called initial there...
What to do now? How to proceed?

If you dropped the database then it's easy. Delete the migrations folder.
Then run
add-migration Initial
update-database
Also, I tend to remove any <Compile Remove="" /> from the project file.

Related

Registering .net assembly for COM succeeds with regasm but fails using RegistrationServices.RegisterAssembly

This is one of the strangest issue I have encountered.
There is a .net assembly, which is exposed to COM.
If you register it with regasm /codebase my.dll - it is sucessfully registered, and can be used.
However, if you register it from code using RegistrationServices.RegisterAssembly() :
[...]
RegistrationServices regSvcs = new RegistrationServices();
Assembly assembly = Assembly.LoadFrom(path);
// must call this before overriding registry hives to prevent binding failures on exported types during RegisterAssembly
assembly.GetExportedTypes();
using (RegistryHarvester registryHarvester = new RegistryHarvester(true))
{
// ******** this throws *********
regSvcs.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
}
Then it throws exception:
Could not load file or assembly 'Infragistics2.Win.UltraWinTree.v9.2, Version=9.2.20092.2083,
Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb' or one of its dependencies.
Provider type not defined. (Exception from HRESULT: 0x80090017)
This error has very little resource on the net, and looks like related to some security(?) cryptography(?) feature.
After long-long hours, I figured out what causes this (but don't know why):
If there is a public class with a public constructor in the assembly with a parameter UltraTree (from the referenced assembly 'Infragistics2.Win.UltraWinTree.v9.2'), then you cannot register from code, but with regasm only.
When I changed the have a public function Init(UltraTree tree), then it works, I can register from code. So:
// regasm: OK / RegistrationServices.RegisterAssembly(): exception
public class Foo
{
public Foo(UltraWinTree tree) { .. }
}
Foo foo = new Foo(_tree);
-------------- vs --------------
// regasm: OK / RegistrationServices.RegisterAssembly(): OK
public class Foo
{
public Foo() {}
public void Init(UltraWinTree tree) { .. }
}
Foo foo = new Foo();
foo.Init(_tree);
So I could workaround by passing UltraWinTree in a new Init() function instead of constructor, but this is not nice, and I want to know the reason, what the heck is going on?
Anyone has any idea? Thanks.
PS:
Okay, but why we want to register from code? As we use Wix to create installer, which uses heat.exe to harvest registry entries (which are added during asm registration), so heat.exe does assembly registration from code.
I've been dealing with this for years so this is the only answer you need to read:
Heat calls regasm /regfile. So does InstallShield when you tell it to. If you read this page:
https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool
There's a very important caveat in the remarks section.
You can use the /regfile option to generate a .reg file that contains
the registry entries instead of making the changes directly to the
registry. You can update the registry on a computer by importing the
.reg file with the Registry Editor tool (Regedit.exe). The .reg file
does not contain any registry updates that can be made by user-defined
register functions. The /regfile option only emits registry entries
for managed classes. This option does not emit entries for TypeLibIDs
or InterfaceIDs.
So what to do? Use Heat to generate most of the metadata. Then on a clean machine, (snapshot VM best) us a registry snapshot and compare tool such as InCntrl3 or InstallWatch Pro and sniff out what additional meta regasm writes to the registry. Finally massage that into your Wxs code.
Then on a cleam machine test the install. The result should work and not require any custom actions in the install.

Nlog variable and log file issue with multiple log files

I created a Log File class that uses NLog with the hopes of writing to multiple log files at the same time. This seems to work fine until I add variables to the mix.
Problem
Changing the variable seems to change that variable setting for all instances of the log file instead of the particular instance I am working with.
My Code
Here is how I have the programming structured:
LogClass - Basically a wrapper to give me some additional functionality. The 'SetVariable' is what I am using to set the particular variable (called dqwAlertName)
With this log class, I am passing in the specific logger that I want to use like this:
Public iLogger as new Logger(NLog.LogManager.GetLogger("dataQualityWatcher"),True)
That statement instantiates the logging class with the "dataQualityWatcher" logger and sets Debug=True (which I simply use to allow a more verbose logging that can be turned on and off).
With that said... The statement above is ALSO within another class object:
dataQualityWatcher Class - This is a 'watcher' that is called many times over and runs continuously. If you familiar with FileSystemWatcher, it works similarly to that. It basically watches data for a specific value and raises an event.
Inside THIS class is where I instantiate the logger as mentioned above with the following code:
Public iLogger as new Logger(NLog.LogManager.GetLogger("dataQualityWatcher"), True)
iLogger.SetVariable("dqwAlertName", _AlertName)
The first line instantiates, the second line will set the variable. The Logging Class SetVariable method is pretty basic:
Public Sub SetVariable(variableName as string, value as String)
'Set variable context for the logger
NLog.LogManager.Configuration.Variables(variableName) = value
End Sub
I am using that variable within the NLog.config file in the following manner:
<variable name="LogLayout" value="[${date:format=MM/dd/yyyy h\:mm\:ss.fff tt}] [${gdc:item=location}] | ${level} | ${message}" />
<variable name="InfoLayout" value="[${date:format=MM/dd/yyyy h\:mm\:ss.fff tt}] ${gdc:item=SoftwareName} Version ${gdc:item=SoftwareVersion} - ${message}" />
<variable name="DebugInfoLayout" value="[${date:format=MM/dd/yyyy h\:mm\:ss.fff tt}] ${message}" />
<variable name="logDir" value="C:/Log/PWTester/" />
<variable name="dqwAlertName" value="" />
<targets>
<target name="dataQualityWatcher" xsi:type="File" fileName="${logDir}/LogFiles/${var:dqwAlertName}-DataQualityWatcher.log" layout="${LogLayout}" />
</targets>
<rules>
<logger name="dataQualityWatcher" minlevel="Trace" writeTo="dataQualityWatcher" />
</rules>
THE PROBLEM:
I run multiple 'watchers' (as I call them) with the following code to create that object and assign properties:
dataWatch.Add(New dataQualityWatcher(True) With {.Tags = lstTags, .AlertTimerInterval = Convert.ToInt64(intTimerMilliseconds), .AlertGroupID = Convert.ToInt64(CARow(0)), .EmailGroupID = Convert.ToInt64(CARow(1)), .CustomSubject = CARow(3), .CustomMessage = CARow(4), .AlertName = DataAlertGroupName, .Debug = blnVerboseLogging, .HistorianServer = SH})
Multiple Version Example
I run the code above where: .AlertName = {"Test1", "Test2", "Test3"}. Other parameters would also change and a new object is instantiated each time. In this example there are 3 dataQualityWatcher objects instantiated, which also instantiates 3 Logger objects.
Each time a new dataQualityWatcher object is instanciated, it instanciates a Logger, which would then write to the file. The AlertName variable is passed on through the SetVariable method above.
I would expect 3 log files to be written:
Test1-DataQualityWatcher.log
Test2-DataQualityWatcher.log
Test3-DataQualityWatcher.log
This DOES happen. However, the last dataQualityWatch object that is created will run the SetVariable method = "Test3" (in this example). Now that variable is set and all 3 Loggers will begin logging to that file (i.e., Test3-DataQualityWatcher.log).
I can only assume that there is a better way to do this with variables such that they are for the life of that particular log instance, but I can't seem to figure it out!
Thanks in advance and sorry for the VERY, VERY long post.
As far as I understand your are trying to log to multiple files, with one target.
This won't work well with the use of variables as they are static (Shared in VB.net) - so this isn't threadsafe.
Other options to do this are:
Create multiple file targets in your nlog.config and setup the right <rules>, or
Pass extra properties for every message, and use event-properties: fileName="${logDir}/LogFiles/${event-properties:dqwAlertName}-DataQualityWatcher.log", VB.NET call:
Dim theEvent As New LogEventInfo(LogLevel.Debug, "", "Pass my custom value")
theEvent.Properties("MyValue") = "My custom string".
You could write a sub class for Logger to make it less verbose. Or
Create the targets & rules programmatically (in VB.NET). See tutorial (in C#)
If performance is very important, choose for 1 or 3.

Invalid value for key attachdbfilename after renaming connectionString

I am building the sample MvcMovie tutorial for ASP.NET MVC 4. I'm using EntityFramework Code First features and created a connectionString as follows.
<add name="MoveDBContext"
connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDictionary|\Movies2.mdf;Integrated Security=True"
providerName="System.Data.SqlClient"
/>
Everything worked fine at this point. But then I realized that I named my connection string MoveDBContext instead of MovieDBContext and, being the perfectionist I renamed it. After doing this I now receive an error in my MoviesController/Index method.
public class MoviesController : Controller
{
private MovieDBContext db = new MovieDBContext();
public ActionResult Index()
{
return View(db.Movies.ToList()); // Error: Invalid value for key 'attachdbfilename'
}
...
}
If I change the name back to MoveDBContext the error goes away.
Can anyone tell me where this original name is being referenced?
EF, by default, looks for a connection string with the same name as the type that extends DbContext.
Or, better put by Scott:
By default, when you create a DbContext class with EF code-first, it
will look for a connection-string that matches the name of the
context-class. Since we named our context class “NerdDinners”, it
will by default look for and use the above “NerdDinners” database
connection-string when it is instantiated within our ASP.NET
application.
Edit:
After looking closer, I think your connection string is the problem. You've got DataDictionary instead of DataDirectory. Try this (line feeds added for readability):
<add name="MovieDBContext"
connectionString="Data Source=(LocalDB)\v11.0;
AttachDbFilename=|DataDirectory|\Movies.mdf;
Integrated Security=True"
providerName="System.Data.SqlClient" />
Apparently, as Ken said, the MoveDBContext was not being referenced.
I removed the entire connectionString from the web.config and everything still functioned correctly.
So, it still begs the question, "How did Visual Studio know to create a database in my SQLExpress instance?" and "Where is that configured at?"

How do I pass MSBuild arguments from the build definition to a MSBuild workflow Activity

I've defined an MSBuild activity in a TFS Workflow template, but currently I have to hard code the 'property' command line arguments directly into the template.
I'd like to be able to specify the arguments in the build definition, via the advanced setting, 'MSBuild Arguments'
I can see that I may have to build up the command line with string replace/concat, as mentioned here, but I can't see what I need to put, maybe something like this:
This is what the default MsBuild task uses:
String.Format("/p:SkipInvalidConfigurations=true {0}", MSBuildArguments)
You can change the MSBuildArguments variable in the build process template in multiple steps. For example, I added a Run Architecture Validation property to the process template and then edited the workflow to simply append /ValidateArchitecture=true to the MSBuildArguments before they're being passed to the MsBuild activity.
<If Condition="[PerformArchitectureValidation]" DisplayName="Configure Architecture Validation MSBuild Arguments">
<If.Then>
<Assign>
<Assign.To>
<OutArgument x:TypeArguments="x:String">[MSBuildArguments]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:String">[MSBuildArguments + " /p:ValidateArchitecture=true"]</InArgument>
</Assign.Value>
</Assign>
</If.Then>
</If>
The PerformArchitectureValidation variable is defined as a Property on the Build Process Template level of type Boolean.
Update: Wrote a blogpost that explains this with steps and screenshots

Obtaining a list of resolved reference paths in msbuild

I'm trying to use ILMerge to merge multiple assemblies into one plugin assembly, which will be dynamically loaded by another application.
Using the community tasks, I've so far got:
<Target Name="CombineAssemblies" AfterTargets="MvcBuildViews">
<ItemGroup>
<MergePaths Include="#(_ResolvedProjectReferencePaths)" />
<!--<MergePaths Include="%(Reference.ResolvedPath)" /> This doesn't work! -->
</ItemGroup>
<ILMerge
DebugInfo="true"
LogFile="log.txt"
InputAssemblies="#(MergePaths)"
OutputFile=""$(TargetPath)""
CopyAttributes="true"
TargetPlatformVersion="v4"/>
</Target>
Using #(_ResolvedProjectReferencePaths) works fine for the project references, and their paths are passesd correctly through to the ILMerge task.
When running this through MSBuild, ILMerge.exe errors out. Looking at the log file, I get:
Unresolved assembly reference not allowed: System.Web.Mvc.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
at System.Compiler.Ir2md.VisitReferencedType(TypeNode type)
at System.Compiler.Ir2md.VisitMethod(Method method)
at System.Compiler.Ir2md.VisitClass(Class Class)
at System.Compiler.Ir2md.VisitModule(Module module)
at System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
at System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation, BinaryWriter writer)
at System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName)
at System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Module module)
at ILMerging.ILMerge.Merge()
at ILMerging.ILMerge.Main(String[] args)
I'm referencing the MVC libraries, which from the log file I assume could not be resolved by ILMerge, even though they are resolved correctly in the actual compilation process.
I therefore want to pass the location of these libraries through to ILMerge, ideally using the References item so I don't have to manually add a bunch of reference paths which would have to change if I added or removed references. Is there some equivalent to #(_ResolvedProjectReferencePaths) for normal references?
Thanks!
Try #(ReferencePath), which should be used after target ResolveAssemblyReferences.