Water damage is costly and can be harmful.
Recognizing visual signs is important.
Let’s explore common signs and how to protect your property.
Cracking the Code: Recognizing Visual Cues for Water Damage - Protech Restoration | Water Damage Restoration In Chicago
Effective Roof Cleaning Techniques for a Spotless Home
Maintaining a clean roof is crucial for the appearance and structure of your home.
Roofs can gather dirt, debris, algae, and moss, which not only affect the visual appeal but can also cause damage.
This blog post will discuss effective roof cleaning techniques for keeping your roof in excellent condition.
15 Effective Roof Cleaning Techniques| Crestview
Stay updated on weather alerts. Prepare an emergency kit.
Share an evacuation plan with family. Move valuables to higher ground.
Avoid floodwaters for safety. Follow evacuation orders. Turn off utilities before leaving.
Find higher ground during a flood. Stay connected with a radio or charged phone. Assist others and report emergencies.
Be aware: I’m not a full time administrator and this post might sound stupid to you.
The Problem
We access certain Active Directory properties with our application and on one customer domain we couldn’t get any data out via our Active Directory component.
Solution
After some debugging and doubts about our functionality we (the admin of the customer and me) found the reason:
Our code was running under a Windows Account that was very limted and couldn’t read those properties.
If you have similar problems you might want to take a look in the AD User & Group management.
- You need to active the advanced features:

- Now check the security tab, go to advanced view and add a new permission or change a existing one:

- Here you should be able to see a huge dialog with all available properties. Check if your user is able to read your target property

Hope this helps!
The Problem
We are using Azure App Service for our application (which runs great BTW) and deploy it automatically via ZipDeploy.
This basic setup was running smoth, but we noticed that at some point the deployment failed with these error messages:
2023-08-24T20:48:56.1057054Z Deployment endpoint responded with status code 202
2023-08-24T20:49:15.6984407Z Configuring default logging for the app, if not already enabled
2023-08-24T20:49:18.8106651Z Zip deployment failed. {'id': 'temp-b574d768', 'status': 3, 'status_text': '', 'author_email': 'N/A', 'author': 'N/A', 'deployer': 'ZipDeploy', 'message': 'Deploying from pushed zip file', 'progress': '', 'received_time': '2023-08-24T20:48:55.8916655Z', 'start_time': '2023-08-24T20:48:55.8916655Z', 'end_time': '2023-08-24T20:49:15.3291017Z', 'last_success_end_time': None, 'complete': True, 'active': False, 'is_temp': True, 'is_readonly': False, 'url': 'https://[...].scm.azurewebsites.net/api/deployments/latest', 'log_url': 'https://[...].scm.azurewebsites.net/api/deployments/latest/log', 'site_name': '[...]', 'provisioningState': 'Failed'}. Please run the command az webapp log deployment show
2023-08-24T20:49:18.8114319Z -n [...] -g production
or this one (depending on how we invoked the deployment script):
Getting scm site credentials for zip deployment
Starting zip deployment. This operation can take a while to complete ...
Deployment endpoint responded with status code 500
An error occured during deployment. Status Code: 500, Details: {"Message":"An error has occurred.","ExceptionMessage":"There is not enough space on the disk.\r\n","ExceptionType":"System.IO.IOException","StackTrace":"
“There is not enough space on the disk”?
The message There is not enough space on the disk
was a good hint, but according to the File system storage everything should be fine with only 8% used.
Be aware - this is important: We have multiple apps on the same App Service plan!

Kudu to the rescure
Next step was to check the behind the scene environment via the “Advanced Tools” Kudu and there it is:

There are two different storages attached to the app service:
c:\home
is the “File System Storage” that you can see in the Azure Portal and is quite large. App files are located here.
c:\local
is a much smaller storage with ~21GB and if the space is used, then ZipDeploy will fail.
Who is using this space?
c:\local
stores “mostly” temporarily items, e.g.:
Directory of C:\local
08/31/2023 06:40 AM <DIR> .
08/31/2023 06:40 AM <DIR> ..
07/13/2023 04:29 PM <DIR> AppData
07/13/2023 04:29 PM <DIR> ASP Compiled Templates
08/31/2023 06:40 AM <DIR> Config
07/13/2023 04:29 PM <DIR> DomainValidationTokens
07/13/2023 04:29 PM <DIR> DynamicCache
07/13/2023 04:29 PM <DIR> FrameworkJit
07/13/2023 04:29 PM <DIR> IIS Temporary Compressed Files
07/13/2023 04:29 PM <DIR> LocalAppData
07/13/2023 04:29 PM <DIR> ProgramData
09/05/2023 08:36 PM <DIR> Temp
08/31/2023 06:40 AM <DIR> Temporary ASP.NET Files
07/18/2023 04:06 AM <DIR> UserProfile
08/19/2023 06:34 AM <SYMLINKD> VirtualDirectory0 [\\...\]
0 File(s) 0 bytes
15 Dir(s) 13,334,384,640 bytes free
The “biggest” item here was in our case under c:\local\Temp\zipdeploy
:
Directory of C:\local\Temp\zipdeploy
08/29/2023 04:52 AM <DIR> .
08/29/2023 04:52 AM <DIR> ..
08/29/2023 04:52 AM <DIR> extracted
08/29/2023 04:52 AM 774,591,927 jiire5i5.zip
This folder stores our ZipDeploy
package, which is quite large with ~800MB. The folder also contains the extracted files - remember: We only have 21GB on this storage, but even if this zip file and the extracted files are ~3GB, there is still plenty of room, right?
Shared resources
Well… it turns out, that each App Service on a App Service plan is using this storage and if you have multiple App Services on the same plan, than those 21GB might melt away.
The “bad” part is, that the space is shared, but each App Services has it’s own c:\local
folder (which makes sense). To free up memory we had to clean up this folder on each App Service like that:
rmdir c:\local\Temp\zipdeploy /s /q
TL;DR
If you have problems with ZipDeploy and the error message tells you, that there is not enough space, check out the c:\local
space (and of course c:\home
as well) and delete unused files. Sometimes a reboot might help as well (to clean up temp-files), but AFAIK those ZipDeploy files will survive that.
Microsoft erweitert die Klassen DateTime, TimeStamp, DateTimeOffset und TimeOnly in .NET 7.0 um die Eigenschaften Microseconds und Nanoseconds.
Neben dem Source-Generator für reguläre Ausdrücke führt .NET 7.0 auch einen für den Zugriff auf native Betriebssystemfunktionen ein.
Ein Vergleich mit anderen Verfahren zeigt, wann es sich lohnt, den neuen Source-Generator für reguläre Ausdrücke zu verwenden.
Ein neuer Generator beschleunigt in .NET 7.0 die Verarbeitung Regulärer Ausdrücke und ist kompatibel zu dem Ahead-of-Time-Kompilierung.
Der JSON-Deserializer kann im jüngsten .NET-Framework ebenso polymorph arbeiten wie der JSON-Serializer.
Die Annotation [JsonDerivedType] in der JSON-Bibliothek System.Text.Json ermöglicht polymorphes Programmieren.
.NET quittiert mit der aktuellen Version das Auslassen von Pflichtattributen bei der JSON-Deserialisierung mit einer Fehlermeldung.
Das Anpassen der Serialisierung und Deserialisierung von JSON-Inhalten ist nützlich, wenn die zu serialisierende Klasse nicht im Sourcecode verfügbar ist.
Das Verarbeiten von Tar-Archiven ist eine Neuerung in .NET 7.0. Um sie zu komprimieren, sind separate Verfahren wie GZip erforderlich.
ALTWater damage can occur in homes and businesses due to various reasons such as floods, leaking pipes, or sewage backups. When faced with water damage, it is crucial to take immediate action to minimize the extent of the damage and prevent further problems. This is where water damage restoration services come into play. In this article, we will explore six benefits of water damage restoration services and why they are essential for restoring your property.
6 Benefits of Water Damage Restoration Service
Water damage restoration services are designed to mitigate the effects of water damage and restore your property to its pre-damaged condition. These services employ trained professionals equipped with specialized tools and techniques to address water-related issues efficiently. Let’s explore the benefits of hiring water damage restoration services in more detail.
Swift Water Extraction
One of the primary benefits of water damage restoration services is their ability to quickly extract water from your property. Time is of the essence when it comes to water damage, as prolonged exposure to water can lead to further damage and mold growth. Restoration professionals use powerful pumps and vacuums to remove standing water efficiently. This swift water extraction minimizes the risk of structural damage and reduces the chances of mold formation.
Professional Assessment and Restoration Plan
When you hire water damage restoration services, professionals conduct a thorough assessment of the affected area. They identify the extent of the damage and create a detailed restoration plan tailored to your specific needs. This assessment ensures that all areas, including hidden spaces and structural components, are accounted for during the restoration process. With a well-defined plan in place, restoration efforts are targeted and efficient.
Mold Prevention and Remediation
Water damage often creates an environment conducive to mold growth. Mold can pose serious health risks and cause further damage to your property if left untreated. Water damage restoration services include mold prevention and remediation as part of their comprehensive approach. Professionals use specialized equipment and techniques to detect and eliminate mold, ensuring a safe and healthy environment for occupants.
Drying and Dehumidification
After water extraction, thorough drying and dehumidification are essential to prevent secondary damage. Water damage restoration services employ industrial-grade drying equipment such as air movers and dehumidifiers to remove excess moisture from the air and surfaces. This process eliminates moisture that can seep into walls, furniture, and other materials, minimizing the risk of warping, swelling, or mold growth.
Structural Repair and Restoration
Water damage can weaken the structural integrity of your property, compromising its safety and stability. Water damage restoration services encompass structural repair and restoration to bring your property back to its pre-damaged condition. Whether it’s repairing damaged walls, floors, or ceilings, these professionals have the expertise to restore the structural elements of your property effectively.
Safety and Health Measures
Water damage restoration services prioritize safety and health measures throughout the restoration process. Professionals are equipped with personal protective equipment (PPE) to ensure their safety while working in hazardous conditions. They also take precautions to prevent cross-contamination and ensure the overall safety of occupants during the restoration process. By adhering to strict safety guidelines, water damage restoration services provide peace of mind to property owners.
Conclusion
Water damage restoration services offer a range of benefits when it comes to addressing water-related issues in your property. From swift water extraction and professional assessment to mold prevention and structural repair, these services play a vital role in minimizing damage and restoring your property to its original condition. By hiring water damage restoration services, you can save time, mitigate risks, and ensure the safety and well-being of your property and its occupants.
Dass .NET 7.0 auch Tar-Archive verarbeiten kann, ist der Docker-Anbindung an die .NET CLI zu verdanken.
ALTWater damage can be a distressing and overwhelming experience for homeowners. Whether it’s caused by a burst pipe, heavy rain, or a plumbing mishap, water damage can lead to structural issues, mold growth, and the loss of personal belongings. However, with prompt action and the right knowledge, it is possible to mitigate the damage and restore your property to its pre-loss condition. In this article, we will provide you with a step-by-step guide to water damage restoration, helping you navigate through the process effectively.
A Step-By-Step Guide to Water Damage Restoration
Water damage can be a nightmare for any homeowner. Whether it’s caused by a burst pipe, heavy rainfall, or a leaky roof, the consequences can be devastating. But fear not! With a step-by-step guide to water damage restoration, you can navigate through this challenging process and restore your home to its former glory.
Step 1: Assess the Damage
The first step in water damage restoration is to assess the extent of the damage. Check for structural damage, mold growth, and any items that may need immediate attention. Take photos or videos as evidence for insurance claims.
Step 2: Remove Standing Water
Once you’ve assessed the damage, the next step is to remove any standing water. Use buckets, mops, or wet vacuums to extract the water from your home. Be cautious of potential electrical hazards and wear protective gear if necessary.
Step 3: Dry Out the Area
After removing standing water, it’s crucial to dry out the affected area properly. Open windows, use fans, or employ dehumidifiers to increase air circulation and speed up the drying process. This will help prevent mold growth and further damage.
Step 4: Salvage or Discard
Next, evaluate the items affected by water damage. Determine what can be salvaged through cleaning and drying, and what needs to be discarded. Be thorough in your assessment to avoid potential health hazards or future problems.
Step 5: Clean and Disinfect
To prevent mold growth and ensure a safe living environment, clean and disinfect all surfaces that came into contact with water. Use appropriate cleaning agents and follow recommended guidelines for effective sanitation.
Step 6: Repair and Restore
Once the area is dry and cleaned, it’s time to repair and restore any damaged structures or materials. This may involve fixing structural issues, replacing damaged flooring or walls, and repainting or refinishing surfaces. Hire professionals if needed to ensure proper repairs are made.
Step 7: Monitor for Mold
Even after completing the restoration process, it’s important to monitor for any signs of mold growth. Keep an eye out for musty odors, discolored patches on walls or ceilings, or increased allergy symptoms. If mold is detected, take immediate action to prevent further damage and health risks.
Step 8: Evaluate Prevention Measures
Finally, evaluate Finally, evaluate the prevention measures you can take to avoid future water damage. This may include installing a sump pump, waterproofing your basement, sealing gaps and cracks, or regularly inspecting and maintaining your plumbing system. Implement these measures to minimize the risk of future water damage.

Important things need to grow to last. The German says “gut ding will weile haben” – good things take time.
The Roman Theatre of Verulamium (St.Albans) built in about 140AD
I did it: almost exactly four years after Nick Tune suggested me a conference in London as the best place to start talking at conferences, I had my first in-person talk at the DDD Meetup in London. Given the timespan, you could say, “What is so special in this? Why should I continue reading?”. Well, you don’t need to
. For me, it feels like quite a wonder that by looking at where I was four years ago (I was completely unknown in the international community) and what happened in the meantime, I can put a
behind this bucket list item.
Writing about the last four years would transform this post into a small book – I won’t do that now. Instead, I will tell you a bit about the talk and how I applied the same ideas to prepare for it. The talk is about Feedback-driven Product Development. This post is about Feedback-driven Self-Development
.
slides on miro
With this talk, I wanted to show how broken our product development processes, long and short-term outcomes, hell, the whole industry is, and they don’t need to be! We have everything we need to improve our life as product developers to enjoy this most creative job, but instead, we feel frustrated, overwhelmed and not fulfilled. In the presentation, I talked about the three things organisations (or single persons) can use to change this.
Disclaimer: I don’t mean software developers here. I mean all the different roles and skills needed to create something good.
Now back to the story I want to tell you: how did I use these three ideas to develop the talk and myself at the same time?
Optimise For the Time to Learn
As I said, I have never written a talk with slides and all. I have even switched from Windows to Linux to never land in a situation where I needed to write a PowerPoint presentation. But the talk itself is only the output, it is not my goal. My goal is to learn if I can do this, if I am good at it. If I enjoy it? I started to speak in international circles on Virtual DDD, and the pandemic made it very easy to meet great people. After three years of interactions, it felt natural to me. We became online friends. Last year I proposed my first workshop at the biggest DDD conference in Europe because I wanted to meet them and knew that I wouldn’t be able to support the trip financially otherwise. It worked, and now I am part of this very special community. It stopped becoming a challenge; I needed something new: a talk presented on a podium, completely out of my comfort zone.
You need to know that I am not a consultant (and I still don’t want to become one), so there are not too many reasons for all the effort needed to travel abroad to conferences and give a high-quality workshop or talk. One (and still the main reason) is to meet and exchange with other nerds. The other is my personal development: can I do this? Where are my boundaries?
So that in January, Nick convinced me to dare the next step. We agreed on a date and on a (vague) topic and set the first milestone with this: learn. Learn about the fun, the (lack) of skills in presenting my thoughts, and the reaction of others. If they are interested at all in what I want to say. My slides (the output) needed to be good enough (on my own quality scale) so that I could achieve these goals.
Measure the Outcome
The outcome was great! Even if it was a complicated plan, everything worked out, and I was ready to go. This meetup, in a city where almost no one knew me (so that they were unbiased), with a topic which could have been everything or nothing, the splendid location at Mastercard providing the conference feeling – all this was on purpose to get the most honest feedback. After seeing several people nicking during the talk, making multiple connections on LinkedIn and mastodon directly after the session, and getting several “Thank you for addressing this” comments, I could tick one important question: people want to know what I am speaking about 
The question about joy, about having fun, was answered too: yes, I had fun. I had great conversations with myself while I was writing the talk, and I had better conversations with the people in the room
.
Embrace the Change
The best outcome I learned by giving this talk was that it is not good enough yet. Imagine the other situation: having it perfect for the first time. Where am I going from here? What a boring (hence scary) thought! After this first practice, I now know (and not guess) that I will need to rework everything, but my first “walking skeleton” is the right one; the first step was exactly in the right direction. The next iterations can come.
The next milestone is at the beginning of October at KanDDDinsky. The steps towards it will be small and several based on feedback from Nick, Andrea, or anyone up to it. My journey to find my boundaries can continue.
Whether it’s a burst pipe, a flooded basement, or a leaky roof, water damage in your home can quickly become a nightmare. Not only can it lead to costly repairs, but it can also pose health risks if left untreated. That’s why it’s essential to address the issue as soon as possible with residential water damage restoration services.
With so many restoration companies out there, it can be overwhelming to know where to start. Today we’ll discuss the necessary steps you should take as soon as possible after discovering water damage in your home. So keep reading for more information. When you need an useful content about water damage restoration, click over here.
Step-by-Step Guide to Quickly Restore Residential Water Damage
Here are some essential steps you need to follow when restoring residential water damage:
Step 1: Assess the Damage
The first step in any water damage restoration process is to assess the damage. This means identifying the source of the water and determining the extent of the damage. It’s important to assess any potential hazards to ensure everyone’s safety. Once you’ve identified the source, shut off the water or electricity if necessary. Remember, if the water damage is severe or contaminated, it’s best to contact a professional rather than attempting to fix it yourself.
Step 2: Remove Any Standing Water
The next step is to remove any standing water from your home. A professional restoration company will use industrial-grade equipment such as pumps, vacuums, and dehumidifiers to speed up the process. Standing water can quickly cause mold and mildew to form, which can be hazardous to your health if not treated quickly.
Step 3: Dry and Dehumidify
After all standing water has been removed, dry and dehumidify the affected areas to prevent further damage. A professional restoration service will use specialized equipment such as air movers and dehumidifiers to ensure that the area is completely dry. They’ll also inspect and replace any damaged insulation or drywall if necessary.
Step 4: Cleaning and Sanitizing
Once the area is completely dry, it’s time to clean and sanitize the affected area. This will ensure that any potential contaminants such as mold and mildew are eliminated. A professional cleaning and sanitizing company will use specialized chemicals and equipment to clean and sanitize your home.
Step 5: Restoration
The final step in the water disturbance restoration process is restoration. This means repairing any damage that has occurred to the property such as replacing drywall, flooring, or insulation. A professional restoration company will ensure that the restoration process is done correctly, preventing any further damage or future issues.
Take Quick Action to Overcome Water Damage
Water damage can be devastating to homeowners, but with the right resources and guidance, it can be overcome. Remember to always address the issue immediately and avoid attempting to solve it yourself if the damage is severe. It’s important to find a reputable restoration company that you can trust to get your home back to its former state. By following the steps mentioned above, you’ll be well on your way to restoring your home and peace of mind.
Die Klasse zum Repräsentieren des Cache im Arbeitsspeicher bietet in .NET 7.0 einige Funktionen für Statistiken der Nutzung des Zwischenspeichers.
Der AOT-Compiler erzeugt ressourcensparende Binaries, aber seine volle Stärke spielt er erst mit den richtigen Einstellungen in der Konfiguration aus.
Der von Microsoft lange Zeit angekündigte Ahead-of-Time-Compiler "Native-AOT" lässt sich erstmalig in .NET 7 nutzen – zumindest in Konsolenprojekten.
Verschiedene Optionen für dotnet publish erlauben individuelle Anpassungen der ohne Dockerfile mit .NET CLI angelegten Docker-Deployments.
Ein neues NuGet-Paket kümmert sich um die passende Docker-Konfiguration für containerisierte .NET-Anwendungen.
Auf Grund der regelmäßigen Artikelaufrufe über die SOLID-Prinzipien hatte ich mich vor einiger Zeit dazu entschlossen, diese Serie auch in Form eines kleinen Buches anzubieten. Ab sofort kann dieses Buch (gedruckt, als auch als E-Book) in allen bekannten Buchhandlungen und digitalen Plattformen bestellt werden. Neben den SOLID-Prinzipien werden in dem Buch auch die Prinzipien KISS, DRY, LoD und YAGNI angesprochen.
Natürlich stehen die bisherigen Artikel auch weiterhin hier auf dem Blog kostenlos zur Verfügung. Somit kann jeder frei entscheiden, ob man die Artikel lieber online, auf dem E-Book Reader oder als gedrucktes Buch lesen möchte. Aktuell liegt das Buch nur in Deutsch vor. Ich gehe aber davon aus, dass ich in einiger Zeit auch die englische Variante anbieten kann.
Der Preis für das knapp 100 Seiten umfassende Buch liegt für die gedruckte Variante (ISBN-13: 9783734746857) bei 6,99 € und für das E-Book (ISBN-13: 9783757870706) bei 4,99 €.
Der Online-Infotag widmet sich diversen Themen rund um C#, .NET und KI-unterstützte Softwareentwicklung.
Wer WebAPI-Projekte mit ASP.NET Core erstellt, hat die Wahl zwischen dem in .NET 6.0 eingeführten Minimal-API-Stil und dem ursprünglichen API-Stil.
Microsoft hat in .NET 7.0 den Umgang mit Befehlsnamen und deren Parametern auf der Kommandozeilen vereinheitlicht.
Kürzlich erhielt ich eine Frage von einem Leser meines Buches Cross-Plattform-Apps entwickeln mit Xamarin.Forms, der auf Seite 336 des Buches auf meine Behandlung von SQLite-Datenbankverbindungen in Xamarin.Forms aufmerksam wurde. Der Leser bemerkte, dass ich im Beispielcode eine Verbindung im Konstruktor öffnete und diese dauerhaft geöffnet hielt, ohne Close oder Dispose aufzurufen. Der Leser befürchtete, dass dies zu Speicherlecks oder sogar Datenverlust führen könnte und wies darauf hin, dass in Foren oft empfohlen wird, Verbindungen nur kurz zu öffnen und sofort wieder zu schließen.
Der Livestream bringt am Mittwoch um 18 Uhr spannende Einblicke und Hintergründe zur enterJS, interessante Gespräche mit Gästen und ein Gewinnspiel.
.NET 7.0 bietet für Konsolen- und Webprojekte wieder den klassischen Startcode mit einer Programklasse inklusive Main()-Methode in den Projektvorlagen.
Is it possible to get YouTube Premium much cheaper with a VPN? Yes, as it turns out, YouTube Premium is offered at a very cheap price in Argentina. We’ll show you a simple trick that will let you enjoy YouTube Premium (without ads) at a much cheaper price. With a reliable VPN service, like the one from CyberGhost* or the slightly more expensive from ExpressVPN*, you can watch YouTube Premium for much cheaper. Just read on to find out how you can use the VPN service and why it’s the easiest (and safest) way to get YouTube Premium at a reasonable price.
YouTube Premium is very cheap in Argentina
YouTube advertising is really annoying. If you want to enjoy the video platform ad-free, you have to pay for YouTube Premium. However, prices vary depending on the country. In Germany, for example, YouTube charges a very high price per month for the Premium variant.
Let’s now take a look at the prices for YouTube Premium in Argentina:
Single tariff: 389 ARS / 2,12 Euro per month
Family tariff: 699 ARS / 3,82 Euro per month
Those Argentinien prices are way lower in comparison with most of the other countries. In Germany, for example, you have to pay 11.99 euros per month for the single rate. The family plan costs 17.99 euros per month. This is a significant saving that justifies the use of a VPN in any case.
How to use a VPN for YouTube Premium – a quick guide
If you want to take advantage of the low YouTube Premium prices in Argentina, you first need to create an account abroad. And this is where a VPN service comes in handy. With a VPN, you can go to virtually any country in the world and easily bypass digital country blocks.
Time needed: 15 minutes.
Follow these simple steps and watch YouTube Premium at a much lower price via a VPN server in Argentina:
- Choose a VPN
We recommend two trusted providers with good reputation – CyberGhost*. CyberGhost offers 48 servers in Argentina. Again, the speed is perfect for the purpose.
- Install the VPN app/client
Once you have chosen a VPN service, you should install the software and launch the program.
- Choose a server in Argentina
Open the app/client. Then open the server or country selector and choose Argentina. Once you have connected to the Argentine VPN server, go to the YouTube Premium website.
Check if you are really connected to the Argentine YouTube site – the YouTube logo should have the abbreviation “AR” in the upper left corner. The prices should be displayed in Argentine pesos (ARS).
- Create a new Google Account
You need a Google Account to get YouTube Premium. Create a new Google Account. You can set it up later as a secondary YouTube account. Go to the YouTube Premium homepage. Click Sign in and follow the steps to set up the new Google Account.
- Go to YouTube Premium
If you now have a new Google account, go back to the YouTube Premium home page. Again, make sure the VPN connection to Argentina is up. When you’re logged in, you should see your shortcode in a colored circle at the top right of the site. Now:
- Fill out the form
On the home page, click the Free Trial button for the Standard Package. For the Family Package click on the Family Membership link. Then click on Try for free.
You will see a form to fill out. Enter your billing address and credit card information. Important: Visa credit cards are no longer accepted, but you can use other credit cards. Enter an Argentine address. Choose a random address through Google Maps and make sure you enter the correct zip code and province. Complete your subscription by clicking the Buy button.
- From now on, enjoy YouTube Premium at a great price!
From now on, you can sign in with your new Google Account and enjoy YouTube Premium at a very affordable price.
YouTube Premium – The advantages
YouTube Premium means videos without ads. That’s a big plus, because we all know how annoying those ads can be. They can really ruin your online experience.
With the Premium subscription, you can also download on the go. So you can download videos and music quickly and easily. You also get background playback – the feature plays videos in the background without interruption, you can open another app or even turn off the screen.
YouTube Music Premium, YouTube Kids, and YouTube Originals are part of the Premium plan. YouTube Originals includes original movies, series, and live events. While these events, movies, and series can occasionally be seen on the YouTube Originals channel, only Premium members can enjoy them without ads, watch all episodes, and get access to interesting bonus material. You also get the YouTube Gaming app.
FAQs – Frequently Asked Questions
Is it legal to buy YouTube Premium in Argentina with a VPN? Subscribing to YouTube Premium in Argentina is perfectly legal, but you are still in violation of the provider’s terms of service. YouTube is aware that there are geographical restrictions. The platform requires its users not to provide inaccurate or misleading information to falsely impersonate residents of a country where the paid services are accessible. Violations of YouTube’s terms of use may result in account suspension or deletion.
Can I use a free VPN service for YouTube Premium in Argentina? It would work if you could find a VPN provider with reliable free servers in Argentina. I have not been able to do that.
If you use streaming platforms frequently, it would generally be a good idea to use a reliable paid VPN service like ExpressVPN* and CyberGhost*.
Why is YouTube Premium no longer an option for India? India offers cheap YouTube Premium prices. But because of some new laws in India, most of the top VPN providers have pulled out of the country. The new law requires VPNs to log their users’ traffic. VPNs like CyberGhost continue to operate servers with India as their geolocation, but in some cases these servers cannot be reliably cloaked. As a result, YouTube either detects that you’re using a VPN or displays incorrect prices.
The post YouTube Premium much cheaper with VPN Argentina appeared first on xplatform.rocks.
Are you wondering how to stream Russian TV over the Internet? I have a great news for you. You can do that with CyberGhost*! It is one of the few VPN providers that still operates servers in Russia.
With CyberGhost you can stream Russian TV over the Internet.
Quick guide: Watch Russian TV online
In order to watch all Russian TV online, you need a VPN that provides servers in Russia. So you need a Russian IP address. There are not so many of them anymore, because the legal situation in Russia regarding VPNs is questionable.
Time needed: 10 minutes.
How to get a Russian IP address outside of Russia to stream Russian TV
- Take a VPN with servers in Russia
One of the few VPN providers that still runs well-functioning servers in Russia is CyberGhost*. The good thing about the service is that it offers a money-back guarantee on the long-term package and is also one of the cheapest providers on the market.
- Install VPN apps
Consider on which devices you want to watch Russian TV. As with many VPNs, you can install CyberGhost on multiple devices and that’s why you can run it on your smartphone, laptop and tablet simultaneously. Currently, CyberGhost allows 7 simultaneous connections and that’s a lot. The VPN supports the most popular operating systems and devices: Android, Windows, iOS, macOS and Linux as well.
- Connect to a Russian server
For Russian TV programs you need a Russian IP address. Therefore, you need to connect to a server in Russia. Then you can stream Russian TV online. That’s it. That’s all you really need to do. It’s actually as simple as it sounds.
Stream better protected
Internet censorship and surveillance is a problem in Russia and therefore you can protect yourself better with a VPN. Your Internet traffic is encrypted by the VPN and no one except the VPN provider can see what you are doing online. Of course, you have to have a certain trust in your VPN provider.
CyberGhost has a no-logs policy and does not collect any data. That’s why it can’t share anything with authorities and third parties, even if the service is asked for it.
Russian broadcasters use geo-blocking and then you need a Russian IP address to watch the corresponding show.
These Russian channels work with VPN
Of course, we tested whether you can safely stream Russian TV over the Internet with a VPN. We took a look at the most popular services and platforms.
Important note: Some of the listed services and streams might be illegal. We could not 100% understand what is legal and illegal because we don’t speak the language and don’t know exactly what the law is. So please inform yourself what is legal and illegal in your country so that you don’t get into trouble!
A popular Russian sports channel is Match TV. There you can find Bundesliga, Premier League, Formula 1 and how to stream Champions League for free. There are many sports events that you can watch there for free in Russian. Match TV belongs to the Gazprom Group.
Among the most important Russian TV channels is ntv.ru (state TV channel from Moscow).
1TV.ru is the first channel of the semi-public Russian broadcaster Perwy. I was also able to watch this portal with CyberGhost* without any problems.
Equally interesting is Planeta-Online.tv. The online TV portal provides access to eight Russian TV channels.
Other streams and channels you can watch with a VPN
Here are a few more portals that work with a VPN, but they can be shady. Extreme caution is advised here, so that you don’t get into conflict with the law.
- Fubo TV
- Rutube
- TVCenter
- Okko
- STS
- Videomore
- TVZavr
- Russian VoD
- Kinopoisk
- Ren TV
- Amediateka
- Zoomby
- KinoPoisk HDTV
- TV-Novosty
- TV-Mania
- TV-Pressa
- NTV Plus
- YuppTV
- TV 1000
- TVCatchup
FAQ – Streaming Russian TV
Here are some answers to questions that interest you.
Can I stream Russian TV with a free VPN? I doubt it, at least for the shows that are geoblocked. I don’t know of any free VPN that offers servers in Russia. Even if they do, reputable free VPNs have severe limitations, such as data volume. Then you can only stream for a short time and the pleasure quickly comes to an end. The problem at this point is that there are many dubious free VPN providers that are not trustworthy. They collect and sell your data to third parties, and in the worst case, they install malware and trackers on your device. Reputable providers like CyberGhost are cheap and have no restrictions. Moreover, they offer functioning servers in Russia.
Does Russian streaming offer other languages? As far as I saw during my tests, you can’t switch to any other language. I was able to use the translation feature of the browsers to help me navigate the site more easily. However, the streaming, i.e. the comments and the language itself always remained Russian.
Which is the best Russian channel for sports? If you are interested in sports events such as the Champions League, Formula 1 and so on, then Match TV is definitely your first port of call. The other channels are interesting for you if you are interested in news or other Russian programs. So it depends on your needs what is the best Russian channel.
The post Stream Russian TV over the Internet appeared first on xplatform.rocks.
Inheritance is a popular method for reusing existing function blocks. It enables new methods and properties to be added or existing methods overwritten without requiring access to the source code for the base function block. Designing software so that it can be extended without modifying the existing code is the key concept behind the Open/Closed Principle (OCP). But using inheritance also has disadvantages. These disadvantages can be minimised by employing interfaces – and this is not the only advantage of this method.
To put it another way, software behaviour should be open to extension without needing to modify the software. Based on our example from my previous posts, we’re going to develop a function block for managing lamp control sequences. We will then add additional functionality to extend this function block. We will use this example to illustrate the key concept underlying the Open/Closed Principle (OCP).
Starting situation
Our main starting point is the function block FB_SequenceManager. This provides access to the individual steps in a sequence via the aSequence property. The Sort() method provides a means to sort the list in accordance with various criteria.
The aSequence property is an array and contains elements of type ST_SequenceItem.
PROPERTY PUBLIC aSequence : ARRAY [1..5] OF ST_SequenceItem
To keep our example simple, we define our array as having fixed upper and lower bounds of 1 and 5. Array elements are of type ST_SequenceItem, which contains a unique ID (nId), the output value for the lamps (nValue) and the duration (nDuration) before switching to the next output value.
TYPE ST_SequenceItem :
STRUCT
nId : UINT;
nValue : USINT(0..100);
nDuration : UINT;
END_STRUCT
END_TYPE
In this example, we will not concern ourselves with methods for processing the sequence. Our example does, however, include a Sort() method for sorting the list by various criteria.
METHOD PUBLIC Sort
VAR_INPUT
eSortedOrder : E_SortedOrder;
END_VAR
The list can be sorted in ascending order only by nId or nValue.
TYPE E_SortedOrder :
(
Id,
Value
);
END_TYPE
In the Sort() method, the eSortedOrder input parameter determines whether the list is sorted by nId or nValue.
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId
// …
E_SortedOrder.Value:
// Sort the list by nValue
// …
END_CASE
Our example is a simple monolithic application which can be put together quickly to meet our requirements.
The UML diagram shows the monolithic structure of the application very clearly:
This does not, however, take account of the amount of work required to realise future extensions.
Sample 1 (TwinCAT 3.1.4024) on GitHub
Extension of the implementation
We are going to extend the application so that, in addition to nId and nValue, we can also sort the list by nDuration. Currently, the list is always sorted in ascending order. We would also like to be able to sort it in descending order.
How can we modify our example to meet these two client requirements?
Approach 1: Quick & dirty
One approach is to simply extend the existing Sort() method so it can also sort by nDuration. To do this, we add the field eDuration to E_SortedOrder.
TYPE E_SortedOrder :
(
Id,
Value,
Duration
);
END_TYPE
We also need a parameter to indicate whether we want to sort in ascending or descending order:
TYPE E_SortedDirection :
(
Ascending,
Descending
);
END_TYPE
So the Sort() method now takes two parameters:
METHOD PUBLIC Sort
VAR_INPUT
eSortedOrder : E_SortedOrder;
eSortedDirection : E_SortedDirection;
END_VAR
The Sort() method now contains two nested CASE statements. The outermost of these deals with the sort direction, the innermost with the parameter by which to sort the list.
CASE eSortedDirection OF
E_SortedDirection.Ascending:
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId in ascending order
// …
E_SortedOrder.Value:
// Sort the list by nValue in ascending order
// …
E_SortedOrder.Duration:
// Sort the list by nDuration in ascending order
// …
END_CASE
E_SortedDirection.Descending:
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId in descending order
// …
E_SortedOrder.Value:
// Sort the list by nValue in descending order
// …
E_SortedOrder.Duration:
// Sort the list by nDuration in descending order
// …
END_CASE
END_CASE
END_CASE
This approach is quick to implement. For a small application with a reasonably small amount of source code, this is absolutely a reasonable approach. But for this approach to be feasible, we have to have access to the source code. In addition, we need to ensure that FB_SequenceManager isn’t shared with other projects via, for example, a PLC library containing FB_SequenceManager. By adding a parameter to the Sort() method, we have also changed its signature. This means that program components that call this method with just a single parameter will no longer compile.
The UML diagram shows clearly that the structure is unchanged – it’s still a highly monolithic application:
Sample 2 (TwinCAT 3.1.4024) on GitHub
Approach 2: Inheritance
Another way to add features to the application is to use inheritance. This allows us to extend function blocks without having to modify the existing function block.
We start by creating a new function block which inherits from FB_SequenceManager:
FUNCTION_BLOCK PUBLIC FB_SequenceManagerEx EXTENDS FB_SequenceManager
The new function block contains a SortEx() method which takes two parameters specifying the required sort direction and order:
METHOD PUBLIC SortEx : BOOL
VAR_INPUT
eSortedOrder : E_SortedOrderEx;
eSortedDirection : E_SortedDirection;
END_VAR
Once again we add a data type E_SortedDirection which specifies whether the list should be sorted in ascending or descending order:
TYPE E_SortedDirection :
(
Ascending,
Descending
);
END_TYPE
Rather than extending E_SortedOrder, we create a new data type:
TYPE E_SortedOrderEx :
(
Id,
Value,
Duration
);
END_TYPE
We can now implement the required sort functions in the SortEx() method.
To sort in ascending order, we can use the Sort() method from the base function block (FB_SequenceManager). We don’t need to reimplement the existing sorting algorithm. All we need to do is add the additional sort type:
CASE eSortedOrder OF
E_SortedOrderEx.Id:
SUPER^.Sort(E_SortedOrder.Id);
E_SortedOrderEx.Value:
SUPER^.Sort(E_SortedOrder.Value);
E_SortedOrderEx.Duration:
// Sort the list by nDuration in ascending order
// …
END_CASE
Sorting in descending order needs to be programmed from scratch, however, as this cannot be achieved using existing methods.
If a new function block extends an existing function block, the new function block inherits the functionality of the base function block. The addition of further methods and properties enables it to be extended without needing to modify the base function block (open for extension). By using libraries, it’s also possible to protect the source code from modification (closed for modification).
Inheritance is therefore one way of implementing the Open/Closed Principle (OCP).
Sample 3 (TwinCAT 3.1.4024) on GitHub
This approach does, however, have two disadvantages.
Excessive use of inheritance can end up generating complex hierarchies. A child function block is absolutely dependent on its base function block. If new methods or properties are added to the base function block, every child function block will also inherit these new elements (if they are PUBLIC), even if the child function block has no intention of exposing these elements externally.
In some circumstances, extension by inheritance is only possible where the child function block has access to internal state information from the base function block. Access to these internal elements can be enabled by marking them as PROTECTED. This restricts access to child function blocks only.
In the example given above, the only reason we were able to add the sorting algorithms was because the setter for the aSequence property was declared as PROTECTED. If we did not have write access to the aSequence property, the child function block would not be able to modify the list, so would not be able to sort it.
This means, however, that the developer coding this function block always has to take into consideration two use cases. Firstly, a user making use of the function block’s public methods and properties. Secondly, users using the function block as a base function block and adding new functionality via PROTECTED elements. But which internal elements need to be marked as PROTECTED? And to enable their use, these elements also need to be documented.
Approach 3: Additional interface
Another approach is to use interfaces rather than inheritance. This, however, needs to be considered during the design phase.
If our aim is to design FB_SequenceManager so that users can add whatever sorting algorithms they want, then we should remove the code for sorting the list. The sorting algorithm should instead access the list via an interface.
In our example, we would add the interface I_SequenceSortable. This interface contains the SortList() method, which contains a reference to the list to be sorted.
METHOD SortList
VAR_INPUT
refSequence : REFERENCE TO ARRAY [1..5] OF ST_SequenceItem;
END_VAR
Next we create the function blocks containing the various sorting algorithms, each of which implements the I_SequenceSortable interface. As an example, we will take the function block for sorting by nId in ascending order.
FUNCTION_BLOCK PUBLIC FB_SequenceSortedByIdAscending IMPLEMENTS I_SequenceSortable
We can call the function block whatever we want; the crucial point is that it implements the I_SequenceSortable interface. This ensures that FB_SequenceSortedByIdAscending contains the SortList() method. The actual sorting algorithm is implemented in the SortList() method.
METHOD SortList
VAR_INPUT
refSequence : REFERENCE TO ARRAY [1..5] OF ST_SequenceItem;
END_VAR
// Sort the list by nId in ascending order
// …
The Sort() method of FB_SequenceManager takes a parameter of type I_SequenceSortable. When calling the Sort() method we pass to it a function block (e.g. FB_SequenceSortedByIdAscending) which implements the I_SequenceSortable interface and therefore also contains the SortList() method. FB_SequenceManager’s Sort() method calls SortList() and passes to it a reference to the aSequence list.
METHOD PUBLIC Sort
VAR_INPUT
ipSequenceSortable : I_SequenceSortable;
END_VAR
IF (ipSequenceSortable <> 0) THEN
ipSequenceSortable.SortList(THIS^._aSequence);
END_IF
This means that a reference to the list to be sorted is passed to the function block containing the implemented sorting algorithm.
We create a separate function block for each sorting algorithm. This means we have access both to FB_SequenceManager containing the Sort() method, and to function blocks containing the sorting algorithms and implementing the I_SequenceSortable interface.
When it calls the Sort() method, FB_SequenceManager passes to it a function block (in our case FB_SequenceSortedByIdAscending). This function block contains the I_SequenceSortable interface subsequently used to call the SortList() method.
PROGRAM MAIN
VAR
fbSequenceManager : FB_SequenceManager;
fbSequenceSortedByIdAscending : FB_SequenceSortedByIdAscending;
// …
END_VAR
fbSequenceManager.Sort(fbSequenceSortedByIdAscending);
// …
This approach avoids the use of inheritance. The sorting algorithm function blocks could employ their own inheritance hierarchy if required. These function blocks could also implement additional interfaces, since it is possible to implement multiple interfaces.
Using this interface realises a clear separation between data storage (the list) and data processing (sorting). The aSequence property does not need write access. We also avoid the need to access internal FB_SequenceManager variables.
In addition, we no longer need the E_SortedOrder and E_SortedDirection data types. The sort type is determined solely by which function block we pass to Sort().
We can also add new sorting algorithms without needing to modify or change existing elements.
Sample 4 (TwinCAT 3.1.4024) on GitHub
Optimization analysis
There are various methods for extending the functionally of an existing function block without having to modify it. As well as inheritance – a key feature of object-oriented programming (OOP) – interfaces may provide a better alternative.
Using interfaces brings greater decoupling. But the individual interfaces do have to be implemented in the software design. This means that we need to consider in advance which areas need to be abstracted via interfaces and which don’t.
With inheritance too, when we develop a function block we have to consider which internal elements should be made accessible (by using the PROTECTED keyword) to function blocks derived from it.
The definition of the Open/Closed Principle
The Open/Closed Principle (OCP) was originated in 1988 by Bertrand Meyer. It states:
Software entities should be open for extension, but closed for modification.
Software entity: This means a class, function block, module, method, service, etc.
Open: The behaviour of a software entity should be able to be extended.
Closed: This extensibility should not be achieved by modifying existing software.
When Bertrand Meyer defined the Open/Closed Principle (OCP) in the late 1980s, the focus was on C++ as a programming language. He used the concept of inheritance, a familiar concept in the object-oriented programming world. Object-orientated programming – at the time a fairly young discipline – was seen as promising big improvements in terms of reusability and maintainability as a result of the ability to reuse classes as base classes for new classes.
When Robert C. Martin took up Meyer’s principle in the 1990s, he took a different approach to its technical implementation. C++ allows the use of multiple inheritance, which is rare in more recent programming languages. Consequently, Robert C. Martin focused on the use of interfaces. More information can be found in his book (Amazon ad link *) Clean Architecture: A Craftsman’s Guide to Software Structure and Design.
Summary
Adhering to the Open/Closed Principle (OCP) does carry a risk of overengineering. We should only implement extensibility where it is actually needed. It is impossible to design software so that every conceivable extension can be implemented without needing to modify the source code.
This concludes my series of posts on SOLID principles. Other principles are of course available, including Keep It Simple, Stupid (KISS), Don’t Repeat Yourself (DRY), Law Of Demeter (LOD) and You Ain’t Gonna Need It (YAGNI). What all these principles have in common is the goal of making software more maintainable and more reusable.
Das aktuelle .NET-Release zeigt Vorschläge für Befehle im Kommandozeilenfenster. Die Autovervollständigung muss man allerdings zunächst aktivieren.
For about a year, I was working on a pretty exciting project. I defined and created a new role for our company that is responsible for application security. Actually, application security was never a missing aspect of our development process. All my colleagues were great developers and highly motivated to create secure applications.
The actual problem was a missing standard, that sets up every project in the same secure way, which helps the QA to also test security aspects, use the same tools to improve the software quality and software security and keep the awareness on security during the entire development process.
Defining the new role
Defining and creating this new role also means that I will take over this new role and be responsible, to ensure and to maintain the security standards throughout the entire company. I will also be responsible to keep awareness about security throughout the entire company and to train the developers, the DevOps responsible person as well as the QA. I am not the only person that is responsible for application security in general. My job will be to make all colleagues feel responsible to create secure applications. That they all have security in mind, that all features will be analyzed from the security perspective as well.
And this is why the process of secure software development doesn't start with development or DevOps.
Adding security to the company
Actually, the secure software development process starts at the sales phase. The salesperson needs to know what type of customer he is selling our services to, what type of data the potential customer will be handle with the new project, and what type of possible risks there are. The salesperson needs to know what level of security he needs to sell. (Exactly, we need to sell security. See next section why)
The process continues with the requirements engineering, even the UX and UI specialists need to take care of security. DevOps is following, by setting up the secure software development infrastructure and secure deployments per project. DevOps is also supporting the developers with the right tools that check for software quality and possible vulnerabilities and code flaws while building and delivering the software. In the development phase, required security aspects need to be implemented and QA needs to know how to test this. Maybe tooling will support the QA to make automated security tests.
Selling security
True, we need to sell levels of security because ensuring application security needs some effort. The more secure the more effort during development and afterward, while ensuring and testing the application security. A potential customer in a sensitive or risky environment should know that security is not for free. A bank, a power plant, and other big and risky industry are paying for security personnel that keeps unauthorized people outside of their restricted areas. Such companies should be aware and willing to pay for implementing higher security mechanisms to keep unauthorized people out of their digitally restricted areas as well.
Actually, application security needs to be ensured in every project and the basic level of security won't be charged separately.
What are the standards we use?
It is the OWASP foundation that helps me dive into new topics. We are going to implement the OWAS Application Security Verification Standard (ASVS) and the mobile version of it (MASVS). ASVS already is divided into three levels of security. Level 1 is the basic level of security that all projects need to implement. Actually, Level 1 is quite basic and despite just a few topics, this is all stuff we as developers almost already knew, used, and implemented in the past. If we have thought about it and if the project pressure wasn't that high. Levels 2 and 3 are adding more security mechanisms to the projects to handle sensitive and critical data and infrastructure.
This standard is helping us like a blueprint for all our projects to keep the levels of security and it helps our QA to know what to test from the security point of view.
Actually, since ASVS is adopting and covering many other standards as well, we will be safe with future security audits, no matter what standard will be used during a possible audit.
Will YOO be a secure software company?
The company already creates secure software. Since it was more or less a side aspect in the past, we'll now focus on security by following the standards and the process we have implemented.
So, yes, we can now call ourselves a secure software company. But we are not certified somehow. OWASP and ASVS are no badges nor provide certificates we can put proudly and high-nosed on our website. But we can proudly mention that we are following a standard that was created by well-known and independent security experts.
My main role is still a software engineer :-)
The application security role is only an additional role to my position as a software engineer. In a midsized company like the YOO ensuring secure software is not such a high effort that it is needed to create a new position like an application security engineer. And therefore it is just a new additional role for me.
Despite that, my job title will change a little bit and will be called Software & AppSec Engineer.
Learning about application security
Actually, application security as a global topic was kinda new to me and I never expected that it is needed to have the entire company involved. But that also was the fun part: Talking to other disciplines and talking to people that are not really involved in my day-to-day business. It is not completely technical to implement application security in a software company.
As mentioned, the website of the OWASP Foundation points me to various learning resources. OWASP is full of projects to learn about application security. You might know the OWASP Top 10 list of security risks. I already mentioned the ASVS. But there is a ton more.
Another great learning resource is the Twitter feed of Tanya Janka. Her talks about application security are amazing and her book is a great read, even on vacation at the beaches of Greece while the kids are playing:

If you want to learn about application security, follow her on Twitter, read her blog, read her book, and watch her talks. You also need to dive into the OWASP website and the various projects of the foundation.
Furthermore
And maybe, you as a software-producing company would like to adopt the same standards and processes. If you would like to know, how we created and implemented the secure software process, feel free to ask. If you need help to make your software development process more secure, we would be happy to help as well.
Learn more: https://www.yoo.digital/applicationsecurity
Conclusion
I'm happy to start working on my new role officially this week and I'm happy to bring the YOO a step further in creating and delivering high-quality and secure software. I'm also pretty excited about how it will go and grow over time. The implementation of a secure software process will never be complete and needs to be adjusted whenever it's needed. It is a living process that needs reviews and adjustments regularly.
.NET 6.0 hat einige generische Mathematikoperationen für beliebige Zahlentypen eingeführt, die im aktuellen .NET produktionsreif sind.
Die jüngste C#-Version ermöglicht das Überladen von Operatoren nicht nur in Klassen, sondern auch in Interfaces.
The AI world is rising very fast these days: ChatGPT is such an awesome (and scary good?) service and Microsoft joined the ship with some partner announcements and investments. The result is of these actions is, that OpenAI is now a “first class citizen” on Azure.
So - for the average Microsoft/.NET developer this opens up a wonderful toolbox and the first steps are really easy.
Be aware: You need to “apply” to access the OpenAI service, but it took less then 24 hours for us to gain access to the service. I guess this is just a temporary thing.
Disclaimer: I’m not an AI/ML engineer and I only have a very “glimpse” knowledge about the technology behind GPT3, ChatGPT and ML in general. If in doubt, I always ask my buddy Oliver Guhr, because he is much smarter in this stuff. Follow him on Twitter!
1. Step: Go to Azure OpenAI Service
Search for “OpenAI” and you will see the “Azure OpenAI Service” entry:

2. Step: Create a Azure OpenAI Service instance
Create a new Azure OpenAI Service instance:

On the next page you will need to enter the subscription, resource group, region and a name (typical Azure stuff):

Be aware: If your subscription is not enabled for OpenAI, you need to apply here first.
3. Step: Overview and create a model
After the service is created you should see something like this:

Now go to “Model deployments” and create a model - I choosed “text-davinci-003”, because I think this is GPT3.5 (which was the initial ChatGPT release, GPT4 is currently in preview for Azure and you need to apply again.

My guess is, that you could train/deploy other, specialized models here, because this model is quite complex and you might want to tailor the model for your scenario to get faster/cheaper results… but I honestly don’t know how to do it (currently), so we just leave the default.
4. Step: Get the endpoint and the key
In this step we just need to copy the key and the endpoint, which can be found under “Keys and Endpoint”, simple - right?

5. Step: Hello World to our Azure OpenAI instance
Create a .NET application and add the Azure.AI.OpenAI NuGet package (currently in preview!).
dotnet add package Azure.AI.OpenAI --version 1.0.0-beta.5
Use this code:
using Azure.AI.OpenAI;
using Azure;
Console.WriteLine("Hello, World!");
OpenAIClient client = new OpenAIClient(
new Uri("YOUR-ENDPOINT"),
new AzureKeyCredential("YOUR-KEY"));
string deploymentName = "text-davinci-003";
string prompt = "Tell us something about .NET development.";
Console.Write($"Input: {prompt}");
Response<Completions> completionsResponse = client.GetCompletions(deploymentName, prompt);
string completion = completionsResponse.Value.Choices[0].Text;
Console.WriteLine(completion);
Console.ReadLine();
Result:
Hello, World!
Input: Tell us something about .NET development.
.NET development is a mature, feature-rich platform that enables developers to create sophisticated web applications, services, and applications for desktop, mobile, and embedded systems. Its features include full-stack programming, object-oriented data structures, security, scalability, speed, and an open source framework for distributed applications. A great advantage of .NET development is its capability to develop applications for both Windows and Linux (using .NET Core). .NET development is also compatible with other languages such as
As you can see… the result is cut off, not sure why, but this is just a simple demonstration.
Summary
With these basic steps you can access the OpenAI development world. Azure makes it easy to integrate in your existing Azure/Microsoft “stack”. Be aware, that you could also use the same SDK and use the endpoint from OpenAI. Because of billing reasons it is easier for us to use the Azure hosted instances.
Hope this helps!
Video on my YouTube Channel
If you understand German and want to see it in action, check out my video on my Channel:
In our product we can interact with different datasource and one of these datasources was a Microsoft Access DB connected via OLEDB
. This is really, really old, but still works, but on one customer machine we had this issue:
'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine
Solution
If you face this issue, you need to install the provider from here.
Be aware: If you have a different error, you might need to install the newer provider - this is labled as “2010 Redistributable”, but still works with all those fancy Office 365 apps out there.
Important: You need to install the provider in the correct bit version, e.g. if you run under x64, install the x64.msi.
The solution comes from this Stackoverflow question.
Helper
The best tip from Stackoverflow was these powershell commands to check, if the provider is there or not:
(New-Object system.data.oledb.oledbenumerator).GetElements() | select SOURCES_NAME, SOURCES_DESCRIPTION
Get-OdbcDriver | select Name,Platform
This will return something like this:
PS C:\Users\muehsig> (New-Object system.data.oledb.oledbenumerator).GetElements() | select SOURCES_NAME, SOURCES_DESCRIPTION
SOURCES_NAME SOURCES_DESCRIPTION
------------ -------------------
SQLOLEDB Microsoft OLE DB Provider for SQL Server
MSDataShape MSDataShape
Microsoft.ACE.OLEDB.12.0 Microsoft Office 12.0 Access Database Engine OLE DB Provider
Microsoft.ACE.OLEDB.16.0 Microsoft Office 16.0 Access Database Engine OLE DB Provider
ADsDSOObject OLE DB Provider for Microsoft Directory Services
Windows Search Data Source Microsoft OLE DB Provider for Search
MSDASQL Microsoft OLE DB Provider for ODBC Drivers
MSDASQL Enumerator Microsoft OLE DB Enumerator for ODBC Drivers
SQLOLEDB Enumerator Microsoft OLE DB Enumerator for SQL Server
MSDAOSP Microsoft OLE DB Simple Provider
PS C:\Users\muehsig> Get-OdbcDriver | select Name,Platform
Name Platform
---- --------
Driver da Microsoft para arquivos texto (*.txt; *.csv) 32-bit
Driver do Microsoft Access (*.mdb) 32-bit
Driver do Microsoft dBase (*.dbf) 32-bit
Driver do Microsoft Excel(*.xls) 32-bit
Driver do Microsoft Paradox (*.db ) 32-bit
Microsoft Access Driver (*.mdb) 32-bit
Microsoft Access-Treiber (*.mdb) 32-bit
Microsoft dBase Driver (*.dbf) 32-bit
Microsoft dBase-Treiber (*.dbf) 32-bit
Microsoft Excel Driver (*.xls) 32-bit
Microsoft Excel-Treiber (*.xls) 32-bit
Microsoft ODBC for Oracle 32-bit
Microsoft Paradox Driver (*.db ) 32-bit
Microsoft Paradox-Treiber (*.db ) 32-bit
Microsoft Text Driver (*.txt; *.csv) 32-bit
Microsoft Text-Treiber (*.txt; *.csv) 32-bit
SQL Server 32-bit
ODBC Driver 17 for SQL Server 32-bit
SQL Server 64-bit
ODBC Driver 17 for SQL Server 64-bit
Microsoft Access Driver (*.mdb, *.accdb) 64-bit
Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb) 64-bit
Microsoft Access Text Driver (*.txt, *.csv) 64-bit
Hope this helps! (And I hope you don’t need to deal with these ancient technologies for too long 😅)
Stellenanzeigen gleichen sich üblicherweise wie ein Ei dem anderen, statt den ersten individuellen Eindruck zu vermitteln, den man von einem Unternehmen erhält.
Every now and then you’ll try to watch a movie trailer or a music video on YouTube only to see this message: “This video is not available in your country”. And that can really spoil the fun. But here’s the good news: There is a way to watch blocked youtube videos from anywhere. If you get a reliable VPN service like CyberGhost*, blocked YouTube videos won’t be a problem anymore. VPNs give you access to different geo-locations. Switching countries (virtually) will allow you to watch geo-blocked YouTube videos. VPNs will also encrypt your connection, making it private and secure. So, you can easily watch blocked YouTube videos with a VPN. Let’s see how it works…
Why Are Some YouTube videos blocked?
YouTube is that massively popular video-sharing platform where you can watch tons of interesting stuff. The billion users can watch, like, subscribe, comment, upload and share videos, and it’s completely free. However, as we have already mentioned, you might encounter annoying geo-restrictions.
The site has licensed content including TV shows, movies and even music videos. Some of them are only licensed for specific countries. YouTube is truly heavily restricted in some parts of the world. For example, the popular video platform is completely blocked in Iran, China, Pakistan and North Korea. There can also be other explanations for why certain YouTube videos are inaccessible.
So, in brief, here are the most common reasons why some YouTube videos might be blocked:
- Licensing Rights – copyright laws or other laws regarding content distribution have placed restrictions covering certain countries or regions.
- Censorship – Some strict countries apply censorship on online content. YouTube can be blocked entirely or partially, depending on the local laws.
- Network Blocks – YouTube can be blocked because of the Network blocks on offices or school properties.
How to unblock YouTube videos in your country – A quick guide
VPN usage is without a doubt the most secure and effective way to get around the region filter and watch blocked videos on YouTube. A trustworthy VPN service will allow you to safely browse the web as if you were in a different country. The VPN will trick the blocked site, in this case it’s YouTube, by masking your local IP address and temporarily changing it. YouTube will assume that you are accessing it from a foreign country.
Time needed: 15 minutes.
Just follow these steps and you’ll be able to watch blocked YouTube videos in no time:
- Get a good VPN
Our top choice is CyberGhost*,a reliable VPN services with a great reputation. You can try using a free VPN but we definitely won’t recommend that option. It’s not really trustworthy and safe.
- Download and install the app
You’ve chosen a VPN? Great, install it and open the app/client.now connect to a server in the correct region. You have to make sure your traffic is being routed through your VPN. Open up the VPN app and toggle it on.
- Go to YouTube and find the video you want to unblock
It’s time to head to YouTube. Open your YouTube app or go to www.youtube.com. Find the blocked video that you want to watch. If the video is allowed in your VPN’s region, you’ll be allowed to watch it. If it doesn’t then you should check your VPN’s regional settings.
- Enjoy YouTube’s content!
That’s it! It’s a really fast and simple trick that will save you a lot of headaches.
VPN for YouTube: The most important features
How to choose the right VPN to unblock YouTube videos? Here are the most important features to look out for:
- The speed of the service: You will need fast server speeds in order to stream YouTube videos smoothly. It’s essential to choose a VPN that offers fast speeds.
- Security credentials: If you’re in a country that has banned YouTube, you’ll want to make sure you have chosen a virtual private network that will protect your online identity. Reputable VPN services offer good security features that will keep you more anonymous.
- The devices it supports: If you’re planning to watch YouTube videos on your smart TV, streaming stick, mobile phone or games console, make sure that your chosen VPN works on all of the devices you’re wanting to use.
- The reputation: Why do people always search for the reviews of all sorts of products? Because a good advice and opinion from someone who has already tried a certain product/service is always helpful. It can guide you in your decision making process. It’s important to get a VPN that has a good reputation and a solid customer support.

About CyberGhost – Watch blocked YouTube videos with CyberGhost
So why is CyberGhost* our top recommendation?
CyberGhost is based in Romania and boasts a plethora of excellent security features that you can fully trust. With this provider, you’ll have access to top-notch encryption (AES 256-bit), split tunneling, integrated ad, and malware protection, SmartDNS, private DNS, and even an automatic kill-switch to safeguard your online activities if your VPN connection unexpectedly drops. The service also operates over 6,000 servers across the globe.
A crucial element to note is the stringent No-Logs policy that CyberGhost adheres to. This means that CyberGhost neither retains nor tracks your data. There’s no data storage or sharing – your digital footprint stays yours alone. Hence, CyberGhost becomes an ideal choice for those who are anxious about government surveillance and censorship, those who seek anonymity and privacy, and those who simply desire a safe and secure online experience.
FAQ – Frequently Asked Questions
Why is my YouTube video blocked? Some YouTube videos may be unavailable because video owners have chosen to make their content available only to certain countries/regions due to licensing rights. YouTube can also block certain content to comply with local laws. The governments in some countries can block videos because of censorship. For example, a certain video might be at odds with a country’s culture and beliefs. So, it will be blocked.
Sometimes, even offices and schools would ban YouTube. That’s because if employees and students are able to watch videos during their work hours they won’t be focused and productive.
How do I unblock YouTube videos? A trustworthy VPN with a flawless reputation is the best way to enjoy blocked YouTube videos. Get a VPN browser extension, select any country and try opening the video. VPNs hide your real IP address, temporarily replacing it with an address from the country you have selected. That’s an easy way to bypass any geological restrictions that a YouTube video might have.
Is unblocking YouTube safe? Yes, it should be perfectly safe when you’re using a dependable VPN service. And not only will a virtual private network (VPN) unblock any YouTube video, so you can watch it without a problem, but it will also encrypt everything you do on the internet. That means you can remain completely anonymous, keep your online activities private and stay protected from hackers and other third party snoopers.
The post Watch blocked youtube videos with a VPN appeared first on xplatform.rocks.
I was playing around with some Visual Studio Tooling and noticed this error during the creation of a “Azure Container Apps”-app:
Resource type is not supported in this subscription

Solution
The solution is quite strange at first, but in the super configurable world of Azure it makes sense: You need to activate the Resource provider for this feature on your subscription. For Azure Container Apps
you need the Microsoft.ContainerRegistry
-resource provider registered:

It seems, that you can create such resources via the Portal, but if you go via the API (which Visual Studio seems to do) the provider needs to be registered at first.
Some resource providers are “enabled by default”, other providers needs to be turned on manually. Check out this list for a list of all resource providers and the related Azure service.
Be careful: I guess you should only enable the resource providers that you really need, otherwise your attack surface will get larger.
To be honest: This was completly new for me - I do Azure since ages and never had to deal with resource providers. Always learning ;)
Hope this helps!
In der neuen Version von Microsofts Programmiersprache C# dürfen Schnittstellendeklarationen von Properties und Methoden als static abstract deklariert sein.
Vererbung ist eine beliebte Methode, um bestehende Funktionsblöcke wiederzuverwenden. Dadurch lassen sich Methoden und Eigenschaften hinzufügen oder bestehende Methoden überschreiben. Hierbei ist es nicht notwendig, den Quellcode des Basis-FB zur Verfügung zu haben. Software so zu designen, dass Erweiterungen möglich sind, ohne die vorhandene Software zu verändern, ist die Grundidee des Open/Closed Principle (OCP). Doch die Anwendung von Vererbung hat hierbei auch Nachteile. Der Einsatz von Schnittstellen minimiert diese Nachteile und bietet zusätzliche Vorteile.
Mit anderen Worten: Das Verhalten von Software sollte erweiterbar sein, ohne dass sie modifiziert werden muss. Angelehnt an das Beispiel aus den bisherigen Posts, soll ein Funktionsblock entwickelt werden, um Sequenzen für die Ansteuerung von Lampen zu verwalten. Anschließend wird der Funktionsblock um zusätzliche Funktionen erweitert. Anhand dieses Beispiels wird die Grundidee des Open/Closed Principle (OCP) genauer betrachtet.
Ausgangssituation
Zentraler Ausgangspunkt ist der Funktionsblock FB_SequenceManager. Dieser stellt über die Eigenschaft aSequence die einzelnen Schritte einer Sequenz zur Verfügung. Über die Methode Sort() kann die Liste nach verschiedenen Kriterien sortiert werden.
Die Eigenschaft aSequence ist ein Array und enthält Elemente vom Typ ST_SequenceItem.
PROPERTY PUBLIC aSequence : ARRAY [1..5] OF ST_SequenceItem
Um das Beispiel überschaubar zu halten, wird mit festen Arraygrenzen von 1 bis 5 gearbeitet. Die Array-Elemente sind vom Typ ST_SequenceItem und enthalten eine eindeutige Id (nId), den Ausgangswert (nValue) für die Lampen und die Dauer (nDuration) bis zum Umschalten auf den nächsten Ausgangswert.
TYPE ST_SequenceItem :
STRUCT
nId : UINT;
nValue : USINT(0..100);
nDuration : UINT;
END_STRUCT
END_TYPE
Auf sämtliche Methoden für die Bearbeitung der Sequenz wurde im Rahmen dieses Beispiels verzichtet. Allerdings enthält das Beispiel die Methode Sort(), um die Liste nach verschiedenen Kriterien zu sortieren.
METHOD PUBLIC Sort
VAR_INPUT
eSortedOrder : E_SortedOrder;
END_VAR
Die Liste kann aufsteigend nach nId oder nValue sortiert werden.
TYPE E_SortedOrder :
(
Id,
Value
);
END_TYPE
In der Methode Sort() wird durch den Eingangsparameter eSortedOrder entschieden, ob nach nId oder nach nValue sortiert werden soll.
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId
// …
E_SortedOrder.Value:
// Sort the list by nValue
// …
END_CASE
Bei dem Beispiel handelt es sich um eine einfache monolithische Anwendung, welche in kurzer Zeit erstellt werden kann, um die gewünschten Anforderungen zu erfüllen.
Das UML-Diagramm zeigt recht deutlich den monolithischen Aufbau der Anwendung:
Dabei wurde allerdings nicht berücksichtigt, mit welchem Aufwand zukünftige Erweiterungen realisierbar sind.
Beispiel 1 (TwinCAT 3.1.4024) auf GitHub
Erweiterung der Implementierung
Die Anwendung soll so erweitert werden, dass nicht nur nach nId und nValue sortiert werden kann, sondern auch zusätzlich nach nDuration. Bisher wurde die Liste immer aufsteigend sortiert. Eine absteigende Sortierung ist ebenfalls gewünscht.
Wie lässt sich unser Beispiel anpassen, damit die beiden Kundenwünsche erfüllt werden?
Ansatz 1: Quick & Dirty
Ein Ansatz besteht darin, die vorhandene Methode Sort() einfach zu erweitern, so dass diese auch nach nDuration sortieren kann. Hierzu wird E_SortedOrder um das Feld eDuration erweitert.
TYPE E_SortedOrder :
(
Id,
Value,
Duration
);
END_TYPE
Zusätzlich wird noch ein Parameter benötigt, der angibt ob in aufsteigender oder in absteigender Reihenfolge sortiert werden soll:
TYPE E_SortedDirection :
(
Ascending,
Descending
);
END_TYPE
Somit besitzt die Methode Sort() jetzt zwei Parameter:
METHOD PUBLIC Sort
VAR_INPUT
eSortedOrder : E_SortedOrder;
eSortedDirection : E_SortedDirection;
END_VAR
Die Methode Sort() enthält jetzt zwei ineinander verschachtelte CASE-Anweisungen. Die Äußere für die Auswahl der Sortierrichtung und die Innere für das Element nach dem sortiert wird.
CASE eSortedDirection OF
E_SortedDirection.Ascending:
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId in ascending order
// …
E_SortedOrder.Value:
// Sort the list by nValue in ascending order
// …
E_SortedOrder.Duration:
// Sort the list by nDuration in ascending order
// …
END_CASE
E_SortedDirection.Descending:
CASE eSortedOrder OF
E_SortedOrder.Id:
// Sort the list by nId in descending order
// …
E_SortedOrder.Value:
// Sort the list by nValue in descending order
// …
E_SortedOrder.Duration:
// Sort the list by nDuration in descending order
// …
END_CASE
END_CASE
END_CASE
Dieser Ansatz ist schnell umzusetzen. Bei einer kleinen Anwendung, wo der Quellcode nicht sehr umfangreich ist, durchaus ein guter Ansatz. Allerdings muss der Quellcode zur Verfügung stehen, damit diese Erweiterungen überhaupt möglich sind. Außerdem muss sichergestellt sein, dass FB_SequenceManager nicht mit anderen Projekten geteilt wird, z. B. durch eine SPS-Bibliothek in der FB_SequenceManager enthalten ist. Da bei der Methode Sort() ein Parameter hinzugekommen ist, hat sich die Signatur geändert. Programmteile, die die Methode mit einem Parameter aufrufen, lassen sich dadurch nicht mehr compilieren.
Durch das UML-Diagramm ist gut zu erkennen, dass sich die Struktur nicht geändert hat. Es ist weiterhin eine sehr monolithische Anwendung:
Beispiel 2 (TwinCAT 3.1.4024) auf GitHub
Ansatz 2: Vererbung
Ein weiterer Ansatz, um die Anwendung mit den gewünschten Funktionen zu erweitern, ist der Einsatz von Vererbung. Dadurch lassen sich Funktionsblöcke erweitern, ohne dass der vorhandene Funktionsblock verändert werden muss.
Hierzu wird als erstes ein neuer Funktionsblock angelegt, der von FB_SequenceManager erbt:
FUNCTION_BLOCK PUBLIC FB_SequenceManagerEx EXTENDS FB_SequenceManager
Der neue Funktionsblock erhält die Methode SortEx(), mit den beiden Parametern, welche die gewünschte Sortierung vorgibt:
METHOD PUBLIC SortEx : BOOL
VAR_INPUT
eSortedOrder : E_SortedOrderEx;
eSortedDirection : E_SortedDirection;
END_VAR
Auch hier wird wieder der Datentyp E_SortedDirection hinzugefügt, der angibt ob in aufsteigender oder in absteigender Reihenfolge sortiert werden soll:
TYPE E_SortedDirection :
(
Ascending,
Descending
);
END_TYPE
Statt E_SortedOrder zu erweitern, wir ein neuer Datentyp angelegt:
TYPE E_SortedOrderEx :
(
Id,
Value,
Duration
);
END_TYPE
In der Methode SortEx() können jetzt die gewünschten Sortierungen umgesetzt werden.
Bei der Sortierung in aufsteigender Reihenfolge ist der Zugriff auf die Methode Sort() des Basis-FBs (FB_SequenceManager) möglich. Dadurch ist eine erneute Implementierung der schon vorhandenen Sortieralgorithmen nicht erforderlich. Nur die zusätzliche Sortierung muss hinzugefügt werden:
CASE eSortedOrder OF
E_SortedOrderEx.Id:
SUPER^.Sort(E_SortedOrder.Id);
E_SortedOrderEx.Value:
SUPER^.Sort(E_SortedOrder.Value);
E_SortedOrderEx.Duration:
// Sort the list by nDuration in ascending order
// …
END_CASE
Die Sortierung in absteigender Reihenfolge muss allerdings komplett programmiert werden, da hier nicht auf bestehende Methoden zurückgegriffen werden kann.
Erbt ein Funktionsblock von einem anderen Funktionsblock, so erhält der neue Funktionsblock den Funktionsumfang des Basis-FBs. Durch zusätzliche Methoden und Eigenschaften kann dieser erweitert werden, ohne die Notwendigkeit, den Basis-FB zu verändern (offen für Erweiterungen). Durch den Einsatz von Bibliotheken kann der Quellcode auch komplett vor Veränderung geschützt werden (geschlossen gegen Modifikationen).
Vererbung ist somit eine Methode, um das Open/Closed Principle (OCP) umzusetzen.
Beispiel 3 (TwinCAT 3.1.4024) auf GitHub
Dieser Ansatz hat allerdings zwei Nachteile:
Durch den übermäßigen Einsatz von Vererbung können komplexe Hierarchien entstehen. Ein abgeleiteter FB ist fest an seinen Basis-FB gebunden. Wird der Basis-FB um weitere Methoden oder Eigenschaften erweitert, so erbt auch jeder abgeleitete FB diese Elemente (wenn diese PUBLIC sind), auch dann, wenn der abgeleitete FB diese Elemente nach Außen gar nicht anbieten möchte.
Eine Erweiterung durch Vererbung ist unter Umständen nur dann möglich, wenn die abgeleiteten Funktionsblöcke auf die internen Zustände des Basis-FBs Zugriff haben. Der Zugriff auf diese internen Elemente kann durch PROTECTED gekennzeichnet werden. Somit können nur abgeleitete Funktionsblöcke darauf zugreifen.
Im obigen Beispiel konnten nur deshalb die Sortieralgorithmen hinzugefügt werden, weil der Setter der Eigenschaft aSequence als PROTECTED deklariert wurde. Wäre ein Schreibzugriff auf die Eigenschaft aSequence nicht möglich, so könnte der abgeleitete Funktionsblock die Liste nicht verändern und somit auch nicht sortieren.
Dieses bedeutet aber, dass der Entwickler dieses Funktionsblocks immer zwei Anwendungsfälle berücksichtigen muss. Zum einen den Anwender, der die öffentlichen Methoden und Eigenschaften verwendet. Zusätzlich aber noch den Anwender, der den Funktionsblock als Basis-FB verwendet und auch über die PROTECTED Elemente neue Funktionalitäten hinzufügt. Doch welche internen Elemente sollen als PROTECTED markiert werden? Auch müssen diese Elemente dokumentiert werden, damit eine Anwendung überhaupt möglich ist.
Ansatz 3: zusätzliche Schnittstelle
Ein weiterer Lösungsansatz ist der Einsatz von Schnittstellen anstatt der Vererbung. Allerdings muss dieses direkt bei dem Design berücksichtigt werden.
Soll FB_SequenceManager so entworfen werden, dass der Anwender des Funktionsblocks beliebige Sortieralgorithmen hinzufügen kann, so sollte der Code für das Sortieren der Liste aus FB_SequenceManager entfernt werden. Der Zugriff aus dem Sortieralgorithmus auf die Liste sollte stattdessen über eine Schnittstelle erfolgen.
Bezogen auf unser Beispiel wird die Schnittstelle I_SequenceSortable hinzugefügt. Diese Schnittstelle enthält die Methode SortList(), welche eine Referenz auf die zu sortierende Liste enthält.
METHOD SortList
VAR_INPUT
refSequence : REFERENCE TO ARRAY [1..5] OF ST_SequenceItem;
END_VAR
Als nächstes werden die Funktionsblöcke angelegt, in denen die jeweiligen Sortieralgorithmen hinterlegt sind. Jeder dieser Funktionsblöcke implementiert die Schnittstelle I_SequenceSortable. Als Beispiel wird hier der Funktionsblock gezeigt, der nach nId aufsteigend sortiert.
FUNCTION_BLOCK PUBLIC FB_SequenceSortedByIdAscending IMPLEMENTS I_SequenceSortable
Der Name des Funktionsblocks ist beliebig, entscheidend ist die Implementierung der Schnittstelle I_SequenceSortable. Dadurch ist sichergestellt das FB_SequenceSortedByIdAscending die Methode SortList() enthält. In der Methode SortList() wird der eigentliche Sortieralgorithmus implementiert.
METHOD SortList
VAR_INPUT
refSequence : REFERENCE TO ARRAY [1..5] OF ST_SequenceItem;
END_VAR
// Sort the list by nId in ascending order
// …
FB_SequenceManager erhält in der Methode Sort() einen Parameter vom Typ I_SequenceSortable. Wird die Methode Sort() aufgerufen, so wird ein Funktionsblock (z.B. FB_SequenceSortedByIdAscending) übergeben, welcher die Schnittstelle I_SequenceSortable implementiert und somit auch die Methode SortList() enthält. In der Methode Sort() von FB_SequenceManager wird SortList() aufgerufen und eine Referenz der Liste aSequence übergeben.
METHOD PUBLIC Sort
VAR_INPUT
ipSequenceSortable : I_SequenceSortable;
END_VAR
IF (ipSequenceSortable <> 0) THEN
ipSequenceSortable.SortList(THIS^._aSequence);
END_IF
Dadurch erhält der Funktionsblock mit dem implementierten Sortieralgorithmus die Referenz auf die zu sortierende Liste.
Für jeden gewünschten Sortieralgorithmus wird ein Funktionsblock erstellt. Somit stehen uns zum einen FB_SequenceManager mit der Methode Sort() zur Verfügung und zum anderen die Funktionsblöcke, welche die Schnittstelle I_SequenceSortable implementieren und die Sortieralgorithmen enthalten.
Wird von FB_SequenceManager die Methode Sort() aufgerufen, so wird ein Funktionsblock übergeben (hier FB_SequenceSortedByIdAscending). Dieser Funktionsblock enthält die Schnittstelle I_SequenceSortable über die anschließend die Methode SortList() aufgerufen wird.
PROGRAM MAIN
VAR
fbSequenceManager : FB_SequenceManager;
fbSequenceSortedByIdAscending : FB_SequenceSortedByIdAscending;
// …
END_VAR
fbSequenceManager.Sort(fbSequenceSortedByIdAscending);
// …
Bei diesem Ansatz wird keine Vererbung angewendet. Die Funktionsblöcke für die Sortieralgorithmen könnten ihre eigene Vererbungshierarchie anwenden, falls dieses gefordert wird. Ebenfalls könnten die Funktionsblöcke weitere Schnittstellen implementieren, da das Implementieren mehrerer Schnittstellen möglich ist.
Datenhaltung (Liste) und Datenverarbeitung (Sortierung) sind durch den Einsatz der Schnittstelle klar voneinander getrennt. Die Eigenschaft aSequence benötigt keinen Schreibzugriff. Auch sind Zugriffe auf interne Variablen von FB_SequenceManager nicht notwendig.
Auch die beiden Datentypen E_SortedOrder und E_SortedDirection werden nicht benötigt. Die Auswahl der Sortierung wird ausschließlich durch den Funktionsblock bestimmt, der an Sort() übergeben wird.
Wird eine neue Sortierung hinzugefügt, so ist es nicht notwendig schon vorhandene Elemente zu verändern oder anzupassen.
Beispiel 4 (TwinCAT 3.1.4024) auf GitHub
Analyse der Optimierung
Es gibt verschiedene Techniken, um einen bestehenden Funktionsblock funktional zu erweitern, ohne diesen verändern zu müssen. Neben der Vererbung, eine der Hauptmerkmale der objektorientierten Programmierung (OOP), stellen Schnittstellen evtl. eine bessere Alternative dar.
Bei der Verwendung von Schnittstellen ist die Entkopplung größer. Allerdings müssen beim Softwareentwurf die einzelnen Schnittstellen implementiert werden. Es muss also im Vorfeld überlegt werden, welche möglichen Bereiche durch Schnittstellen abstrahiert werden und welche nicht.
Aber auch bei Verwendung von Vererbung muss bei der Entwicklung eines Funktionsblocks überlegt werden, welche internen Elemente den abgeleiteten Funktionsblöcken per PROTECTED angeboten werden.
Die Definition des Open/Closed Principle
Das Open/Closed Principle (OCP) wurde im Jahr 1988 von Bertrand Meyer formuliert und besagt:
Eine Softwareentität sollte offen für Erweiterungen, aber zugleich auch geschlossen gegenüber Modifikationen sein.
Softwareentität: Damit ist eine Klasse, Funktionsblock, Module, Methode, Service, … gemeint.
Offen: Das Verhalten von Softwaremodule sollte erweiterbar sein.
Geschlossen: Eine Erweiterbarkeit soll nicht dadurch erreicht werden, indem bestehende Software verändert wird.
Als das Open/Closed Principle (OCP) von Bertrand Meyer Ende der 80er definiert wurde, lag der Fokus auf der Programmiersprache C++. Er nutzte die in der objektorientierten Welt bekannte Vererbung. Die damals noch junge Disziplin der Objektorientierung versprach große Verbesserungen bei Wiederverwendbarkeit und Wartbarkeit dadurch, dass konkrete Klassen als Basisklassen für neue Klassen verwendet werden können.
Als Robert C. Martin in den 90er Jahren das Prinzip von Bertrand Meyer übernahm, setzte er es technisch anders um. C++ ermöglicht die Verwendung von Mehrfachvererbung, während in neueren Programmiersprachen Mehrfachvererbung eher selten anzutreffen ist. Aus diesem Grund setzte Robert C. Martin den Fokus auf die Verwendung von Schnittstellen. Weitere Informationen hierzu sind in dem Buch (Amazon-Werbelink *) Clean Architecture: Das Praxis-Handbuch für professionelles Softwaredesign zu finden.
Zusammenfassung
Die Einhaltung des Open/Closed Principle (OCP) birgt allerdings die Gefahr des Overengineering. Die Möglichkeit für Erweiterungen sollte nur dort implementiert werden, wo sie konkret benötigt wird. Eine Software lässt sich nicht so designen, dass jede denkbare Erweiterung umgesetzt werden kann, ohne dass Anpassungen an dem Quellcode notwendig sind.
Damit ist meine Serie über die SOLID-Prinzipien abgeschlossen. Neben den SOLID-Prinzipien gibt es allerdings noch weitere Prinzipien, wie z.B. Keep It Simple, Stupid (KISS), Don’t Repeat Yourself (DRY), Law Of Demeter (LOD) oder You Ain‘t Gonna Need It (YAGNI). All diese Prinzipen haben das gemeinsame Ziel, die Wartbarkeit und die Wiederverwendbarkeit von Software zu verbessern.
What is Playwright?
Playwright is a Web UI testing framework that supports different languages and is maintained by Microsoft. Playwright can be used with JavaScript/TypeScript, Python, Java and for sure C#. It comes with windowless browser support with various browsers. It has to be used with unit testing frameworks and because of this, you can just run it within your CI/CD pipeline. The syntax is pretty intuitive and I actually love it. Besides that the documentation is really good and helps a lot to easily start working with it.
In this blog post, I don't want to introduce Playwright. Actually, the website and the documentation is a much better resource to learn about the it. I would like to play around with it and to use it differently. Instead of testing a pre-hosted web application, I'd like to test a web application that is self hosted in the test project using the WebApplicationFactory
. This way you have really isolated UI tests that don't relate to on another infrastructure and won't fail because of network problems.
Does it work?
Let's try it:
Setting up the solution
The following lines create an ASP.NET Core MVC project and an NUnit test project. After that, a solution file will be created and the projects will be added to the solution. The last command adds the NUnit implementation of Playwright to the test project:
dotnet new mvc -n PlayWithPlaywright
dotnet new nunit -n PlayWithPlaywright.Tests
dotnet new sln -n PLayWithPlaywright
dotnet sln add .\PlayWithPlaywright\
dotnet sln add .\PlayWithPlaywright.Tests\
dotnet add .\PlayWithPlaywright.Tests\ package Microsoft.Playwright.NUnit
Run those commands and build the solution:
dotnet build
The build is needed to copy a PowerShell script to the output directory of the test project. This PowerShell script is the command line interface to control playwright.
At next we need to install the required browsers to execute the tests via that PowerShell:
.\PlayWithPlaywright.Tests\bin\Debug\net7.0\playwright.ps1 install
Generating test code
Using the codegen
command helps you to autogenerate test code that can be copied to the test project:
.\PlayWithPlaywright.Tests\bin\Debug\net7.0\playwright.ps1 codegen https://asp.net-hacker.rocks/
This command opens the Playwright Inspector where you can record your test case. While clicking through your application the test code will be generated on the right hand side:

Instead of testing an external website like I did, you can also call codegen
with a locally running application.
Just copy the generated code into the NUnit test project and fix the namespace and class name to match the namespace of your project.
Using the generated code as an example you will be able to write more the tests manually.
If this is done, just run dotnet test
to execute the generated test and just to verify that Playwright is working.
Start playing
Usually Playwright is testing applications that are running somewhere on a server. This as one simple problem: If the test cannot connect to the running application because of network issues the test will fail. Usually a test should only have one single reason to fail: It should fail because the expected behavior didn't occure.
The solution would be to test a web application that is hosted on the same infrastructure and within the same process as the actual test.
Microsoft already provided the possibility to write integration tests against a web application using the WebApplicationFactory. My Idea was to use this WebApplicationFactory
to host an application that can be tested with Playwright.
Since the WebApplicationFactory also provides a HttpClient, I would expect to have an URL to connect to. That HttpClient would have a BaseAddress that I can use to pass to Playwright.
Would this really work?
WebApplicationFactory and Playwright
Actually, we can't combine them by default because the WebApplicationFactory
doesn't really host a web application over HTTP. That means it doesn't use Kestrel to expose an endpoint over HTTP. The WebApplicationFactory
creates a test server that hosts the application in memory and just simulates an actual HTTP server.
We need to find a way to start a HTTP server, like Kestrel, to host the application. Actually we could start WebApplicationBuilder
but the Idea was to reuse the configuration of the Program.cs of the application we want to test. Like it is done with the WebApplicationFactory
.
Daniel Donbavand actually found a solution how to override the WebApplicationFactory to actually host the application over HTTP and to get an endpoint that can be used with Playwright. I used Daniels solution but made it a little more Generic.
Let's see how this works together with Playwright.
First, add a project reference to the web project within the Playwright test project and add a package reference to Microsoft.AspNetCore.Mvc.Testing.
dotnet add .\PlayWithPlaywright.Tests\ reference .\PlayWithPlaywright\
dotnet add .\PlayWithPlaywright.Tests\ package Microsoft.AspNetCore.Mvc.Testing
The first one is needed to use the Program.cs
with the WebApplicationFactory
. The second one adds the WebApplicationFactory
and the test server to the test project.
To use the Program
class that is defined in a Program.cs
that uses the minimal API you can simply add an empty partial Program class to the Program.cs
.
I just put the following line at the end of the Program.cs
:
public partial class Program { }
To make the Playwright tests as generic as possible I created an abstract SelfHostedPageTest
class that inherits the PageTest
class that comes with Playwright and use the CustomWebApplicationFactory
there and just expose the server address to the test class that inherits the SelfHostedPageTest
:
public abstract class SelfHostedPageTest<TEntryPoint> : PageTest where TEntryPoint : class
{
private readonly CustomWebApplicationFactory<TEntryPoint> _webApplicationFactory;
public SelfHostedPageTest(Action<IServiceCollection> configureServices)
{
_webApplicationFactory = new CustomWebApplicationFactory<TEntryPoint>(configureServices);
}
protected string GetServerAddress() => _webApplicationFactory.ServerAddress;
}
The actual Playwright test just inherits the SelfHostedPageTest
as follows instead of the PageTest
:
public class PlayWithPlaywrightHomeTests : SelfHostedPageTest<Program>
{
public PlayWithPlaywrightHomeTests() :
base(services =>
{
// configure needed services, like mocked db access, fake mail service, etc.
}) { }
// ...
}
As you can see, I pass in the Program type as generic argument to the SelfHostedPageTest
. The CustomWebApplicationFactory
that is used inside is almost the same implementation as done by Daniel. I just added the generic argument for the Program class and added the possibility to pass the service configuration via the constructor:
internal class CustomWebApplicationFactory<TEntryPoint> :
WebApplicationFactory<TEntryPoint> where TEntryPoint : class
{
private readonly Action<IServiceCollection> _configureServices;
private readonly string _environment;
public CustomWebApplicationFactory(
Action<IServiceCollection> configureServices,
string environment = "Development")
{
_configureServices = configureServices;
_environment = environment;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment(_environment);
base.ConfigureWebHost(builder);
// Add mock/test services to the builder here
if(_configureServices is not null)
{
builder.ConfigureServices(_configureServices);
}
}
// ...
}
Now we can use GetServerAddress()
to get the server address and to pass it to the Page.GotoAsync()
method:
[Test]
public async Task TestWithWebApplicationFactory()
{
var serverAddress = GetServerAddress();
await Page.GotoAsync(serverAddress);
await Expect(Page).ToHaveTitleAsync(new Regex("Home Page - PlayWithPlaywright"));
Assert.Pass();
}
That's it.
To try it out. just call dotnet test on the Command Line or PowerShell or run the relevant test in a test explorer.
Conclusion
The result with my test project looks like the following while running all the tests when I was offline:

One failing test is the recorded test session of my blog on https://asp.net-hacker.rocks/ and the other one is the demo test I found on https://playwright.dev. The passed test is the one that uses the CustomWebApplicationFactory
This is exactly the result I expected.
You'll find the the example on my GitHub repository.
Die jüngste Version von Microsofts Programmiersprache belegt in parameterlosen Konstruktoren die Felder und Properties von Strukturen mit Standardwerten.
The basic idea of the Interface Segregation Principle (ISP) has strong similarities with the Single Responsibility Principle (SRP): Modules with too many responsibilities can negatively influence the maintenance and maintainability of a software system. The Interface Segregation Principle (ISP) focuses on the module’s interface. A module should implement only those interfaces that are needed for its task. The following shows how this design principle can be implemented.
Starting situation
In the last post (IEC 61131-3: SOLID – The Liskov Substitution Principle), the example was extended by another lamp type (FB_LampSetDirectDALI). The special feature of this lamp type is the scaling of the output value. While the other lamp types output 0-100%, the new lamp type outputs a value from 0 to 254.
Just like all other lamp types, the new lamp type (DALI lamp) has an adapter (FB_LampSetDirectDALIAdapter). The adapters have been added during the implementation of the Single Responsibility Principle (SRP) and ensure that the function blocks of the individual lamp types are only responsible for a single function (see IEC 61131-3: SOLID – The Single Responsibility Principle).
The sample program was last adapted so that the output value from the new lamp type (FB_LampSetDirectDALI) is scaled within the adapter from 0-254 to 0-100 %. This makes the DALI lamp behave exactly like the other lamp types without violating the Liskov Substitution Principle (LSP).
This will serve as a starting point for explaining the Interface Segregation Principle (ISP).
Extension of the implementation
Also this time, the application has to be extended. However, it is not a new lamp type that is defined, but an existing lamp type is extended by a functionality. The DALI lamp should be able to count the operating hours. For this purpose, the function block FB_LampSetDirectDALI is extended by the property nOperatingTime.
PROPERTY PUBLIC nOperatingTime : DINT
The setter can be used to set the operating hours counter to any value, while the getter returns the current state of the operating hours counter.
Since FB_Controller represents the individual lamp types, this function block is also extended by nOperatingTime.
The operating hours are recorded in the FB_LampSetDirectDALI function block. If the output value is > 0, the operating hours counter is incremented by 1 every second:
IF (nLightLevel > 0) THEN
tonDelay(IN := TRUE, PT := T#1S);
IF (tonDelay.Q) THEN
tonDelay(IN := FALSE);
_nOperatingTime := _nOperatingTime + 1;
END_IF
ELSE
tonDelay(IN := FALSE);
END_IF
The variable _nOperatingTime is the backing variable for the new property nOperatingTime and is declared in the function block.
What possibilities are there to transfer the value of nOperatingTime from FB_LampSetDirectDALI to the property nOperatingTime of FB_Controller? Here, too, there are now various approaches of integrating the required extension into the given software structure.
Approach 1: Extension of I_Lamp
The property for the new feature is integrated into the I_Lamp interface. Thus, the abstract function block FB_Lamp also receives the nOperatingTime property. Since all adapters inherit from FB_Lamp, the adapters of all lamp types receive this property, regardless of whether the lamp type supports an operating hours counter or not.
The getter and the setter of nOperatingTime in FB_Controller can thus directly access nOperatingTime of the individual adapters of the lamp types. The getter of FB_Lamp (abstract function block from which all adapters inherit) returns the value -1. The absence of the operating hours counter can thus be detected.
IF (fbController.nOperatingTime >= 0) THEN
nOperatingTime := fbController.nOperatingTime;
ELSE
// service not supported
END_IF
Since FB_LampSetDirectDALI supports the operating hours counter, the adapter (FB_LampSetDirectDALIAdapter) overwrites the nOperatingTime property. The getter and the setter from the adapter access nOperatingTime from FB_LampSetDirectDALI. In this way, the value of the operating hours counter is passed on to FB_Controller.
(abstract elements are displayed in italics)
Sample 1 (TwinCAT 3.1.4024) on GitHub
This approach implements the feature as desired. Also, none of the SOLID principles shown so far are violated.
However, the central interface I_Lamp is extended only to add another feature for one lamp type. All other adapters of the lamp types, even those that do not support the new feature, also receive the nOperatingTime property via the abstract base FB_Lamp.
With each feature that is added in this way, the interface I_Lamp increases and so does the abstract base FB_Lamp.
Approach 2: Additional Interface
In this approach, the I_Lamp interface is not extended, but a new interface (I_OperatingTime) is added for the desired functionality. I_OperatingTime contains only the property necessary for providing the operating hours counter:
PROPERTY PUBLIC nOperatingTime : DINT
This interface is implemented by the adapter FB_LampSetDirectDALIAdapter.
FUNCTION_BLOCK PUBLIC FB_LampSetDirectDALIAdapter EXTENDS FB_Lamp IMPLEMENTS I_OperatingTime
Thus, FB_LampSetDirectDALIAdapter receives the property nOperationTime not via FB_Lamp or I_Lamp, but via the new interface I_OperatingTime.
If FB_Controller accesses the active lamp type in the getter of nOperationTime, it is checked before the access whether the selected lamp type implements the I_OperatingTime interface. If this is the case, the property is accessed via I_OperatingTime. If the lamp type does not implement the interface, -1 is returned.
VAR
ipOperatingTime : I_OperatingTime;
END_VAR
IF (__ISVALIDREF(_refActiveLamp)) THEN
IF (__QUERYINTERFACE(_refActiveLamp, ipOperatingTime)) THEN
nOperatingTime := ipOperatingTime.nOperatingTime;
ELSE
nOperatingTime := -1; // service not supported
END_IF
END_IF
The setter of nOperationTime is structured similarly. After the successful check whether I_OperatingTime is implemented by the active lamp, the property is accessed via the interface.
VAR
ipOperatingTime : I_OperatingTime;
END_VAR
IF (__ISVALIDREF(_refActiveLamp)) THEN
IF (__QUERYINTERFACE(_refActiveLamp, ipOperatingTime)) THEN
ipOperatingTime.nOperatingTime := nOperatingTime;
END_IF
END_IF
(abstract elements are displayed in italics)
Sample 2 (TwinCAT 3.1.4024) on GitHub
Optimization analysis
The use of a separate interface for the additional feature corresponds to the ‘optionality’ from IEC 61131-3: SOLID – The Liskov Substitution Principle. In the above example, it can be checked at runtime of the program (with __QUERYINTERFACE()) whether a specific interface is implemented and thus the respective feature is supported. Further features, like bIsDALIDevice from the ‘Optionality’ example, are not necessary with this solution approach.
If a separate interface is offered for each feature or functionality, other lamp types can also implement this in order to implement the desired feature. If FB_LampSetDirect also has to receive an operating hours counter, FB_LampSetDirect must be extended by the property nOperatingTime. In addition, FB_LampSetDirectAdapter must implement the I_OperatingTime interface. All other function blocks, including FB_Controller, remain unchanged.
If the functionality of the operating hours counter changes and I_OperatingTime receives additional methods, only the function blocks that also support the feature must be adapted.
Examples of the Interface Segregation Principle (ISP) can also be found in .NET. For example, .NET has the interface IList. This interface contains methods and properties for creating, modifying and reading listings. However, depending on the use case, it may be sufficient for the user to only read a listing. However, passing a listing through IList in this case would also provide methods to modify the listing. One can use the IReadOnlyList interface for these use cases. With this interface, a listing can only be read. Accidental modification of the data is therefore not possible.
Dividing functionalities into individual interfaces thus increases not only the maintainability but also the security of a software system.
The definition of the Interface Segregation Principle
This brings us to the definition of the Interface Segregation Principle (ISP):
A module that uses an interface should be presented with only those methods that the interface really needs.
Or to put it another way:
Clients should not be forced to depend on methods they do not need.
A common argument against the Interface Segregation Principle (ISP) is the increased number of interfaces. A software design can still be adapted at any time during its development cycles. So, if you feel that an interface contains too many functionalities, check whether segregation is possible. Of course, overengineering should always be avoided. A certain amount of experience can be helpful here.
Abstract function blocks also represent an interface (see FB_Lamp). An abstract function block can contain basic functions to which the user only adds the necessary details. It is not necessary to implement all the methods or properties yourself. Here also it is important not to burden the user with technicalities which are not necessary for his tasks. The set of abstract methods and properties should be as small as possible.
Adherence to the Interface Segregation Principle (ISP) keeps interfaces between functional blocks as small as possible, reducing coupling between each functional block.
Summary
If a software system has to cover further performance features, reflect the new requirements and do not hastily extend existing interfaces. Check whether separate interfaces are not a better decision. The reward is a software system that is easier to maintain, to test and to extend.
In the last pending part, the Open/Closed Principle (OCP) will be explained in more detail.
Ein neues Schlüsselwort für Microsofts Programmiersprache C# legt fest, dass Properties oder Felder zwingend gesetzt werden müssen.
Das Pattern Matching funktioniert in C# 11 mit Listen. Außerdem lassen sich Teilmengen extrahieren.