*Few changes so far, other than that Quino-Windows targets .NET Framework 4.7.2 and DevExpress 18.2.5

Highlights

Inline Documentation: Quino packages now consistently include inline/developer documentation, in the form of *.xml files. IDEs use these files to provide real-time documentation in tooltips and code-completion.

Nullability Attributes: Attributes like NotNull, CanBeNull, [Pure], etc. are now included in Quino assemblies. Tools like R# use these attributes during code-analysis to find possible bugs and improve warnings and suggestions.

Debugging: We've laid a bunch of groundwork for SourceLink. If the NuGet server supports this protocol, then Visual Studio automatically offers to download source code for debugging. This feature will be fully enabled in subsequent releases, after we've upgraded our package server.

Cross-platform: We've made more improvements to how Quino-Standard compiles and runs under Linux or MacOS. All tests now run on Linux or MacOS as well as Windows.

Containers: Improved integration and usage of Docker for local development and on build servers

Roslyn: Encodo.Compilers now uses Roslyn to provide compiling services. Quino-Standard uses these from tests to verify generated code. As of this release, C#6 and C#7 features are supported. Also, the compiler support is available on all platforms.

Breaking Changes

UseRunModeCommand() is no longer available by default. Applications have to opt-in to the rm -debug setting. Please see the Debugging documentation for assistance in converting to the new configuration methods.

KeyValueNode.GetValue() no longer accepts a parameter of type logger. All logging is now done to the central logger. If you still need to collect messages from that operation, then see ConfigurableLoggerExtensions.UseLogger() or ConfigurableLoggerExtensions.UseInMemoryLogger().

IDatabaseProperties.Collation is now of type string rather than Collation. This change was made to allow an application to specify exactly the desired collation without having Quino reinterpret it or do matching.

Similarly, ISqlServerCollationTools.GetEncodingAndCollation() now returns a tuple of (Encoding, string) rather than a tuple of (Encoding, Collation).

The constructor of NamedValueNode has changed. Instead, you should use the abstraction INamedValueNodeTools.CreateNode()or INamedValueNodeTools.CreateRootNode().

If you're familiar with the topic, you might be recoiling in horror. It would be unclear, though, whether you're recoiling from the "using Collab" part or the "using Collab with Git" part.

Neither is as straightforward as I'd hoped.

tl;dr: If you have to use Collab with Unity, but want to back it up with Git, disable core.autocrlf1 and add * -text to the .gitattributes.

Collab's Drawbacks

Collab is the source-control system integrated into the Unity IDE.

It was built for designers to be able to do some version control, but not much more. Even with its limited scope, it's a poor tool.

The functionality horror

The system does not ever show you any differences, neither in the web UI nor the local UI, neither for uncommitted nor committed files

Some changes cannot be reverted. No reason is given.

You can only delete new files from the file system.

There is no support for renaming

Reverting to a known commit has worked for me exactly once out of about 10 tries. The operation fails with an Error occurred and no further information. If you really get stuck, your only choice is to restore the entire workspace by re-cloning/re-downloading it.

Conflict resolution is poorly supported, although it works better than expected (it integrates with BeyondCompare, thank goodness).

The usability horror

The UI only lets you commit all changed files at once.

There is no notion of "commits".

You can’t commit individual files or chunks.

There is no staging area.

You can't exclude files.

You can ignore them completely, but that doesn't help.

The UI is only accessible via mouse from the menu bar.

You can sometimes revert folders (sometimes you can't, again with an Error occurred message), but you can't revert arbitrary groups of files.

The UI is almost entirely in that custom drop-down menu.

You can scroll through your changed files, but you can't expand the menu to show more files at once.

You can show a commit history, but there are no diffs. None.

There aren't even any diffs in the web version of the UI, which is marginally better, but read-only.

Pair Git with Collab

This is really dangerous, especially with Unity projects. There is so much in a Unity project without a proper "Undo" that you very often want to return to a known good version.

So what can we do to improve this situation? We would like to use Git instead of Collab.

However, we have to respect the capabilities and know-how of the designers on our team, who don't know how to use Git.

On our current project, there's no time to train everyone on Git—and they already know how to use Collab and don't feel tremendously limited by it.

Remember, any source control is better than no source control. The designers are regularly backing up their work now. In its defense, Collab is definitely better than nothing (or using a file-share or some other weak form of code-sharing).

Instead, those of us who know Git are using Git alongside Collab.

It kind of works...

We started naively, with all of our default settings in Git. Our workflow was:

Pull in Unity/Collab

Fetch from Git/Rebase to head (we actually just use "pull with rebase")

Unfortunately, we would often end up with a ton of files marked as changed in Collab. These were always line-ending differences. As mentioned above, Collab is not a good tool for reverting changes.

The project has time constraints—it's a prototype for a conference, with a hard deadline—so, despite its limitations, we reverted in Collab and updated Git with the line-endings that Collab expected.

We limped along like this for a bit, but with two developers on Git/Collab on Windows and one designer on Collab on Mac, we were spending too much time "fixing up" files. The benefit of having Git was outweighed by the problems it caused with Collab.

Know Your Enemy

So we investigated what was really going on. The following screenshots show that Collab doesn't seem to care about line-endings. They're all over the map.

Configuring Git

Git, on the other hand, really cares about line-endings. By default, Git will transform the line-endings in files that it considers to be text files (this part is important later) to the line-ending of the local platform.

In the repository, all text files are LF-only. If you work on MacOS or Linux, line-endings in the workspace are unchanged; if you work on Windows, Git changes all of these line-endings to CRLF on checkout—and back to LF on commit.

Our first "fix" was to turn off the core.autocrlf option in the local Git repository.

git config --local core.autocrlf false

We thought this would fix everything since now Git was no longer transforming our line-endings on commit and checkout.

This turned out to be only part of the problem, though. As you can see above, the text files in the repository have an arbitrary mix of line-endings already. Even with the feature turned off, Git was still normalizing line-endings to LF on Windows.

The only thing we'd changed so far is to stop using the CRLF instead of LF. Any time we git reset, for example, the line-endings in our workspace would still end up being different than what was in Git or Collab.

Git: Stop doing stuff

What we really want is for Git to stop changing any line-endings at all.

This isn't part of the command-line configuration, though. Instead, you have to set up .gitattributes. Git has default settings that determine which files it treats as which types. We wanted to adjust these default settings by telling Git that, in this repository, it should treat no files as text.

Once we knew this, it's quite easy to configure. Simply add a .gitattributes file to the root of the repository, with the following contents:

* -text

This translates to "do not treat any file as text" (i.e. match all files; disable text-handling).

Conclusion

With these settings, the two developers were able to reset their workspaces and both Git and Collab were happy. Collab is still a sub-par tool, but we can now work with designers and still have Git to allow the developers to use a better workflow.

The designers using only Collab were completely unaffected by our changes.

Technically, I don't think you have to change the autocrlf setting. Turning off text-handling in Git should suffice. However, I haven't tested with this feature left on and, due to time-constraints, am not going to risk it.↩

Microsoft has recently made a lot of their .NET code open-source. Not only is the code for many of the base libraries open-source but also the code for the runtime itself. On top of that, basic .NET development is now much more open to community involvement.

C# 6 Recap

You may be surprised at the version number "7" -- aren't we still waiting for C# 6 to be officially released? Yes, we are.

If you'll recall, the primary feature added to C# 5 was support for asynchronous operations through the async/await keywords. Most .NET programmers are only getting around to using this rather far- and deep-reaching feature, to say nothing of the new C# 6 features that are almost officially available.

Auto-Property Initializers: initialize a property in the declaration rather than in the constructor or on an otherwise unnecessary local variable.

Out Parameter Declaration: An out parameter can now be declared inline with var or a specific type. This avoids the ugly variable declaration outside of a call to a Try* method.

Using Static Class: using can now be used with with a static class as well as a namespace. Direct access to methods and properties of a static class should clean up some code considerably.

String Interpolation: Instead of using string.Format() and numbered parameters for formatting, C# 6 allows expressions to be embedded directly in a string (á la PHP): e.g. "{Name} logged in at {Time}"

nameof(): This language feature gets the name of the element passed to it; useful for data-binding, logging or anything that refers to variables or properties.

Null-conditional operator: This feature reduces conditional, null-checking cruft by returning null when the target of a call is null. E.g. company.People?[0]?.ContactInfo?.BusinessAddress.Street includes three null-checks

Looking ahead to C# 7

If the idea of using await correctly or wrapping your head around the C# 6 features outlined above doesn't already make your poor head spin, then let's move on to language features that aren't even close to being implemented yet.

Pattern-matching: C# has been ogling its similarly named colleague F# for a while. One of the major ideas on the table for C# is improving the ability to represent as well as match against various types of pure data, with an emphasis on immutable data.

Metaprogramming: Another focus for C# is reducing boilerplate and capturing common code-generation patterns. They're thinking of delegation of interfaces through composition. Also welcome would be an improvement in the expressiveness of generic constraints.

Controlling Nullability: Another idea is to be able to declare reference types that can never be null at compile-time (where reasonable -- they do acknowledge that they may end up with a "less ambitious approach").

Readonly parameters and locals: Being able to express when change is allowed is a powerful form of expressiveness. C# 7 may include the ability to make local variables and parameters readonly. This will help avoid accidental side-effects.

Lambda capture lists: One of the issues with closures is that they currently just close over any referenced variables. The compiler just makes this happen and for the most part works as expected. When it doesn't work as expected, it creates subtle bugs that lead to leaks, race conditions and all sorts of hairy situations that are difficult to debug.

If you throw in the increased use of and nesting of lambda calls, you end up with subtle bugs buried in frameworks and libraries that are nearly impossible to tease out.

The idea of this feature is to allow a lambda to explicitly capture variables and perhaps even indicate whether the capture is read-only. Any additional capture would be flagged by the compiler or tools as an error.Contracts(!): And, finally, this is the feature I'm most excited about because I've been waiting for integrated language support for Design by Contract for literally decades1, ever since I read the Object-Oriented Software Construction 2 (OOSC2) for the first time. The design document doesn't say much about it, but mentions that ".NET already has a contract system", the weaknesses of which I've written about before. Torgersen writes:

When you think about how much code is currently occupied with arguments and result checking, this certainly seems like an attractive way to reduce code bloat and improve readability.

...and expressiveness and provability!

There are a bunch of User Voice issues that I can't encourage you enough to vote for so we can finally get this feature:

With some or all of these improvements, C# 7 would move much closer to a provable language at compile-time, an improvement over being a safe language at run-time.

We can already indicate that instance data or properties are readonly. We can already mark methods as static to prevent the use of this. We can use ReSharper [NotNull] attributes to (kinda) enforce non-null references without using structs and incurring the debt of value-passing and -copying semantics.

I'm already quite happy with C# 5, but if you throw in some or all of the stuff outlined above, I'll be even happier. I'll still have stuff I can think of to increase expressiveness -- covariant return types for polymorphic methods or anchored types or relaxed contravariant type-conformance -- but this next set of features being discussed sounds really, really good.

I love the features of the language Eiffel, but haven't ever been able to use it for work. The tools and IDE are a bit stuck in the past (very dated on Windows; X11 required on OS X). The language is super-strong, with native support for contracts, anchored types, null-safe programming, contravariant type-conformance, covariant return types and probably much more that C# is slowly but surely including with each version. Unfair? I've been writing about this progress for years (from newest to oldest):↩

We would like to remove the logger parameter. So we deprecate the method above and declare the new method.

bool TryGetValue<T>(
out T value,
TKey key = default(TKey)
);

Now the compiler/ReSharper notifies you that there will be an ambiguity if a caller does not pass a logger. How to resolve this? Well, we can just remove the default value for that parameter in the obsolete method.

But now you've got another problem: The parameter logger cannot come after the key parameter because it doesn't have a default value.

So, now you'd have to move the logger parameter in front of the key parameter. This will cause a compile error in clients, which is what we were trying to avoid in the first place.

In this case, we have a couple of sub-optimal options.

Multiple Releases

Use a different name for the new API (e.g. TryGetValueEx à la Windows) in the next major version, then switch the name back in the version after that and finally remove the obsolete member in yet another version.

That is,

in version n, TryGetValue (with logger) is obsolete and users are told to use TryGetValueEx (no logger)

in version n+1, TryGetValueEx (no logger) is obsolete and users are told to use TryGetValue (no logger)

in version n+2, we finally remove TryGetValueEx.

This is a lot of work and requires three upgrades to accomplish. You really need to stay on the ball in order to get this kind of change integrated and it takes a non-trivial amount of time and effort.

We generally don't use this method, as our customers are developers and can deal with a compile error or two, especially when it's noted in the release notes and the workaround is fairly obvious (e.g. the logger parameter is just no longer required).

Remove instead of deprecating

Accept that there will be a compile error and soften the landing as much as possible for customers by noting it in the release notes.

In late 2011 and early 2012, Encodo designed a querying language for Quino. Quino has an ORM that, combined with .NET Linq provides a powerful querying interface for developers. QQL is a DSL that brings this power to non-developers.

QQL never made it to implementation---only specification. In the meantime, the world moved on and we have common, generic querying APIs like OData. The time for QQL is past, but the specification is still an interesting artifact, in its own right.

Who knows? Maybe we'll get around to implementing some of it, at some point.

The following excerpts should give you an idea of what you're in for, should you download and read the 80-page document.

Details

The TOC lists the following top-level chapters:

Introduction

Examples

Context & Scopes

Standard Queries

Grouping Queries

Evaluation

Syntax

Data Types and Operators

Libraries

Best Practices

Implementation Details

Future Enhancements

From the abstract in the document:

The Quino Query Language (QQL) defines a syntax and semantics for formulating data requests against hierarchical data structures. It is easy to read and learn both for those familiar with SQL and non-programmers with a certain capacity for abstract thinking (i.e. power users). Learning only a few basic rules is enough to allow a user to quickly determine which data will be returned by all but the more complex queries. As with any other language, more complex concepts result in more complex texts, but the syntax of QQL limits these cases.

From the overview:

QQL defines a syntax and semantics for writing queries against hierarchical data structures. A query describes a set of data by choosing an initial context in the data and specifying which data are to be returned and how the results are to be organized. An execution engine generates this result by applying the query to the data.

Examples

Standard Projections

The follow is from chapter 2.1, "Simple Standard Query":

The following query returns the first and last name of all active people as well as their 10 most recent time entries, reverse-sorted first by last name, then by first name.

In chapter 2, there are also "2.2 Intermediate Standard Query" and "2.3 Complex Standard Query" examples.

Grouping Projections

The following is from chapter 2.4, "Simple Grouping Query":

The following query groups active people by last name and returns the age of the youngest person and the maximum contracts for each last name. Results are ordered by the maximum contracts for each group and then by last name.

Any software product should have a version number. This article will answer the following questions about how Encodo works with them.

How do we choose a version number?

What parts does a version number have?

What do these parts mean?

How do different stakeholders interpret the number?

What conventions exist for choosing numbers?

Who chooses and sets these parts?

Stakeholders

In decreasing order of expected expertise,

Developers: Writes the software; may change version numbers

Testers: Tests the software; highly interested in version numbers that make sense

End users: Uses the software as a black box

The intended audience of this document is developers.

Definitions and Assumptions

Build servers, not developer desktops, produce artifacts

The source-control system is Git

The quino command-line tool is installed on all machines. This tool can read and write version numbers for any .NET solution, regardless of which of the many version-numbering methods a given solution actually uses.

A software library is a package or product that has a developer as an end user

A breaking change in a software library causes one of the following

a build error

an API to behave differently in a way that cannot be justified as a bug fix

Semantic versions

Encodo uses semantic versions. This scheme has a strict ordering that allows you to determine which version is "newer". It indicates pre-releases (e.g. alphas, betas, rcs) with a "minus", as shown below.

Conventions

Uniqueness for official releases

There will only ever be one artifact of an official release corresponding to a given "main" version number.

That is, if 1.0.0.523 exists, then there will never be a 1.0.0.524. This is due the fact that the build number (e.g. 524) is purely for auditing.

For example, suppose your software uses a NuGet package with version 1.0.0.523. NuGet will not offer to upgrade to 1.0.0.524.

Pre-release Labels

There are no restrictions on the labels for pre-releases. However, it's recommended to use one of the following:

alpha

beta

rc

Be aware that if you choose a different label, then it is ordered alphabetically relative to the other pre-releases.

For example, if you were to use the label pre-release to produce the version 0.9.0-prealpha21, then that version is considered to be higher than 0.0.0-alpha34. A tool like NuGet will not see the latter version as an upgrade.

Release branches

The name of a release branch should be the major version of that release. E.g. release/1 for version 1.x.x.x.

Pre-release branches

The name of a pre-release branch should be of the form feature/[label] where [label] is one of the labels recommended above. It's also OK to use a personal branch to create a pre-release build, as in mvb/[label].

Setting the base version

A developer uses the quino tool to set the version.

For example, to set the version to 1.0.1, execute the following:

quino fix -v 1.0.1.0

The tool will have updated the version number in all relevant files.

Calculating final version

The build server calculates a release's version number as follows,

major: Taken from solution

minor: Taken from solution

maintenance: Read from solution

label: Taken from the Git branch (see below for details)

build: Provided by the build server

Git Branches

The name of the Git branch determines which kind of release to produce.

If the name of the branch matches the glob **/release/*, then it's an official release

Everything else is a pre-release

For example,

origin/release/1

origin/production/release/new

origin/release/

release/1

production/release/new

release/

The name of the branch doesn't influence the version number since an official release doesn't have a label.

Pre-release labels

The label is taken from the last part of the branch name.

For example,

origin/feature/beta yields beta

origin/feature/rc yields rc

origin/mvb/rc yields rc

The following algorithm ensures that the label can be part of a valid semantic version.

Remove invalid characters

Append an X after a trailing digit

Use X if the label is empty (or becomes empty after having removed invalid characters)

For example,

origin/feature/rc1 yields rc1X

origin/feature/linux_compat yields linuxcompat

origin/feature/12 yields X

Examples

Assume that,

the version number in the solution is 0.9.0.0

the build counter on the build server is at 522

Then,

Deploying from branch origin/release/1 produces artifacts with version number 0.9.0.522

Deploying from branch origin/feature/rc produces artifacts with version number 0.9.0-rc522

Overview

At long last, Quino enters the world of .NET Standard and .NET Core. Libraries target .NET Standard 2.0, which means they can all be used with any .NET runtime on any .NET platform (e.g. Mac and Linux). Sample applications and testing assemblies target .NET Core 2.0. Tools like quinogenerate and quinofix target .NET Core 2.1 to take advantage of the standardized external tool-support there.

Furthermore, the Windows, Winform and WPF projects have moved to a separate solution/repository called Quino-Windows.

Quino-Standard is the core on which both Quino-Windows and Quino-WebAPI build.

All core assemblies target .NET Standard 2.0.

All assemblies in Quino-Windows target .NET Framework 4.6.2 because that's the first framework that can interact with .NET Standard (and under which Windows-specific code runs).

All assemblies in Quino-WebAPI currently target .NET Framework 4.6.2. We plan on targeting .NET Core in an upcoming version (tentatively planned for v7).

Breaking Changes

6.0 is a pretty major break from the 5.x release. Although almost all assembly names have stayed the same, we had to move some types around to accommodate targeting .NET Standard with 85% of Quino's code.

APIs

We've tried to support existing code wherever possible, but some compile errors will be unavoidable (e.g. from namespace changes or missing references). In many cases, R#/VS should be able to help repair these errors.

These are the breaking changes that are currently known.

Moved IRunSettings and RunMode from Encodo.Application to Encodo.Core.

References

Any .NET Framework executable that uses assemblies targeting .NET Standard must reference .NET Standard itself. The compiler (MSBuild) in Visual Studio will alert you to add a reference to .NET Standard using NuGet. This applies not just to Winform executables, but also to any unit-test assemblies.

Tools

One piece that has changed significantly is the tool support formerly provided with Quino.Utils. As of version 6, Quino no longer uses NAnt, instead providing dotnet-compatible tools that you can install using common .NET commands. Currently, Quino supports:

dotnet quinofix

dotnet quinogenerate

dotnet quinopack

Please see the tools documentation for more information on how to install and use the new tools.

The standalone Winforms-based tools are in the Quino-Windows download, in the Tools.zip archive.

Plugin support has been moved from Encodo.Application.Standard to Encodo.Plugins

PasswordEncryptionType has been replaced with HashAlgorithm

GetSearchClass(IMetaClass) was moved from Quino.Meta to Quino.Builders

GetLayout no longer has a default value for the layoutType parameter (pass LayoutType.Detail to get the previous behavior)

IKeyValueNode.GetValue() has been moved to an extension method. You may need to include the namespace Encodo.Nodes.

Namespaces in Quino.Bookmarks have been changed from Encodo.Quino.Bookmarks.Bookmarks to Encodo.Quino.Bookmarks.

WPF schema-migration types have been moved from the Quino.Module.Schema.Wpfnamespace to Encodo.Quino.Module.Schema.Wpf.

Some testing support types have been moved from the Tests to the Testing namespace (e.g. EncodoServicesBasedTestsBase and EncodoTestsBase).

Introduced the IFinalizerBuilderProvider so that applications no longer need to include the MetaStandardBuilder in models.

Finalizer Provider

Whiile an application no longer needs to manage the insertion point of the MetaStandardBuilder and therefore no longer needs to override PostFinalize() instead of a more relevant method, this is a breaking change for certain situations. For example, if a builder overrode PostFinalize() and expected the finalizer builders included in MetaStandardBuilder to have already run previously.

The workaround is to either:

Rewrite the logic to avoid depending on expressions/model added by the finalizer builders. (this is what we ended up doing in Punchclock).

Add the builder to the list of finalizer builders (in which case it will be executed last). You can customize the finalizer builders as with an other IProvide using something like the following in your application startup.

Relation Captions

Whereas previously, the captions of the target class were always used, the FallbackCaptionBuilder now only does this if the name of the relation has not been changed from the default.

If the name was changed to something else, then that name is used for relation captions. For example, if you make a relation from a Company to a Person called People, then Quino will still use the captions from the class. This allows your application to set the caption on the class and it will be used for many relations.

However, if you create a similar relation, but rename it to CEO, Quino will no longer use the class captions, since in this case Person and People (and translations) are no longer appropriate. In this case, CEO and CEOs is more appropriate.

In this case, the captions in the default language will be more appropriate but the captions for other languages previously taken from the class will no longer be used on the relation.