The summary below describes major new features, items of note and breaking changes.
The links above require a login.
EnsureLoaded()
to support more relations (no complement and multiple complements) (QNO-6409)DynamicStrings
(QNO-6330, QNO-6389, QNO-6399, QNO-6415, )DynamicString
no longer declares a virtual
method named GetValueWithoutFallback()
. It has been redefined as an extension method and replaced with TryGetValueWithoutFallback()
, which accepts an IExpressionContext
. This change will only affect products that have inherited from DynamicString
.
The summary below describes major new features, items of note and breaking changes.
The links above require a login.
CreateGuid()
, CreateDate()
, and CreateTime()
. (QNO-6304, QNO-6305)Before upgrading, products should make sure that they do not depend on any obsolete members in the current version (7.x).
Quino-Web 8.0 is a rewrite and is therefore mostly incompatible with 7.x.
See the Quino-Web/Sandbox.Web
project for a working example. This integrates the standard SandboxApplication
into a web site using the standard GenericController
and MetadataController
to provide data and UI to the generic Quino Client.
Some internal types in Quino-Standard have been moved to more appropriate namespaces and assemblies, but the impact on products should be non-existent or very limited.
The following types were moved from Encodo.Quino.Core
to Encodo.Quino.Culture
:
LanguageTextAttribute
IValueParser
CaptionAttribute
LanguageDescriptionAttribute
The following types were moved from Encodo.Quino.Core
to Encodo.Quino.TextFormatting
:
IFileSizeFormatter
Quino's default culture-handling has been overhauled. Instead of tracking its own language, Quino now uses the standard .NET CultureInfo.CurrentUICulture
for the default language and CultureInfo.CurrentCulture
for default formatting (e.g. times, dates, and currencies). Many fields have been marked as obsolete and are no longer used by Quino.
The default languages in Quino have changed from "en-US" and "de-CH" to "en and "de", respectively.
The reasoning behind this is that, while a requested language should be as specific as possible, a supported language should be as general as possible. The standard culture mechanisms and behavior (e.g. .NET Resources) "fall back" to a parent language when a more-specific language cannot be found. If an application claims to only support "en-US", then a request for "en-GB" fails. If the supported language is "en", then any request to a language in the "en" family (e.g. "en-US", "en-GB", "en-AU") will use "en".
An application that supports "en-US" and "de-CH" has, therefore, a more limited palette of languages that it can support.
Quino code runs in the context of a user, who has a list of preferred languages, in decreasing order of preference. This context can last the entire duration of an application (e.g. a standalone application like a console or desktop application) or last as long as a web request.
The application itself has a list of languages that it supports, as well as resources and metadata that defines text in these languages. The resources are standard .NET Resources with the standard fallback mechanism (i.e. a request for "en-US" can be satisfied by "en"). The metadata uses DynamicString
objects, which encapsulate a map from language codes (e.g. "en" or "de") to strings.
During application startup or at the beginning of a web request, the ILanguageResolver
determines the language to use for a given set of requested languages. In ASP.NET Core, the requested languages come from the HTTP headers provided by the browser. In standalone applications, the IRequestedLanguageCalculator
provides the requested languages. The ILanguageInitializer
is responsible for coordinating this during application startup.
The rest of Quino uses the following singletons to work with languages.
IDynamicStringFallbackCalculator
: Comes into play when a request is made for a language that is not directly supported. For example, if the application supports "en" and "de", then a request for "en-US" will ask this singleton how to resolve the request.IDynamicStringFactory
: Creates a dynamic string to describe a given object. The default implementation uses .NET Attributes.ILanguageResolver
: Determines the culture to use from a list of available cultures and a list of requested/preferred cultures.IRequestedLanguageCalculator
: Provides the sequence of languages from which to choose during initial resolution (web requests do not use this).ILanguageInitializer
: Integrates language-selection into the application startup.ICaptionCalculator
: Extracts a single caption for a culture from a given object. Applications should use the IDynamicStringFactory
in most cases, instead.An application can control fallback by registering custom IDynamicStringFallbackCalculator
and ILanguageResolver
implementations (though this is almost certainly not necessary).
Any product that calls AddEnglishAndGerman()
will automatically be upgraded as well. A product can avoid this change by calling AddAmericanEnglishAndSwissGerman()
instead.
A product that uses the new languages will have to replace all fields in reports targeted at "en-US" and "de-CH" to target "en" and "de" instead.
A product that does use the new default languages will have to determine how to migrate database fields created for languages that are no longer explicitly supported. If the model includes value-lists (enums) or multi-language properties , the application will have to migrate the database schema to update multi-language fields (e.g. "caption_en_us" => "caption_en").
A product that sets MetaIds
manually will migrate without modification (Quino will rename the property in the database).
A product that does not set MetaIds
(this has been the default in Quino since version 2) will have a MetaID mismatch because the name has changed.
By default, Quino will migrate by attempting to drop, then re-create multi-language properties. In the case of value-list captions, this is harmless (since the data stored in these tables are generated wholly from the metadata). For actual multi-language properties with user data in them, this is a problem.
The simple solution is to call UseLegacyLanguageMappingFinalizerBuilder()
during application configuration to ensure a smooth migration (Quino will rename the property in the database).
A product that updates its languages should regenerate code to update any generated language-specific properties. Properties that had previously been generated as, e.g. Caption_en_us
will now be Caption_en
.
The title is a bit specific for this blog post, but that's the gist of it: we ended up with a bunch of references to an in-between version of .NET (4.6.1) that was falsely advertising itself as a more optimal candidate for satisfying 4.6.2 dependencies. This is a known issue; there are several links to MS GitHub issues below.
In this blog, I will discuss direct vs. transient dependencies as well as internal vs. runtime dependencies.
If you've run into problems with an application targeted to .NET Framework 4.6.2 that does not compile on certain machines, it's possible that the binding redirects Visual Studio has generated for you use versions of assemblies that aren't installed anywhere but on a machine with Visual Studio installed.
How I solved this issue:
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
to your project)The product should now run locally and on other machines.
For more details, background and the story of how I ran into and solved this problem, read on.
What do we mean when we say that we "build" an application?
Building is the process of taking a set of inputs and producing an artifact targeted at a certain runtime. Some of these inputs are included directly while others are linked externally.
The machine does exactly what you tell it to, so it's up to you to make sure that your instructions are as precise as possible. However, you also want your application to be flexible so that it can run on as wide an array of environments as possible.
Your source code consists of declarations. We've generally got the direct inputs under control. The code compiles and produces artifacts as expected. It's the external-input declarations where things go awry.
What kind of external inputs does our application have?
How is this stitched together to produce the application that is executed?
The NuGet dependencies are resolved at build time. All resources are pulled and added to the release on the build machine. There are no run-time decisions to make about which versions of which assemblies to use.
Dependencies come in two flavors:
It is with the transient references that we run into issues. The following situations can occur:
An application generally includes an app.config
(desktop applications or services) or web.config
XML file that includes a section where binding redirects are listed. A binding redirect indicates the range of versions that can be mapped (or redirected) to a certain fixed version (which is generally also included as a direct dependency).
A redirect looks like this (a more-complete form is further below):
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
When the direct dependency is updated, the binding redirect must be updated as well (generally by updating the maximum version number in the range and the version number of the target of the redirect). NuGet does this for you when you're using package.config
. If you're using Package References, you must update these manually. This situation is currently not so good, as it increases the likelihood that your binding redirects remain too restrictive.
NuGet packages are resolved at build time. These dependencies are delivered as part of the deployment. If they could be resolved on the build machine, then they are unlikely to cause issues on the deployment machine.
Where the trouble comes in is with dependencies that are resolved at execution time rather than build time. The .NET Framework assemblies are resolved in this manner. That is, an application that targets .NET Framework expects certain versions of certain assemblies to be available on the deployment machine.
We mentioned above that the algorithm sometimes chooses the desired version or higher. This is not the case for dependencies that are in the assembly-binding redirects. Adding an explicit redirect locks the version that can be used.
This is generally a good idea as it increases the likelihood that the application will only run in a deployment environment that is extremely close or identical to the development, building or testing environment.
How can we avoid these pesky run-time dependencies? There are several ways that people have come up with, in increasing order of flexibility:
To sum up:
Our application targets .NET Framework (for now). We're looking into .NET Core, but aren't ready to take that step yet.
To sum up the information from above, problems arise when the build machine contains components that are not available on the deployment machine.
How can this happen? Won't the deployment machine just use the best match for the directives included in the build?
Ordinarily, it would. However, if you remember our discussion of assembly-binding redirects above, those are set in stone. What if you included binding redirects that required versions of system dependencies that are only available on your build machine ... or even your developer machine?
We actually discovered an issue in our deployment because the API server was running, but the Authentication server was not. The Authentication server was crashing because it couldn't find the runtime it needed in order to compile its Razor views (it has ASP.Net MVC components). We only discovered this issue on the deployment server because the views were only ever compiled on-the-fly.
To catch these errors earlier in the deployment process, you can enable pre-compiling views in release mode so that the build server will fail to compile instead of a producing a build that will sometimes fail to run.
Add the <MvcBuildViews>true</MvcBuildViews>
to any MVC projects in the PropertyGroup
for the release build, as shown in the example below:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>6</LangVersion>
<MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>
We mentioned above that NuGet is capable of updating these redirects when the target version changes. An example is shown below. As you can see, they're not very easy to write:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Extensions" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0"/>
</dependentAssembly>
<!-- Other bindings... -->
</assemblyBinding>
</runtime>
</configuration>
Most bindings are created automatically when MSBuild emits a warning that one would be required in order to avoid potential runtime errors. If you compile with MSBuild in Visual Studio, the warning indicates that you can double-click the warning to automatically generate a binding.
If the warning doesn't indicate this, then it will tell you that you should add the following to your project file:
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
After that, you can rebuild to show the new warning, double-click it and generate your assembly-binding redirect.
When MSBuild generates a redirect, it uses the highest version of the dependency that it found on the build machine. In most cases, this will be the developer machine. A developer machine tends to have more versions of the runtime targets installed than either the build or the deployment machine.
A Visual Studio installation, in particular, includes myriad runtime targets, including many that you're not using or targeting. These are available to MSBuild but are ordinarily ignored in favor of more appropriate ones.
That is, unless there's a bit of a bug in one or more of the assemblies included with one of the SDKs...as there is with the net461 distribution in Visual Studio 2017.
Even if you are targeting .NET Framework 4.6.2, MSBuild will still sometimes reference assemblies from the 461 distribution because the assemblies are incorrectly marked as having a higher version than those in 4.6.2 and are taken first.
I found the following resources somewhat useful in explaining the problem (though none really offer a solution):
How can you fix the problem if you're affected?
You'll generally have a crash on the deployment server that indicates a certain assembly could not be loaded (e.g. System.Runtime
). If you show the properties for that reference in your web application, do you see the path C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461
somewhere in there? If so, then your build machine is linking in references to this incorrect version. If you let MSBuild generate binding redirects with those referenced paths, they will refer to versions of runtime components that do not generally exist on a deployment machine.
Tips for cleaning up:
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461
in the output?A sample warning message:
[ResolvePackageFileConflicts] Encountered conflict between 'Platform:System.Collections.dll' and 'CopyLocal:C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Collections.dll'. Choosing 'CopyLocal:C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Collections.dll' because AssemblyVersion '4.0.11.0' is greater than '4.0.10.0'.
As mentioned above, but reiterated here, this what I did to finally stabilize my applications:
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
to your project)When you install any update of Visual Studio, it will silently repair these missing files for you. So be aware and check the folder after any installations or upgrades to make sure that the problem doesn't creep up on you again.
In a recent article, we outlined a roadmap to .NET Standard and .NET Core. We've made really good progress on that front: we have a branch of Quino-Standard that targets .NET Standard for class libraries and .NET Core for utilities and tests. So far, we've smoke-tested these packages with Quino-WebApi. Our next steps there are to convert Quino-WebApi to .NET Standard and .NET Core as well. We'll let you know when it's ready, but progress is steady and promising.
With so much progress on several fronts, we want to address how we get Quino from our servers to our customers and users.
Currently, we provide access to a private fileshare for customers. They download the NuGet packages for the release they want. They copy these to a local folder and bind it as a NuGet source for their installations.
In order to make a build available to customers, we have to publish that build by deploying it and copying the files to our file share. This process has been streamlined considerably so that it really just involves telling our CI server (TeamCity) to deploy a new release (official or pre-). From there, we download the ZIP and copy it to the fileshare.
Encodo developers don't have to use the fileshare because we can pull packages directly from TeamCity as soon as they're available. This is a much more comfortable experience and feels much more like working with nuget.org directly.
The debugging story with external code in .NET is much better than it used to be (spoiler: it was almost impossible, even with Microsoft sources), but it's not as smooth as it should be. This is mostly because NuGet started out as a packaging mechanism for binary dependencies published by vendors with proprietary/commerical products. It's only in recent year(s) that packages are predominantly open-source.
In fact, debugging with third-party sources – even without NuGet involved – has never been easy with .NET/Visual Studio.
Currently, all Quino developers must download the sources separately (also available from TeamCity or the file-share) in order to use source-level debugging.
Binding these sources to the debugger is relatively straightforward but cumbersome. Binding these sources to ReSharper is even more cumbersome and somewhat unreliable, to boot. I've created the issue Add an option to let the user search for external sources explicitly (as with the VS debugger) when navigating in the hopes that this will improve in a future version. JetBrains has already fixed one of my issues in this are (Navigate to interface/enum/non-method symbol in Nuget-package assembly does not use external sources), so I'm hopeful that they'll appreciate this suggestion, as well.
The use case I cited in the issue above is,
Developers using NuGet packages that include sources or for which sources are available want to set breakpoints in third-party source code. Ideally, a developer would be able to use R# to navigate through these sources (e.g. via F12) to drill down into the code and set a breakpoint that will actually be triggered in the debugger.
As it is, navigation in these sources is so spotty that you often end up in decompiled code and are forced to use the file-explorer in Windows to find the file and then drag/drop it to Visual Studio where you can set a breakpoint that will work.
The gist of the solution I propose is to have R# ask the user where missing sources are before decompiling (as the Visual Studio debugger does).
There is hope on the horizon, though: Nuget is going to address the debugging/symbols/sources workflow in an upcoming release. The overview is at NuGet Package Debugging & Symbols Improvements and the issue is Improve NuGet package debugging and symbols experience.
Once this feature lands, Visual Studio will offer seamless support for debugging packages hosted on nuget.org. Since we're using TeamCity to host our packages, we need JetBrains to [Add support for NuGet Server API v3|https://youtrack.jetbrains.com/issue/TW-47289] in order to benefit from the improved experience. Currently, our customers are out of luck even if JetBrains releases simultaneously (because our TeamCity is not available publicly).
I've created an issue for Quino, Make Quino Nuget packages available publicly to track our progress in providing Quino packages to our customers in a more convenient way that also benefits from improvements to the debugging workflow with Nuget Packages.
If we published Quino packages to NuGet (or MyGet, which allows private packages), then we would have the benefit of the latest Nuget protocol/improvements for both ourselves and our customers as soon as it's available. Alternatively, we could also proxy our TeamCity feed publicly. We're still considering our options there.
As you can see, we're always thinking about the development experience for both our developers and our customers. We're fine-tuning on several fronts to make developing and debugging with Quino a seamless experience for all developers on all platforms.
We'll keep you posted.
With Quino 5, we've gotten to a pretty good place organizationally. Dependencies are well-separated into projects—and there are almost 150 of them.
We can use code-coverage, solution-wide-analysis and so on without a problem. TeamCity runs the ~10,000 tests quickly enough to provide feedback in a reasonable time. The tests run even more quickly on our desktops. It's a pretty comfortable and efficient experience, overall.
As of Quino 5, all Quino-related code was still in one repository and included in a single solution file. Luckily for us, Visual Studio 2017 (and Rider and Visual Studio for Mac) were able to keep up quite well with such a large solution. Recent improvements to performance kept the experience quite comfortable on a reasonably equipped developer machine.
Having everything in one place is both an advantage and disadvantage: when we make adjustments to low-level shared code, the refactoring is applied in all dependent components, automatically. If it's not 100% automatic, at least we know where we need to make changes in dependent components. This provides immediate feedback on any API changes, letting us fine-tune and adjust until the API is appropriate for known use cases.
On the other hand, having everything in one place means that you must make sure that your API not only works for but compiles and tests against components that you may not immediately be interested in.
For example, we've been pushing much harder on the web front lately. Changes we make in the web components (or in the underlying Quino core) must also work immediately for dependent Winform and WPF components. Otherwise, the solution doesn't compile and tests fail.
While this setup had its benefits, the drawbacks were becoming more painful. We wanted to be able to work on one platform without worrying about all of the others.
On top of that, all code in one place is no longer possible with cross-platform support. Some code—Winform and WPF—doesn't run on Mac or Linux.1
The time had come to separate Quino into a few larger repositories.
We decided to split along platform-specific lines.
The Quino-WebApi and Quino-Windows solution will consume Quino-Standard via NuGet packages, just like any other Quino-based product. And, just like any Quino-based product, they will be able to choose when to upgrade to a newer version of Quino-Standard.
Part of the motivation for the split is cross-platform support. The goal is to target all assemblies in Quino-Standard to .NET Standard 2.0. The large core of Quino will be available on all platforms supported by .NET Core 2.0 and higher.
This work is quite far along and we expect to complete it by August 2018.
As of Quino 5.0.5, we've moved web-based code to its own repository and set up a parallel deployment for it. Currently, the assemblies still target .NET Framework, but the goal here is to target class libraries to .NET Standard and to use .NET Core for all tests and sample web projects.
We expect to complete this work by August 2018 as well.
We will be moving all Winform and WPF code to its own repository, setting it up with its own deployment (as we did with Quino-WebApi). These projects will remain targeted to .NET Framework 4.6.2 (the lowest version that supports interop with .NET Standard assemblies).
We expect this work to be completed by July 2018.
One goal we have with this change is to be able to use Quino code from Xamarin projects. Any support we build for mobile projects will proceed in a separate repository from the very beginning.
We'll keep you posted on work and improvements and news in this area.
Customer will, for the most part, not notice this change, except in minor version numbers. Core and platform versions may (and almost certainly will) diverge between major versions. For major versions, we plan to ship all platforms with a single version number.
I know, Winform can be made to run on Mac using Mono. And WPF may eventually become a target of Xamarin. But a large part of our Winform UI uses the Developer Express components, which aren't going to run on a Mac. And the plans for WPF on Mac/Linux are still quite up in the air right now.↩
The Quino roadmap shows you where we're headed. How do we plan to get there?
A few years back, we made a big leap in Quino 2.0 to split up dependencies in anticipation of the initial release of .NET Core. Three tools were indispensable: ReSharper, NDepend and, of course, Visual Studio. Almost all .NET developers use Visual Studio, many use ReSharper and most should have at least heard of NDepend.
At the time, I wrote a series of articles on the migration from two monolithic assemblies (Encodo
and Quino
) to dozens of layered and task-specific assemblies that allows applications to include our software in a much more fine-grained manner. As you can see from the articles, NDepend was the main tool I used for finding and tracking dependencies.1 I used ReSharper to disentangle them.
Since then, I've not taken advantage of NDepend's features for maintaining architecture as much as I'd like. I recently fired it up again to see where Quino stands now, with 5.0 in beta.
But, first, let's think about why we're using yet another tool for examining our code. Since I started using NDepend, other tools have improved their support for helping a developer maintain code quality.
IDisposable
pattern. The Portability Analysis is essential for moving libraries to .NET Standard but doesn't offer any insight into architectural violations like NDepend does.With a concrete .NET Core/Standard project in the wings/under development, we're finally ready to finish our push to make Quino Core ready for cross-platform development. For that, we're going to need NDepend's help, I think. Let's take a look at where we stand today.
The first step is to choose what you want to cover. In the past, I've selected specific assemblies that corresponded to the "Core". I usually do the same when building code-coverage results, because the UI assemblies tend to skew the results heavily. As noted in a footnote below, we're starting an effort to separate Quino into high-level components (roughly, a core with satellites like Winform, WPF and Web). Once we've done that, the health of the core itself should be more apparent (I hope).
For starters, though, I've thrown all assemblies in for both NDepend analysis as well as code coverage. Let's see how things stand overall.
The amount of information can be quite daunting but the latest incarnation of the dashboard is quite easy to read. All data is presented with a current number and a delta from the analysis against which you're comparing. Since I haven't run an analysis in a while, there's no previous data against which to compare, but that's OK.
Let's start with the positive.
Now to the cool part: you can click anything in the NDepend dashboard to see a full list of all of the data in the panel.
Click the "B" on technical debt and you'll see an itemized and further-drillable list of the grades for all code elements. From there, you can see what led to the grade. By clicking the "Explore Debt" button, you get a drop-down list of pre-selected reports like "Types Hot Spots".
Click lines of code and you get a breakdown of which projects/files/types/methods have the most lines of code
Click failed quality gates to see where you've got the most major problems (Quino currently has 3 categories)
Click "Critical" or "Violated" rules to see architectural rules that you're violating. As with everything in NDepend, you can pick and choose which rules should apply. I use the default set of rules in Quino.
Most of our critical issues are for mutually-dependent namespaces. This is most likely not root namespaces crossing each other (though we'd like to get rid of those ASAP) but sub-namespaces that refer back to the root and vice-versa. This isn't necessarily a no-go, but it's definitely something to watch out for.
There are so many interesting things in these reports:
Click the "Low" issues (Quino has over 46,000!) and you can see that NDepend analyzes your code at an incredibly low level of granularity
Finallly, there's absolutely everything, which includes boxing/unboxing issues 7, method-names too long, large interfaces, large instances (could also be generated classes).
These already marked as low, so don't worry that NDepend just rains information down on you. Stick to the critical/high violations and you'll have real issues to deal with (i.e. code that might actually lead to bugs rather than code that leads to maintenance issues or incurs technical debt, both of which are more long-term issues).
What you'll also notice in the screenshots that NDepend doesn't just provide pre-baked reports: everything is based on its query language. That is, NDepend's analysis is lightning fast (takes only a few seconds for all of Quino) during which it builds up a huge database of information about your code that it then queries in real-time. NDepends provides a ton of pre-built queries linked from all over the UI, but you can adjust any of those queries in the pane at the top to tweak the results. The syntax is Linq to Sql and there are a ton of comments in the query to help you figure out what else you can do with it.
As noted above, the amount of information can be overwhelming, but just hang in there and figure out what NDepend is trying to tell you. You can pin or hide a lot of the floating windows if it's all just a bit too much at first.
In our case, the test assemblies have more technical debt than the code that it tests. This isn't optimal, but it's better than the other way around. You might be tempted to exclude test assemblies from the analysis, to boost your grade, but I think that's a bad idea. Testing code is production code. Make it just as good as the code it tests to ensure overall quality.
I did a quick comparison between Quino 4 and Quino 5 and we're moving in the right direction: the estimation of work required to get to grade A was already cut in half, so we've made good progress even without NDepend. I'm quite looking forward to using NDepend more regularly in the coming months. I've got my work cut out for me.
--
Many thanks to Patrick Smacchia of NDepend for generously providing an evaluator's license to me over the years.↩
We came up with a plan for reducing the size of the core solution in a recent architecture meeting. More on that in a subsequent blog post.↩
Quino has 10,000 tests, many of which are integration tests, so a change to a highly shared component would trigger thousands of tests to run, possibly for minutes. I can't see how it would be efficient to run tests continuously as I type in Quino. I've used continuous testing in smaller projects and it's really wonderful (both with ReSharper and also Wallaby for TypeScript), but it doesn't work so well with Quino because of its size and highly generalized nature.↩
I ran the analysis on both Quino 4 and Quino 5, but wasn't able to directly compare results because I think I inadvertently threw them away with our nant clean
command. I'd moved the ndepend out
folder to the common folder and our command wiped out the previous results. I'll work on persisting those better in the future.↩
I generated coverage data using DotCover, but realized only later that I should have configured it to generate NDepend-compatible coverage data (as detailed in NDepend Coverage Data. I'll have to do that and run it again. For now, no coverage data in NDepend. This is what it looks like in DotCover, though. Not too shabby:↩
Getting that documentation out to our developers is also a work-in-progress. Until recently, we've been stymied by the lack of a good tool and ugly templates. But recently we added DocFX support to Quino and the generated documentation is gorgeous. There'll be a post hopefully soon announcing the public availability of Quino documentation.↩
There's probably a lot of low-hanging fruit of inadvertent allocations here. On the other hand, if they're not code hot paths, then they're mostly harmless. It's more a matter of coding consistently. There's also an extension for ReSharper (the "Heap Allocations Viewer") that indicates allocations directly in the IDE, in real-time. I have it installed, and it's nice to see where I'm incurring allocations.↩
The summary below describes major new features, items of note and breaking changes. The full list of issues is in the release notes and is available to those with access to the Encodo issue tracker.
Unless we find a blocking issue that can't be fixed with a patch to the product, this will be the last release on the 4.x branch.
IExternalLoggerFactory
has been renamed to IExternalLoggerProvider
ExternalLoggerFactory
has been renamed to ExternalLoggerProvider
NullExternalLoggerFactory
has been renamed to NullExternalLoggerProvider
IUserCredentials.AuthenticationToken
is now an IToken
instead of a stringThe summary below describes major new features, items of note and breaking changes. The full list of issues is in the release notes and is available to those with access to the Encodo issue tracker.
ReportDefinitionParameter.Hidden
now has the default value false
. Integrating this release will trigger a schema migration to adjust that value in the database.The summary below describes major new features, items of note and breaking changes. The full list of issues is in the release notes and is available to those with access to the Encodo issue tracker.
The summary below describes major new features, items of note and breaking changes. The full list of issues is in the release notes and is available to those with access to the Encodo issue tracker.
ConfigureWhen()
to apply application configuration based on configuration data (QNO-5688)