Sharing configurations between project files

EDIT: Updated examples based on comment by Laimis: https://mstdn.social/@laimis/110746925177883695

Basically, a dotnet solution is made up of one or more projects. Each of these projects have a project file, .csproj og .vbproj. If you’ve ever been working on a dotnet project, you’d already know this.

When we, the bureau I’m with, create new projects, we need to remember to add all sorts of configurations. All of them are shared between projects. Configurations like these:

<Project>
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
		<Nullable>enable</Nullable>
		<GenerateDocumentationFile>true</GenerateDocumentationFile>
		<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>
</Project>

Further more, we also need to add a reference to, at least, one nuget package: https://www.nuget.org/packages/StyleCop.Analyzers and a reference to a stylecop config file:

<Project>
	<ItemGroup>
		<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
	</ItemGroup>
</Project>

This is a lot to remember when creating new projects. Especially for new developers or when time is short.

To fix this, we can use a “small hack”.

First, create a new .props file: [solution-folder]/configs/shared.props:

<Project>
	<PropertyGroup>
		<TargetFramework>net7.0</TargetFramework>
		<Nullable>enable</Nullable>
		<GenerateDocumentationFile>true</GenerateDocumentationFile>
		<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
		<ImplicitUsings>enable</ImplicitUsings>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
			<PrivateAssets>all</PrivateAssets>
			<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
	</ItemGroup>
</Project>

And in your .csproj files, remove configurations present in the shared.props file. and add this line:

<Import Project="..\configs\shared.props" />

Now, you project has all of its own configurations, and all of the shared configurations. Including nuget packages:

<Project Sdk="Microsoft.NET.Sdk.Web">

	<Import Project="..\configs\shared.props" />

	<PropertyGroup>
		<UserSecretsId>[secret]</UserSecretsId>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Azure.Identity" Version="1.9.0" />
		<PackageReference Include="EfCore.SchemaCompare" Version="7.0.0" />
		<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.9" />
		<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.9">
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		  <PrivateAssets>all</PrivateAssets>
		</PackageReference>
		<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
		<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9">
		  <PrivateAssets>all</PrivateAssets>
		  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
		</PackageReference>
		<PackageReference Include="Microsoft.Extensions.Azure" Version="1.6.3" />
	</ItemGroup>
</Project>

Why this works? The <import> documentation states the following:

Declares that the contents of another project file should be inserted at this location

It simply just takes the content of the .props file, and inserts it into the .csproj file.

That’s it, really. A small life-improving, “hack” for dotnet projects.

Create magic with APE

I would like to present to you, “APE”. APE stands for “Awesome Property Extractor” and is simply a framework for extracting properties and their values from arbitrary data and turn them into strongly typed C# properties and values.

To begin with, we have created a package for Umbraco called APE.Umbraco.

What is APE.Umbraco

APE.Umbraco is a nuget package for extracting Umbraco properties and dictionary items and transform them into strongly typed C# properties that can be accessed using “dot-notation”.

APE accesses the database and retrieves all dictionary items, doctypes and their properties and data types, and builds a bunch of strongly typed classes.

These classes give you access to all the values without ever opening the Umbraco back office, just to look what a given alias or dictionary key was called. All you need to know is what doctype the property is on, and you can access all the properties, said doctype has, even those that has been inherited. You can even “dot” your way to all the doctypes, using a common static class called DocTypes, we even have a class called Dictionary!

We have created two releases of this package:

  • APE.Umbraco v1.0, this release is built for Umbraco 6 and does not support Umbraco 7, for that we have,
  • APE.Umbraco v2.0, this release is built for Umbraco 7, and does not work with Umbraco 6!

1.0 is released to support “old” sites and will not be updated unless a bug is reported. 2.0+ is the one we will build upon and add features to.

What’s in the package

The package contains the following:

APE.Umbraco.dll, this is the “magic box”. This assembly is merely an implementation of the method described in my earlier post.

This folder tree:

  • Classes
    • APE
    • Cms

The APE folder contains a FileManager.ttinclude file. This file allows us to split the generated classes into multiple files, improving readability. It will be released with each APE.* release.

The Cms folder contains two T4 templates: Umbraco.Dictionary.tt. This template, extracts all dictionary items from the database using the umbracoDbDSN connection string and generates a class containing all dictionary keys and a class for getting dictionary values using said keys. The other file is Umbraco.DocTypes.tt. It uses the same connection string to extract all document types and their properties, maps the Umbraco data into C# classes and properties into a class pattern like the one I described in my previous post, “Umbraco and .NET magic”.

The future

In the future we will have an APE.UCommerce package and many other APE packages. You can help us making APE the best alternative to Code First on platforms not build to support Code first, like Umbraco and uCommerce. A long with the release of APE.Umbraco, we are making the whole deal open source on GitHub:https://github.com/LAITDK/AwesomePropertyExtractor. This is also the place to find documentation and code examples, post bug reports and make pull requests.

It’s awesome! Where can I get it?!

As this package is intended for developers it is only released through NuGet. There will not be an Umbraco Package. It just makes no sense to create an Umbraco package for something that needs Visual Studio to work, (I don’t know if it works in WebMatrix).

To get the package, look no further. https://www.nuget.org/packages/APE.Umbraco/

To see examples on what APE can do, take a look at the wiki: https://github.com/LAITDK/AwesomePropertyExtractor/wiki

Hope you like the package and will help making this the goto package for any Umbraco project.

Umbraco and .NET magic

Wouldn’t it be fun, if you could simply call one method on an IPublishedContent, and get any property as the right type, always, without parsing or converting the data, passing any type-parameter or calling different methods? Wouldn’t it be cool if said method knew that a rich text editor property is always an IHtmlString and a content picker is an IPublishedContent rather than a string or an integer? Wouldn’t it be awesome if the same method returning an IPublishedContent could return an integer or a string with no additional code other than a type parameter? And how about this method is seamlessly put on top of Umbraco without interfering with existing code, but still used in the same manner?

Would your mind be blown if I told you it is possible and how to accomplish exactly that?

Basic theory

The idea behind this “method” is basically this:

“Everytime I need a property I have to call GetPropertyValue(), add a type parameter, and a magic string with the alias of the property from which I want the value.

Nine out of ten times, that value is either a string or an int that I have to parse or convert to a different type. Why do I have to pass two values each time? And what if said property is a content picker? I can’t pass IPublishedContent as a type parameter. How should umbraco know if I want a media item or a content item?

If we could “learn” umbraco to automatically give me the right value each time, so I don’t need to know if this property is a string, a media item or a DateTime. It would make our lives a bit easier.

Let’s take a look at some code

First off, we need to know what type, each of our properties have. So we look into our doctypes and each of their properties. We need two informations: Alias and Data Type.

Then we make a small class. Nothing big, just a small one:

public abstract class DocTypeProperty
{
     protected DocTypeProperty(){}

    public string Alias {get;set;}
}

This class contains half of what we need, but it’s okay.

Please note, it’s marked as abstract, and it constructor is protected. This is because, it’s purpose is not to be instantiated directly, but to serve as a base / master class, that has to be inherited.

Then we need to store our data type. This is where magic begins:

public class DocTypeProperty<TValue>
{
    public DocTypeProperty() : base(){}
}

Is that it?! No!! As I wrote: “The magic begins”.

We have now both information needed to perform the real magic, we know the alias of the property and the type we want the data as. As the smart developer you propably are, you have noticed that I have no parameters in my constructors. That is correct. More on that later.

To instantiate our “DocTypeProperty’s” we do this:

var myProp = new DocTypeProperty<string>(){ Alias = “someAlias” }

Please take note of the variable name: “myProp”. We are going to need that later.

How and when you do this I will not tell in this post, as it’s a complete post on its own. All I can tell is T4 templating IS an option!

So now we have our alias, and the type we want to convert the value into. But we are not done yet.

As I wrote ealier, I would like this to be seamlessly integrated on top of umbraco, but I cannot just pass my DocTypeProperty into the GetPropertyValue-method as is. To do this, we need to add an implicit operator, that can “transform” our neet DocTypeProperty to an old school string containing our alias:

public static implicit operator string(DocTypeProperty prop)
{
    return prop.Alias;
}

Now we can do this:

SomeContent.GetPropertyValue<string>(myProp);

This works exactly like oldschool umbraco. No magic yet, other than the implicit operator.

Now we need to get rid of the type parameter. To do this, we need to add an extension method to IPublishedContent:

public static class PublishedContentExtensions
{
    public static TValue GetPropertyValue<TValue>(this IPublishedContent content , DocTypeProperty<TValue> property)
    {
        return content.GetPropertyValue<TValue>(property);
    }
}

This will result in an overload to the standard umbraco GetPropertyValue-method.
Now, we can do this:

SomeContent.GetPropertyValue(myProp);

See, simple. This will return the value of the property as a string. But this is great for standard data types, but what about other types like media pickers, content pickers, multiitem whatever picker? We still need to learn our DocTypeProperty-class, how to handle those types.

So we add this method to the generic DocTypeProperty:

public virtual TValue Map(IPublishedContent content)
{
    return content.GetPropertyValue<TValue>(this);
}

This is the generic / default mapper. It handles all default umbraco data types, like string, DateTime, int and so on.

Furthermore, we need to update our own GetPropertyValue()-method:

return property.Map(content);

So this is all jolly, but we still need to be able to get an IPublishedContent instead of just an integer with a node/media id. To do this, we need to override the mapper:

public class MediaPickerProperty : DocTypeProperty<IPublishedContent>
{
    public override IPublishedContent Map(IPublishedContent content)
    {
        var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); // This should not be done each time we call this method. Put it outside in a cached field!
        return umbracoHelper.TypedMedia(content.GetPropertyValue<int>(this));
    }
}

Now we can make an instance of the MediaPickerProperty:

var myMediaProp = new MediaPickerProperty(){ Alias = “someMediaPicker”};

Again, note the property name. Now I have a few options:

The old fashioned:

someContent.GetPropertyValue<int>(“alias”) // returns an int.
someContent.GetPropertyValue<int>(myMediaProp) // Also returns an int.

and the new:

someContent.GetPropertyValue(myMediaProp) // Returns IPublishedContent.

And if I did this:

someContent.GetPropertyValue(myProp)

It would return string.

But let’s say we have myProp registered as DocType<string>, but I would like it as an IHtmlString more than just once, but not as often as I want it as string. I don’t want to have the same property registered twice. And what if I have more properties that needs to be IHtmlString once or twice?

Well, first we need to define an IHtmlStringProperty-class, and create a mapper. Like we did with the MediaPickerProperty. The mapper however, is a bit simpler, just:

return new HtmlString(content.GetPropertyValue<string>(this));

Now I need to be able to transform my DocTypeProperty<string> into my IHtmlStringProperty.

To do this, as simple as possible, we need to add a method to the base DocTypeProperty:

public TProp As<TProp>()
    where TProp : DocTypeProperty, new()
{
    return new TProp() { Alias = this.Alias };
}

It doesn’t get any simpler than that. And now you can see why I needed to have a parameterless constructor. Otherwise, this would have been a pain in the arse.

So now I can do this:

someContent.GetPropertyValue(myProp) // string

someContent.GetPropertyValue(myProp.As<IHtmlStringProperty>()); // IHtmlString

As you can see, this is completely transparent. You can write less code. , and you don’t have to remember hundreds of aliases, and update each and everyone of them when they change. You have the alias registered once. Especially if you autogenerates the properties using T4. You can always go back to umbraco’s own GetPropertyValue. Just add a type parameter. You have complete control over what types your property values are fetched as and the code is 100% reusable!

We use this approach, including the T4 templating, in all our new projects, and we are able to put upon existing applications, without interfering with code already written.

IEnumerable, why use it?

Okay, first off: I really love the IEnumerable<T> interface, and I use it excessively throughout my applications.

Almost any list or collection throughout the .NET framework implements this interface. It exposes only one method (GetEnumerator), but this method is your friend in almost any circumstance.

This method returns an Enumerator, which again exposes methods used to traverse (or loop through) the collection. This means you can make a method definition like this:

public void MyMethod(IEnumerable<string> someEnumerable)
{

}

I could call this method with any sort of collection, it could be string[], List<string>, IList<string>, IQueryable<string> or even a basic IEnumerable<string>. I don’t give a damn. I don’t care what the implementation is, I just want a collection to work with, nothin’ more.

This means I don’t need to force other developers who use my code, to pass an array or a List or some custom collection, they just pass whatever collection they have, and I will be able to work with it. That’s awesome!

But wait! How do you get an IEnumerable<T> in the first place? How can we make our own?

Well, in “ye olde times” one would make an abomination like this:

public List<string> MyMethod()
{
List<string> myResult = newList<string>();
for (int i = 0; i < dataSource.Length; i++)
{
// Do some work.
myResult.Add(dataSource[i].SomeProperty);
}
return myResult;
}

Can you see what’s wrong here?

There’s actually more than one thing that’s wrong with this snippet.

First off: When we return a specific implementation, in this case List<T>, we force our code to use this implementation. We kinda dictates the use of our result which is a major drawback on agile Development. What if the consumer don’t want or need a list?

Second: In line 3, we create an instance of an object we actually don’t need, so that we can return a list.

Third: We loop through a collection of an unknown size. This Collection could potentially contain a billion items.

Fourth: We have to wait until the loop has ended. Which could take forever. Think about what would happen to your app, when you loop though a billion items and only need the first five.

This is where .NET provides a concept of Enumerators. These are methods that only returns data when said data is ready to be returned on not when all data is ready to be returned. And it is actually the same method called from the GetEnumerator()-method.

To convert our method from above into an Enumerator, we must make some changes. First we need to return an IEnumerable<string> instead of a List. Second we need to remove the list-object. And finally rewrite the loop to use the keyword “yield”.

This is how our method would then look like:

public IEnumerable<string> MyMethod()
{
for (int i = 0; i < dataSource.Length; i++)
{
// Do some work.
yield return dataSource[i].SomeProperty;
}
yield break;
}

As you can see, the code looks way simpler. We don’t force our consumer to take a list. And the yield-keyword, changes the method into an Enumerator, which gives us a LOT of benefits:

The yield return, returns our data when the property Current is called on the Enumerator-instance. This property is usually called each time the MoveNext()-method is called. This Means you do not have to wait until all items are looped. You can stop at any moment. the yield-return is only called when you request the next item.

So if your consumer needs to loop through each of your items, you will only have to loop once, whereas the List-thingy above, would loop through the same items twice. That’s two billion items! Secondly the first example would always loop though each item, but the second one only loops through as many items as you want it to. Nothin more nothin less.

 

The essence of it all: Always use IEnumerable, only use lists where it cannot be done without it.

For performance reasons, always use Enumerators. They will save the day.

 

This is my first attempt on a “professional”-programming guide. Please provide feedback!

DBTesting

Efter lang tids stilhed kommer her en lille opdatering på hvad jeg har gang i for tiden.

Jeg arbejder på et database-testnings system, DBTesting, til at oprette og køre automatiserede tests af en database under udvikling, så man sikre sig, at de data man får ud, er præcis som de skal være og ikke bliver ændret på nogen måder.

Jeg vil ikke gå så meget i dybden her, men du kan læse mere på hjemmesiden:

http://dbtesting.ndesoft.dk

NMapper v2.0

NMapper Logo

NMapper Logo

Så er jeg i gang med version 2 af min NMapper.

NMapper v2.0 kommer til at have følgende features:

  • Alt fra de foregående versioner.
  • Forbedret cache system.
  • Nyt DB Manager system.
    • Ansvaret flyttes fra managerne til NMapperen.
  • Mulighed for at lave instanser af datamapperen så man kan bruge flere DB managere.
  • Helt ny indre struktur.
  • Forbedret ydelse.
  • Flere generelle og automatiserede metoder.
  • Mulighed for at få taget tid på SQL kaldene.
  • Og meget mere.

Håber at blive færdig om 2-3 måneder. Hvis du har idéer eller ønsker til yderligere funktionalitet så må du gerne smide en kommentar herunder.

XML DBManager

commentxml1[1]Så er min XML Manager til NMapperen, ved at være godt moden. Jeg bruger den på mit arbejde, sammen med NMapperen, selvfølgelig, og har i den forbindelse fundet en hel del svagheder i den. I den seneste version er de fleste af de svagheder blevet helbredt.

Et af mine problemer opstod da jeg ville indsætte 9445 rækker i en tabel (xml fil) navigate to these guys. Den insisterede på at gemme filen for hver indsættelse. Det tog evigheder, sad i en halv time og ventede.

Så slog det mig. Indsæt en buffer, der indeholder alle XML-filer fra de oprettes eller indlæses, og så lade en tråd gemme de filer en gang i mellem.

På den måde skal der faktisk kun læses een gang fra harddisken og kun skrives til disken samtidig med at cachen renser sig selv.

NModules

Til den seneste version af NMapper’en har jeg haft brug for en modul styrings komponent. Det er der kommet NModules ud af.

I NModules kan du registrere flere interfaces. NModules gennemsøger så en mappe for dll’er og finder alle de klasser der implementere et registreret interface. Ud af de klasser kan NModules så lave instanser som kan bruges som var de hårdkodet i systemet.

NCache

Til den seneste version af NMapper’en, har jeg lavet et par nye komponenter. Det ene er NCache.

NCache bruges til cache håndtering. Man kan smide hvad som helst i den og få det frem igen. Som standard gemmer den de cachede objekter i 10 minutter, hvorefter den fjerner dem. Men den fjerner kun de objekter der er ældre end 10 min. så alle objekter er cachede lige lang tid.

Man kan selvfølgelig selv ændre hvor lang tid cachen skal gemmes.

Du kan læse mere om NCache her.

NMapper 1.0

Endelig! Efter utrolig lang tid, er det endelig lykkedes mig at få tid til at færdiggøre NMapper 1.0.

Der er kommet en masse nye features.

NMapper skal bl.a. ikke længere kende alle mulige og umulige versioner og udgaver af database systemer. NMapper er nemlig blevet modul baseret, så man selv kan lave en database manager der passer til den database man bruger. NMapperen skal nok selv finde og registrere manageren.

Ud over at have lavet database managere, har jeg også lavet cache styrring, så der skæres ned på trafikken til og fra databasen, samtidig med at svar tiden bliver hurtigere.

Det eneste jeg mangler nu er bare at skrive en masse dokumentation, men indtil den er færdig, kan du jo skrive herunder hvis der skulle blive problemer.

Læs mere her.