CODE 4-2022 Web
CODE 4-2022 Web
JUL
AUG
2022
codemag.com - THE LEADING INDEPENDENT DEVELOPER MAGAZINE - US $ 8.95 Can $ 11.95
An Introduction
to TypeScript
Features
8 S implifying ADO.NET Code 64 The Excellent Schemer
in .NET 6: Part 1 With Accelerate for Excel in Microsoft 365, Bob finds that
the Scheme tool makes writing user-defined functions easy.
Paul starts a new series about creating wrappers to limit the amount Bob Calco
of code you need to write when you’re working with ADO.NET in .NET 6.
Paul D. Sheriff
20 Writing High-Performance
Code Using Span<T> Columns
and Memory<T> in C# 74 CODA: Agility’s Primary Concern:
C# 7.2 has introduced two new types: Span and Memory.
Joydip dives in and finds that they’re incredibly useful. Content Distribution
Joydip Kanjilal Not everyone understands the true meaning of running an
agile shop. John reviews how to keep focus and get the most
27 uilding MVC Applications
B from this philosophy.
John V. Petersen
in PHP Laravel: Part 2
Last time, Bilal looked at Models—the M in MVC. This time,
he explains about Views and Controllers (the V and the C) and
how to take advantage of them in PHP Laravel.
Bilal Haidar Departments
38 TypeScript: An Introduction 6 Editorial
Everyone’s using JavaScript. That’s terrific, but it has its limitations.
Shawn shows you how to overcome those limitations using TypeScript.
Shawn Wildermuth 17 Advertisers Index
46 Developing Dashboards 73 Code Compilers
Using Grafana
Wei-Meng explores creating dashboards using Grafana, a great tool
for creating charts and other visual presentations of your data.
Wei-Meng Lee
US subscriptions are US $29.99 for one year. Subscriptions outside the US pay $50.99 USD. Payments should be made in US dollars drawn on a US bank. American Express,
MasterCard, Visa, and Discover credit cards are accepted. Back issues are available. For subscription information, send e-mail to subscriptions@codemag.com or contact
Customer Service at 832-717-4445 ext. 9.
Subscribe online at www.codemag.com
CODE Component Developer Magazine (ISSN # 1547-5166) is published bimonthly by EPS Software Corporation, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.
POSTMASTER: Send address changes to CODE Component Developer Magazine, 6605 Cypresswood Drive, Suite 425, Spring, TX 77379 U.S.A.
Longevity by Design
Early last month, I began working on a set of applications that may very well be my “last” applications
as a software engineer. I put the “last” in quotes for a reason. Technically these applications will not be
the LAST applications I work on but they will outlast me as a software engineer. Here’s why I started to
ponder this: Many of the applications I’ve worked cleaner and better code, and older applications Once I felt that we could achieve our look and feel
on have been used in one form or another for have what I like to call “seasoning,” meaning using Bootstrap, I moved on to the most important
many years after their initial development. These that their original features may have been aug- aspect of the application: The system architecture.
applications I’m starting on now will be around mented, replaced, or sometimes removed.
long after I retire as professional software engi- Using the same discovery process, I chooe one part
neer. After over 30 years in this business, it never Applications are living, breathing entities and of the application (a simple lookup table/screen)
ceases to amaze me just how long our applica- they need to be accorded respect for their lon- and built out the requisite features needed by the
tions live. gevity. Also, the longer an application is used, its system. This includes CRUD operations, validation
value to an organization likely grows. rules, service architecture, unit tests, integration
I have a little game I play when describing the tests, etc. Keep in mind that I didn’t build all of this
age of the applications. I envision my applica- It's this longevity that we must always consider from scratch. My team has around 10 years of code
tions as my children who have their own ages and as developers. For every application I build, I try that I was able to pull the best code from. Once I
personalities. I discuss each application in terms to keep in mind that these applications live on was happy with this architecture, I continued my
of childhood development. Some are toddlers, and on and on. I try not to take shortcuts. Or discovery with other system entities. I built around
some are in elementary school, some in middle when I do take a shortcut (knowing that reality half a dozen features and I saw that there’s a lot
school, some in high school and in one case, if always trumps theory when deploying applica- of boiler plate code that could be carved from the
the application had been a real person, I could’ve tions), I try to clean up after myself. system with a few carefully constructed classes. So
sat down for a beer with it before it was replaced I refactored the entire application and reduced the
by a younger sibling. To illustrate this, I’d like to discuss the new ap- lines of code by hundreds. Although no new fea-
plications I mentioned in my introduction. These tures were added, the architecture of the system
It may seem trivial to assign ages to applications are green field applications, and I’m doing my best improved greatly and will hopefully take us a long
as though they’re children. I disagree. This as- to ensure their success in the long term. I started way forward in the construction of this application.
signment of age gives a certain perspective to these projects using a discovery-based process, not
the general condition of an application. Gener- in architecture but in design. I know that these ap- Now, I could have just said “meh” that’s good
ally (with a capital G), newer applications have plications need a reasonably polished look and feel. enough, let’s move on. But I had the nagging
Rather than reinvent- feeling that this would insure our long-term suc-
ing the wheel, I start- cess. I’m hoping that this application lives on so
ed with Bootstrap 5 long that I can celebrate it with a beer when it’s of
as my design frame- age. Which gets me back to my original premise.
work. I like Bootstrap
because it’s a well- Let’s discuss the application I joked about having a
designed layout tool beer with. That application was built in Visual Fox-
with a broad depth of Pro around 1998/1999 against a beta build of SQL
adoption and knowl- Server 7.0. That application lived until 2019, when
edge surrounding it. it was replaced by a shiny new WPF application.
Bootstrap, like most This application has a newer architecture, is fully
frameworks, comes unit and integration tested, and is likely to be a
with its own set of critical application for that client for the next 20+
constraints and I years. Here’s the funny part: I was a bit sad that
wanted to make sure this application was replaced until I realized that
that it can support a TON of the code is baked into SQL Server stored
our look-and-feel procedures and some applications that are just
goals. Using the now hitting the 20-year mark. So that application
discovery method, I and some of its DNA lives on and, given its age, is
created a new form just about to be able to rent a car, put on some
and went to work sunglasses, and ride off into the sunset.
using Bootstrap to
build a page called
Standards. Figure 1 Rod Paddock
demonstrates the re-
sults of this discovery
process.
6 Editorial codemag.com
CUSTOM SOFTWARE DEVELOPMENT
STAFFING TRAINING/MENTORING SECURITY
Contact us today for a complimentary one hour tech consultation. No strings. No commitment. Just CODE.
codemag.com/code
832-717-4445 ext. 9 • info@codemag.com
codemag.com
ONLINE QUICK ID 2207021
If each of these ORMs are simply wrappers around ADO.NET, dynamic SQL or stored procedures. You’re going to be able
can you write your own wrapper to cut down the amount of to retrieve the identity value generated from SQL Server,
code you need to write? Absolutely! This series of articles handle data validation using the .NET data annotations, and
shows you how to create a set of reusable wrapper classes to add your own custom validation code. Finally, you’ll create a
make it simpler to work with ADO.NET in .NET 6. way to get excellent exception information to help you track
down any potential problems quickly and easily.
In these articles, you write code to map columns to
properties in a class just like the ORMs do. You check for
attributes such as [Column] and [Key] on class properties Using the ADO.NET Wrapper Classes
Paul D. Sheriff and use those attributes in your data application. You build Once you build a few reusable classes that you’ll learn about
http://www.pdsa.com SQL statements from your classes and submit that SQL to in this article, all you’ll need are an entity class, a search
the database efficiently and safely. By the end of these class, and a repository class for each table in your database.
Paul has been in the IT articles, you’re going to have a design pattern for typical Let’s look at some typical code you’re going to have to write
industry over 35 years. In CRUD applications that’s fast and requires less ADO.NET to retrieve rows from a table.
that time, he has success- code, just like the most popular ORMs out there. Besides
fully assisted hundreds
learning about ADO.NET, these articles teach you how to Get All Rows
of company’s architect
create a set of generic classes and methods. By the end of Once you have a “Database Context”, a “Repository”, and
software applications to
these articles, you’ll have some insight into how many ORMs an “Entity” class built, you can retrieve all rows in the table
solve their toughest busi-
ness problems. Paul has work. by using code shown in the following code snippet. Now,
been a teacher and mentor you must admit, this is very simple code and right one par
with what you’d write when using an ORM such as the Entity
through various mediums What’s in These ADO.NET Framework.
such as video courses,
blogs, articles and speak- Wrapper Classes
ing engagements at user The classes you’re going to create in these articles are not using AdvWorksDbContext db = new(connectString);
groups and conferences intended to be an ORM. Instead, they are going to help you
around the world. perform standard CRUD operations in an efficient manner List<Product> list = db.Products.Search();
Paul has many courses in and with less code. The classes you’re going to learn
the www.pluralsight. about will perform the same, and sometimes better, than // Display the Results
com library (http://www. the corresponding ORMs because there is less overhead. foreach (var item in list) {
pluralsight.com/author/ Another advantage of using the classes described herein is Console.WriteLine(item.ToString());
paul-sheriff) on topics that you’re writing straight-forward C# code and you don’t }
ranging from .NET 6, LINQ, have to learn any new configuration or tooling. You’re going
JavaScript, Angular, MVC, to make these classes generic so that you can use them with In the code above, create an instance of a database context
WPF, ADO.NET, jQuery, any .NET data provider such as SQL Server or Oracle. class, named AdvWorksDbContext, within a using so the
and Bootstrap. Contact connection is closed and disposed of properly after the data
Paul at psheriff@pdsa.com.
Read Operations is read. Within the AdvWorksDbContext class, you expose an
The classes you’re going to learn about in these articles instance of a ProductRepository class as a property named
will perform standard CRUD operations. For reading data, Products. Pass in a connection string to the constructor of
you can submit dynamic SQL, call views, or invoke stored the AdvWorksDbContext class. This connection string is used
procedures to return data and have that data automatically by the base class to create a connection to the database.
mapped to C# classes. Here’s what your database context class is going to look
like.
Standard data annotations such as the [Column] attribute
can be used to map a column to a property name public class AdvWorksDbContext :
that’s different from the column name. You can use the SqlServerDatabaseContext {
[NotMapped] attribute so you can add properties that public AdvWorksDbContext(string connectString)
have nothing to do with the table. You’re going to create : base(connectString) {
a custom attribute called [Search] to help you handle many }
searching scenarios in a generic manner.
public override void Init() {
Modify Operations base.Init();
These wrapper classes also allow you to submit action Products = new(this);
queries such as INSERT, UPDATE, and DELETE using either }
Listing 4: The Search() method is the public API to perform searching of records The DataReaderExtensions Class
public virtual List<Product> Search One of the challenges you may encounter when reading data
(string connectString, string sql) { from a table, is a null value in columns. When a null is read
List<Product> ret; from a data reader, .NET interprets it as a DbNull object.
You can’t put this value into a .NET data type, even if it’s
// Create a connection
using SqlConnection cnn=new(connectString); a nullable type. So, the best thing to do is to create an
extension method, named GetData<T>(), to handle these
// Create a command object DbNull values. For example, if your reader variable is named
using SqlCommand cmd = new(sql, cnn); rdr, call the GetData() method passing in the data type
// Open the connection
cnn.Open();
that you wish the data to be converted to as shown in the
// Create a data reader within a using following code snippet.
// so it is closed and disposed of properly
using SqlDataReader rdr = cmd.ExecuteReader ProductID = rdr.GetData<int>(“ProductID”);
(CommandBehavior.CloseConnection); Name = rdr.GetData<string>(“Name”);
// Build the collection of entity objects
ret = BuildEntityList(rdr); Right mouse-click on the project and add a new folder
named Common. Right mouse-click on this Common folder
return ret; and add a new class named DataReaderExtensions. Add the
}
code shown in Listing 3 to this file.
It’s now time to try out the code you wrote to ensure
that you can read data from the Product table and build a
collection of Product objects. Open the Program.cs file and Figure 1: You should see a couple hundred Product objects appear when you run the application.
delete all the code. Add the code shown in Listing 6 to this
file.
Try It Out
Run the console application, and if you typed everything in
correctly, you should see a set of products displayed, along
with the total items returned, as shown in Figure 1.
The next method to create helps you retrieve any output return DataReaderObject;
parameters returned from a stored procedure. This method }
GetParameter() returns an IDataParameter object, but
is an abstract method so you must override it in the
SqlServerDataContext, or OracleDataContext class you
Create a SQL Server Database
create. Context Class
Now that you have the generic DatabaseContext class
public abstract IDataParameter GetParameter created, build the SqlServerDatabaseContext class. This class
(string paramName); supplies the concrete “Sql*” objects to work with any SQL
Server database. Right mouse-click on the Common folder
The next two methods are used to create an instance of a and create a new class named SqlServerDatabaseContext.
data reader object. Ensure you’re returning the IDataReader Add the code shown in Listing 9. This class has a constructor
interface from these methods in this class. that must accept a connection string, and it passes it directly
to the base class. In the Init() method, which is called from
public virtual IDataReader CreateDataReader() { the base class, is where you set the ParameterPrefix property
return CreateDataReader(CommandObject, to the at (@) sign, which will be used by the methods dealing
CommandBehavior.CloseConnection); with parameters.
}
Let’s now add the various overrides to the methods that
public virtual IDataReader CreateDataReader( create the concrete implementations of the SQL objects.
CommandBehavior cmdBehavior) { Start by overriding the CreateConnection() method to
return CreateDataReader(CommandObject, return a SqlConnection object. You only need to override the
cmdBehavior); one method as the other method in the base class simply
} calls this one.
ParameterPrefix = “@”; The last method to override is the one that creates the
} SqlDataReader object. Pass in an instance of a command
} object and optionally, the CommandBehavior to use after
closing the reader.
public override SqlCommand CreateCommand You’re not going to create the OracleDatabaseContext class
(IDbConnection cnn, string sql) { in these articles. However, you follow the same procedures I
CommandObject = new SqlCommand(sql, did in this section for building the SqlServerDatabaseContext
(SqlConnection)cnn); class, just substituting the appropriate “Oracle*” classes for
CommandObject.CommandType = CommandType.Text; the “Sql*” classes used here.
return (SqlCommand)CommandObject; Modify the Search() Method to Use the SQL Server
} Database Context Class
Now that you have built the generic SqlServerDatabaseContext
The next methods to override are the ones that create new class, you should use this to perform the searching. Open the
parameter objects. The first method accepts a parameter ProductRepository.cs file and remove the using System.
name and the value to assign to the parameter. It checks Data.SqlClient; statement. Modify the Search() method to
to see if the paramName parameter starts with the value look like the following code.
in the ParameterPrefix property or not. If it doesn’t, then
the ParameterPrefix is added to the paramName parameter public virtual List<Product> Search
before the SqlParameter object is created. The second (string connectString, string sql) {
method returns an empty SqlParameter object. List<Product> ret;
When you make the call to the Search() method from the
Program.cs file, specify the name of the class to create as
the list of objects. In the code shown below, you pass in Visit dtSearch.com for
Product to the <TEntity> type parameter. • hundreds of reviews and case studies
List<Product> list = • fully-functional enterprise and
repo.Search<Product>(ConnectString, Sql); developer evaluations
Make BuildEntityList() a Generic Method
Modify the BuildEntityList() to be a generic method as The Smart Choice for Text
shown in Listing 10. Add the <TEntity> type parameter like
you did in the Search() method. The SqlDataReader object
Retrieval® since 1991
has a GetName() method, which retrieves the current
column name via an index. You need to be able to map dtSearch.com 1-800-IT-FINDS
this column name to the same property name in the type
of class passed in. Use the typeof() method on the type
parameter passed in and then call the GetProperties() be camel case. Or maybe you just want your property name
method to return all the properties in the class. You now to be more descriptive than the column name is. Whatever,
have all the property names, and you can get the column the reason, simply add a [Column] attribute above the
name from the data reader. All you need to do now is to property you want to be different. Open the Product.cs file
map the value from the data reader into the property of and rename the ProductID and the Name properties to be
the class. Id and ProductName respectively.
Loop through each row in the Product table and each time public int Id { get; set; }
through, create a new instance of a Product object using public string ProductName { get; set; }
the Activator.CreateInstance() method. Loop through each
column in the data reader object and set the field name Above the Id property, add the [Column] attribute to specify
into the variable named columnName. This code assumes the name of the actual column in the table.
that the column name in the table exactly matches the
property name in the class. Later you’re going to learn how [Column(“ProductID”)]
to map column names to property names using an attribute. public int Id { get; set; }
Look up the property by calling the props.FirstOrDefault()
method to locate where the property name is the same as Do the same for the renamed ProductName property, and
the column name. add the [Column] attribute to be the actual name of the
column in the table.
If a property is found with the same name, check to see if
the value in the column is not a DbNull value. If it isn’t, [Column(“Name”)]
use the reflection SetValue() method on the property to public string ProductName { get; set; }
set the value on the newly created instance of the Product
class to the value from the column in the table. Add the new Check for the [Column] Attribute When Reading Data
instance of the Product class to the collection and repeat Add some code to the BuildEntityList() to take advantage of
this process until all rows in the table have been processed. the [Column] attribute. Open the ProductRepository.cs file
and add a using statement at the top of the file.
Try It Out
Open the Program.cs file and modify the call to the Search() using System.ComponentModel.DataAnnotations
method to look like the following code. .Schema;
if (col == null) {
Let’s also add some additional functionality to check // Is column name in a [Column] attribute?
whether a [NotMapped] attribute has been added to any col = props.FirstOrDefault(
properties. Sometimes you need additional properties in c => c.GetCustomAttribute
your entity class, but those properties aren’t mapped to any <ColumnAttribute>()?.Name == columnName);
column in the table, this is what the [NotMapped] attribute }
is for. To illustrate the [NotMapped] attribute, open the if (col != null) {
Product.cs file and add the following property somewhere // Get the value from the table
within this class. var value = rdr[columnName];
// Assign value to the property if not null
if (!value.Equals(DBNull.Value)) {
[NotMapped] col.SetValue(entity, value, null);
public bool IsSelected { get; set; } }
}
Instead of gathering the properties of the class within the }
BuildEntityList() method, let’s move this functionality into
a new method. The new method uses reflection to collect
the column name and the property information for each
property in the entity class and return that as a collection
of objects. Create a class to hold that information by right ADVERTISERS INDEX
mouse-clicking on the Common folder and adding a new
class called ColumnMapper as shown in the code snippet
below. Advertisers Index
#nullable disable Apex
www.apexdatasolutions.com 63
using System.Reflection;
CODE Consulting
namespace AdoNetWrapper.Common; www.codemag.com/code 7
CODE Legacy Beach
public class ColumnMapper {
www.codemag.com/modernize 76
public string ColumnName { get; set; }
public PropertyInfo PropertyInfo { get; set; } DevIntersection
} www.devintersection.com 2
dtSearch
Open the ProductRepository.cs file and add a few
www.dtSearch.com 15
new properties to this class. You can see the collection Advertising Sales:
of ColumnMapper classes that are going to hold the LEAD Technologies Tammy Ferguson
832-717-4445 ext 26
information about each property in the Product entity www.leadtools.com 5 tammy@codemag.com
class. There are also properties for the schema and table
UAV
name of the table this repository class is working with. The
www.expouav.com 75
SQL property will hold the last statement used to retrieve
data from the table.
Listing 13: Add a method to build the SELECT SQL statement to submit to the database ColumnMapper object and set the PropertyInfo property to
protected virtual string the current PropertyInfo object, and the property name to
BuildSelectSql<TEntity>() { the ColumnName property.
Type typ = typeof(TEntity);
StringBuilder sb = new(2048); Next, check for a [Column] attribute on the property. If the
string comma = string.Empty;
[Column] attribute exists, make sure the Name property
// Build Column Mapping Collection has been filled in on that attribute. It’s possible to just
Columns = BuildColumnCollection<TEntity>(); specify a type for the column and not set the name for the
column when using the [Column] attribute. If the Name
// Set Table and Schema properties
SetTableAndSchemaName(typ);
property exists, replace the ColumnName property in the
ColumnMapper object. Finally, add the ColumnMapper
// Build the SELECT statement object to the collection to be returned, and repeat this
sb.Append(“SELECT”); process until all properties have been processed.
foreach (ColumnMapper item in Columns) {
// Add column
sb.Append($”{comma} [{item.ColumnName}]”); Add a Method to Set Table and Schema Properties
comma = “,”; To build the SELECT statement, you need the schema name
} and table name in the database you’re building the SQL
// Add ‘FROM schema.table’ for. By default, the SchemaName property is initialized to
sb.Append($” FROM {SchemaName}.{TableName}”);
“dbo,” as that’s the most common schema in SQL Server.
return sb.ToString(); The TableName property is set to the entity class name.
} However, most entity classes use the [Table] attribute to
specify the table name and optionally the schema name.
Create a method in your RepositoryBase class named
Add a constructor to the ProductRepository() class to call SetTableAndSchemaName and add the following code to
the Init() method. this method.
to submit to the database. Use a StringBuilder object to List<Product> list = SPONSORED SIDEBAR:
create the SELECT statement, so bring in the System.Text repo.Search<Product>(ConnectString);
namespace by adding a using statement at the top of the Get .NET 6 Help
ProductRepository class. Run the console application and you should see the same for Free
product objects displayed.
using System.Text; How does a FREE hour-long
CODE Consulting virtual
Add the BuildSelectSql() method to the repository class, Summary meeting with our expert
.NET consultants sound?
as shown in Listing 14. Call the BuildColumnCollection() In this article, you learned how to use .NET reflection to
Yes, FREE. No strings.
method to fill in the Columns property. Call the create a generic way to build a collection of entity objects No commitment. No credit
SetTableAndSchemaName() method to set the TableName from any table in a database. You learned how to use the cards. Nothing to buy.
and SchemaName properties. Build the SELECT statement [Column] and the [NotMapped] attributes to set the column For more information,
by iterating over the Columns collection and adding each name or ignore properties in your entity class. An abstract visit www.codemag.com/
ColumnName property to the SELECT statement. base class was illustrated from which you then provided consulting or email us at
a concrete implementation for accessing any SQL Server info@codemag.com.
Modify the Search() Method database. This same base class could then be used as the
Modify the Search() method as shown in Listing 14. Remove basis for any other database context classes you need to
the string sql parameter passed in. Add the call to the access Oracle, SQL Lite, or other databases that have a .NET
BuildSelectSql() method and place the resulting SELECT provider.
statement into the SQL property of this class. Pass the SQL
property to the CreateCommand() method. Then call the In the next article, you’re going to refactor the repository
BuildEntityList() method as you did before. class so it’s much more generic. You’ll also add search
capabilities to your repository class, retrieve a scalar value,
public virtual List<TEntity> Search<TEntity> work with multiple result sets, and call stored procedures.
(string connectString) {
List<TEntity> ret; Paul D. Sheriff
// Build SELECT statement
SQL = BuildSelectSql<TEntity>();
return ret;
}
Try It Out
Open the Program.cs file, remove the Sql declaration and
assignment, and remove it from the second parameter
passed to the Search() method.
©s
hu
tte
rs tock
/Buntoon Rodseng
The Span<T> and Memory<T> structs provide low-level public readonly ref struct Span<T> {
interfaces to an array, string, or any contiguous managed internal readonly
or unmanaged memory block. Their primary function is to ByReference<T> _pointer;
foster micro-optimization and write low-allocation code that private readonly int _length;
reduces managed memory allocations, thus decreasing the //Other members
strain on the garbage collector. They also allow for slicing or }
dealing with a section of an array, string, or memory block
without duplicating the original chunk of memory. Span<T> You can take a look at the complete source code of the struct
and Memory<T> are very beneficial in high-performance Span<T> here: https://github.com/dotnet/corefx/blob/
areas, such as the ASP.NET 6 request-processing pipelines. master/src/Common/src/CoreLib/System/Span.cs.
A Span type represents a contiguous chunk of memory Here’s how Span<T> is declared in the System namespace.
that resides in the managed heap, the stack, or even in
unmanaged memory. If you create an array of a primitive public readonly ref struct Span<T>
type, it’s allocated on the stack and doesn’t require garbage
collection to manage its lifetime. Span<T> is capable of To create an empty Span, you can use the Span.Empty
pointing to a chunk of memory allocated whether on the property:
stack or on the heap. However, because Span<T> is defined
as a ref struct, it should reside only on the stack. Span<char> span = Span<char>.Empty;
The following are the characteristics of Span<T> at a glance: The following code snippet shows how you can create a
byte array in the managed memory and then create a span
• Value type instance out of it.
• Low or zero overhead
• High performance var array = new byte[100];
• Provides memory and type safety var span = new Span<byte>(array);
int sum = 0;
foreach (int value in span)
sum += value;
Console.WriteLine
($”The sum of the numbers
in the array is {sum}”);
Marshal.FreeHGlobal(nativeMemory);
You can also allocate a Span in the stack memory using the
stackalloc keyword, as shown below:
byte data = 0;
Span<byte> span =
stackalloc byte[100];
int sum = 0;
foreach (int value in span)
Figure 1: Turn on unsafe compilation for your project to enable unsafe code. sum += value;
Console.WriteLine
($”The sum of the numbers
in the array is {sum}”);
You can now use the following code snippet to store integers When you execute the preceding code snippet, the integers
inside the memory pointed to by the Span and display the present in the sliced array will be displayed at the console,
sum of all the integers stored: as shown in Figure 2.
Although both Span<T> and Memory<T> represent a Here are some more advantages:
contiguous chunk of memory, unlike Span<T>, Memory<T>
is not a ref struct. So, contrary to Span<T>, you can have • They reduce the number of allocations for the garbage
Memory<T> anywhere on the managed heap. Hence, you collector. They also reduce the number of copies of
using HighPerformanceCodeDemo;
Benchmarking Performance using System.Runtime.InteropServices;
It’s time for some measurements. Let’s now benchmark the
performance of Span<T> struct versus the Substring method class Program
of the String class. {
static void Main(string[] args)
Create a New Console Application Project in {
Visual Studio 2022
Let’s create a console application project that you’ll use
for benchmarking performance. You can create a project in Listing 1: Setting up the benchmark data
Visual Studio 2022 in several ways. When you launch Visual
[MemoryDiagnoser]
Studio 2022, you’ll see the Start window. You can choose [Orderer(BenchmarkDotNet.Order.
Continue without code to launch the main screen of the SummaryOrderPolicy.FastestToSlowest)]
Visual Studio 2022 IDE. [RankColumn]
You’ll use this application in the subsequent sections of this Listing 2: The Substring and Span Methods
article. [Benchmark]
public void Substring()
Install NuGet Package(s) {
for(int i = 0; i < N; i++)
So far so good. The next step is to install the necessary {
NuGet Package(s). To install the required packages into your var data = countries.Substring
project, right-click on the solution and the select Manage (index + 1,
numberOfCharactersToExtract - 1);
NuGet Packages for Solution.... Now search for the package }
named BenchmarkDotNet in the search box and install it. }
Alternatively, you can type the commands shown below at
the NuGet Package Manager Command Prompt: [Benchmark(Baseline = true)]
public void Span()
{
PM> Install-Package BenchmarkDotNet for(int i=0; i < N; i++)
{
var data = countries.AsSpan().
Slice(index + 1,
Benchmarking Span<T> Performance numberOfCharactersToExtract - 1);
Let’s now examine how to benchmark the performance of }
Substring and Slice methods. Create a new class named }
Limitations of Span<T>
Span<T> is stack-only, which means it’s inappropriate for
storing references to buffers on the heap, as in routines
performing asynchronous calls. It’s not allocated in the
managed heap but on the stack and it doesn’t support boxing
to prevent promotion to the managed heap. You can’t use
Span<T> as a generic type but you can use it as a field type
in a ref struct. You can’t assign Span<T> to variables of type
dynamic, object, or any other interface type. You can’t use
Span<T> as fields in a reference type, nor can you use it across
await and yield boundaries. Additionally, because Span<T>
doesn’t inherit IEnumerable, you can’t use LINQ with it.
Controllers
C is for Controllers in the acronym MVC. The main purpose Facades make it easy to group
of a controller is to handle an incoming request and conse- together and access features
quently return a proper response. Often forgotten (or hid-
den) is the Routing mechanism in MVC applications. It’s the from a single endpoint.
router engine that decides which controller will handle a
new incoming request.
Resource Controllers
A Laravel project ships with routing files representing routes In Laravel, Resource controllers stem from the general REST
for different aspects of the application. Two important route concepts. REST APIs are typically based on HTTP methods Bilal Haidar
types exist in Laravel: (GET, POST, PUT, etc.) to access a resource via a URL and the bhaidar@gmail.com
use of JSON or XML to transmit data. https://www.bhaidar.dev
• Web routes: Web routes serve the public and protect- @bhaidar
ed routes of an application. In Laravel, you can think of a Model as a Resource. You want
Bilal Haidar is an
• API routes: API routes serve the public and protected to create a controller to handle model creation, deletion, accomplished author,
routes of an API application in Laravel. Yes! Not only reading, updating, etc. You want to group all those model- Microsoft MVP of 10 years,
can you build a back-end application with PHP Laravel, related functions into a single controller class. ASP.NET Insider, and has
but you can also build a REST API in Laravel. You can been writing for CODE
read a complete guide on how to build a RESTful API in Laravel can generate a Resource Controller for a single Magazine since 2007.
Laravel here: https://www.toptal.com/laravel/restful- model. It scaffolds a set of controller actions to manage
laravel-api-tutorial. this model. With 15 years of extensive
experience in Web develop-
You aren’t limited to these built-in route types. You can even Let’s create your first resource controller by making use of ment, Bilal is an expert in
define your own. For instance, let’s say that you want to the make:controller Artisan command: providing enterprise Web
build a section of an application that only admin(s) users solutions.
can access. Something like an Admin Panel for your main sail artisan make:controller \
He works at Consolidated
application. You can create a new route file and put all ad- PostController \
Contractors Company in
min routes in that file. Here’s a complete guide on creating --resource \
Athens, Greece as a full-
custom route files in Laravel: https://onlinewebtutorblog. --requests \
stack senior developer.
com/how-to-create-custom-route-file-in-laravel-8/. --model=Post
Bilal offers technical
When you create a new Laravel application, you’ll have a Or consultancy for a variety
single route defined inside the routes/web.php file. of technologies including
php artisan make:controller \ Nest JS, Angular, Vue JS,
<?php PostController \ JavaScript and TypeScript.
use Illuminate\Support\Facades\Route; --resource \
Route::get(‘/’, function () { --requests \
return view(‘welcome’); --model=Post
});
Let’s understand the command sections:
This basic route in Laravel defines a request handler, in the
form of closure (function), for accessing the root path of the • Specify the --resource flag to instruct Laravel to scaf-
application. The closure simply returns a view named welcome. fold a Resource Controller.
• Specify the --requests flag to instruct Laravel to gener-
Views will be discussed in the next section of the article. ate Form Requests for the store() and update() actions.
For now, it’s enough to understand that accessing the root • Specify the --model option to specify the resource/
path of the application returns an HTML page that’s defined model you want to create the resource controller for.
inside the welcome route.
Table 1: Resource routes and details To view the routes generated, run the following command:
Or
Ignore the first two lines. Just focus on the routes that are
now defined for the Post resource/model.
• GET /posts: The PostController->index() action meth- Form Request promotes encapsulation and reusability all the way!
od handles this request.
• POST /posts: The PostController->store() action Controller Middleware
method handles this request. Middleware in Laravel provides a convenient mechanism for
• DELETE /posts/1: The PostController->destroy() ac- inspecting and filtering HTTP requests entering your appli-
tion method handles this request. cation. For instance, you can define a middleware to verify
that the user is authenticated before accessing a controller.
In addition to creating resource controllers in Laravel, you You can read the full documentation on Laravel Middlewares
can also create normal controller classes and invokable con- here: https://laravel.com/docs/9.x/middleware.
trollers.
Laravel ships with a few middleware out of the box. You
Form Requests aren’t limited to these and can create your own.
Laravel provides a validation library to help you validate
all incoming requests. There are multiple ways to use this For example, you can specify that only authenticated users
library. You can read more about the Laravel validation li- can access and use the PostController. You can specify the
brary here: https://laravel.com/docs/9.x/validation. auth middleware inside the controller itself (https://lara-
vel.com/docs/5.8/authentication), as shown in Listing 4.
One way of using the validation library in Laravel is to make
use of Form Requests. Let’s revisit the store() controller
action method. Listing 2: StorePostRequest form request class
<?php
public function store(StorePostRequest $request)
{ } namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
The Form Request for this action method is named Store-
PostRequest. It’s a class defined inside the path /app/ class StorePostRequest extends FormRequest
HTTP/Requests folder. {
/**
* Determine if the user is authorized to
Listing 2 shows the entire source code of this class. * make this request.
*
You can always ignore the authorize() method and return * @return bool
true instead of false value. */
public function authorize()
{
The rules() method lets you define the validation rules that return false;
apply when creating a new Post model. }
To update a Post record, you execute a PUT/PATCH /posts/ Laravel documentation contains tons more information,
{post} request. Typically, the {post} route segment is re- such as Laravel Controllers, Service Providers, and Service
placed by the Post ID field. Container.
Now that you know how to create a controller and how a Let’s run the application and see the view returned inside the
controller handles requests, it’s time to cover the last letter browser. Figure 2 shows the app running inside the browser.
of MVC, which in this case, is the letter V, representing Views
or HTML pages in your application. The welcome.blade.php is just an HTML page with PHP snip-
pets here and there. The Blade engine has rich components
and directives that you can review here: https://laravel.
Views com/docs/9.x/blade#components and here: https://lara-
Views represent the User Interface (UI) of an MVC applica- vel.com/docs/9.x/blade#blade-directives.
tion. Typically, they render as HTML/CSS pages for browsers.
Layout Template
Let’s revisit the routes/web.php file and study the first Most of the applications you build have a general theme
route added by Laravel. across all the pages. For instance, you might have a naviga-
tion bar at the top, a sidebar, and a footer. The main content
<?php area is dynamic and changes from one page to another. In-
use Illuminate\Support\Facades\Route; stead of repeating the same layout over and over each page,
Route::get(‘/’, function () { you put all the common components inside one file and then
return view(‘welcome’); reuse them across the rest of the pages in the application.
});
The Blade Engine allows building Layouts (https://laravel.
This route maps any request to the root path of the applica- com/docs/9.x/blade#building-layouts). A Layout template
tion and, in response, returns the View named welcome. holds all the common sections and defines placeholders to
Laravel defines and stores all views inside the /resources/ be later populated by the View extending this Layout View.
views folder. Locate this folder and notice that this folder
currently contains a single View, the welcome.blade.php Create a new blade file named /resources/views/Layout.
file. blade.php. I’ve created a GitHub Gist (https://is.gd/09RS9m)
that you can use to copy/paste the source code from.
Laravel makes use of a simple, yet powerful templating engine
named Blade (https://laravel.com/docs/9.x/blade). Unlike The template is a basic HTML5 page with two placeholders:
other PHP templating engines, Blade templates are all com-
piled into plain PHP code and cached until they are modified. • Title
• Content
Think of Blade as the ASP.NET Razor (https://www.w3schools.
com/asp/razor_intro.asp), or Django DTL or Jinja 2 (https:// The Views that extend this Layout template can substitute
docs.djangoproject.com/en/4.0/topics/templates/). values for both the title and content.
Notice how the Layout template is embedding the JavaScript The action method is straightforward. First, you query for
and CSS files. This is handled by Laravel Mix (https://lara- all Post records in the database. Then, you use the view()
vel.com/docs/9.x/mix#main-content). helper function to return the Index View with some data to
render inside the view. In case you are using the Laravel
Now let’s create the applications views to handle creating, standard way of defining views, then the view name is noth-
reading, updating, and destroying of Post records. ing but a concatenation of the folder name and the first part
of the view name with a dot in between. In case you’re nest-
Index View ing your view in a more deep and nested folder structure,
Let’s start by defining the Index View. This view displays all you’ve got to include all the subfolders in the path.
Post records that are stored in the database. On this view,
you will be able to create a new Post, edit existing ones, Let’s now highlight the important sections of the Index
and of course, destroy one. Figure 3 shows the view up and View.
running.
I want this view to extend the Layout template. I do this by
Create a new Blade view and store it under the path /re- adding the following statement:
sources/views/post/index.blade.php. I’ve created a
GitHub Gist (https://is.gd/nIlHKi) that you can use to copy/ @extends(‘layout’)
paste the source code from.
Then, I want to provide a value for the Title section that
Back in Table 1, I compiled the list of all Post routes and I’ve defined in the Layout template. I do this by adding the
actions. In order to access this view, you need to implement following statement:
the PostController index() view.
@section(‘title’, ‘All Posts’)
Switch to the PostController and write the following:
To render this view inside the Content section of the Layout
public function index() template, I wrap all my HTML markup inside the following
{ statement:
$posts = Post::all();
return view(‘post.index’, compact(‘posts’)); @section('content')
} @endsection
The route() function takes the route name as input. Table 1 con- The form posts to the route named posts.store. It submits a
tains all route information that you’ve created for Post Model. POST request to the endpoint /posts with the Post fields in
the payload of the request.
When the view renders in the browser, the helper function
will be replaced with this: The form uses the @csrf Blade directive. Laravel offers a
layer of protection against cross-site request forgery. You
<a href=”/posts/create”>Create Post</a> need to always embed this directive when using forms in
Blade. You can read more about CSRF Protection in Laravel
The next thing to cover is looping over the Post record that here: https://laravel.com/docs/9.x/csrf#main-content.
was retrieved from the database and sent down by the in-
dex() action method. I’m using the old() helper function to preserve the value
that the user enters. For instance, let’s say that I fill out
@foreach($posts as $post) the form and leave a required field empty. When the form
<tr> is submitted, Laravel validates the input fields. If any field
<td>{{ $post->title }}</td> is not valid, Laravel redirects to the same view. Hence, the
<td>{{ $post->slug }}</td> page refreshes in its place. The old() function comes in
<button type=”submit”>Save</button> Let’s switch back to the Index View and see how and where
</form> the success message is displayed.
@if (session(‘success’))
Listing 6: Render error messages <div
class=”alert alert-success mb-2”
@if ($errors->any()) role=”alert”
<div class=”alert alert-danger mb-4” role=”alert”>
<ul> >
@foreach ($errors->all() as $error) {{ session(‘success’) }}
<li>{{ $error }}</li> </div>
@endforeach
</ul> @endif
</div>
@endif This is the section that renders the success message and is
placed above the table. Figure 5 shows where the message
renders.
handy in preserving whatever I’ve typed before submitting
the form. In case validation fails, Laravel renders the same view with
an additional piece of data, named $errors, representing a
The PostController create() action method handles render- collection of errors resulting from the validation. To render
ing this view. those errors, you need to add this section on top of the
Create Post form. Listing 6 shows the entire code for this
public function create() section.
{
return view(‘post.create’); The code loops over all error messages and displays them
} inside an unordered list.
The PostController store() action method handles the form Edit Post View
submission. Create a new Blade view and store it under the path /resourc-
es/views/post/edit.blade.php. I’ve created a GitHub Gist
public function store(StorePostRequest $request) (https://is.gd/fkEGEa) that you can use to copy/paste the
{ source code from. Figure 6 shows the view up and running.
$post = Post::create([
...$request->validated(), This view is similar to the Create View. In this case, the view
‘user_id’ => 1 renders an existing Post record in edit mode.
]);
return to_route(‘posts.index’) To reach this view, you need to adjust the Index View and
->with( wrap the edit icon with a hyperlink to the Edit View URL.
‘success’,
‘Post is successfully saved’ <a href=”{{ route(‘posts.edit’, $post->id) }}”>
); </a>
}
The posts.edit route name corresponds to /posts/{post-
Notice how the store() action method is making use of id}/edit/. This route corresponds to the PostController
Laravel Implicit Route Binding to receive a Post object in- edit() action method.
stead of Post ID.
public function edit(Post $post)
The store() method has the StorePostRequest Form Request {
as a single input parameter. This class handles the valida- return view('post.edit', compact('post'));
tion of the incoming request. It then provides the store() }
method with valid data that is accessible via the $request-
validated() method. Notice how the edit() action method is making use of Lara-
vel Implicit Route Binding to receive a Post object instead
I’ve created a GitHub Gist (https://is.gd/UOY79V) that you of Post ID. The method returns the Edit View together with
can use to copy/paste the source code from. the $post variable containing the Post object.
The code uses the Post::create() method that represents the The view binds the Post properties to the various HTML con-
mass assignment in Eloquent, as discussed in the Implicit Route trols, as you’ve seen in the Create Post View section.
When the Post record is created or updated, the static clo- Laravel application: https://tailwindcss.com/docs/guides/
sure runs and updates the Post slug field. Laravel defines laravel.
many Model events that you can use as per the scenario at
hand. I’ve borrowed the HTML markup for my Views from the fol-
lowing resource: https://larainfo.com/blogs/tailwind-css-
You could have defined the same code block on the Post simple-post-crud-ui-example. They’ve got many posts and
Model. However, I find it cleaner to use the provider for such material on using Tailwind CSS in Laravel.
tasks. In Laravel, Service providers are the central place of all
Laravel application bootstrapping. It defines two methods: In this article, I covered using Laravel Blade as part of
building MVC applications in Laravel. However, Laravel al-
• register() lows you to use other client-side rendering engines and isn’t
• boot() limited to Blade only.
You use the register() method when you want to register For instance, you can build a Laravel application for the
classes with the Laravel Service Container. Right after run- back-end with React JS, Vue JS, Svelte JS, Livewire, and In-
ning all register() methods in the application, Laravel runs ertia JS for the front-end.
the boot() method. At this stage, you are sure that all class-
es have been registered inside the Service container and it’s Now that you know how PHP Laravel implements the MVC
safe to access. architecture, you can start building your own applications
with Laravel. What you have seen here is just the tip of the
You can read more about Laravel Service providers here: iceberg. My plan is to use this article and the application
https://laravel.com/docs/9.x/providers. that we have built together as a basis to build and add more
Laravel features very soon.
Summary
I’ve been using Tailwind CSS throughout the different views Stay tuned to discover more with PHP Laravel.
that I’ve implemented. If you’re interested in using Tailwind
CSS in Laravel, I advise checking this simple guide that will Bilal Haidar
teach you how to install and configure Tailwind CSS in your
TypeScript: An Introduction
Writing JavaScript can be difficult. The speed and simplicity of development using a weakly typed language like JavaScript has
its merits, but, as whole books have attested, JavaScript isn’t a perfect language. Yet JavaScript powers so much of the code we
write these days, whether it’s server-side (node or deno), browsers, or even application development. As JavaScript projects
grow, the weak typing can get in your way. Let’s talk about To do this, download the installer at https://shawnl.ink/
TypeScript as a solution to some of the limitations of Ja- install-node.
vaScript.
Once you have Node/NPM installed, you can install the com-
I expect that there are three types of developers reading piler (globally in this case) with NPM:
this article. First, you JavaScript developers who want to
understand how TypeScript works: Welcome to the article. > npm install typescript -g
Second, for you developers from other languages that are
suspicious of JavaScript and wonder if TypeScript fixes ev- Now that TypeScript is installed, you can create your first
Shawn Wildermuth erything in JavaScript: I’m glad you’re here! For the third TypeScript file by naming it as {filename}.ts. The compiler
shawn@wildermuth.com type of developer who already uses TypeScript and wants command is tsc (TypeScript Compiler). To compile your first
wildermuth.com to find a mistake in this article: Please correct me if I get TypeScript file, call the compiler with the file name:
twitter.com/shawnwildermut anything wrong.
> tsc index.ts
Shawn Wildermuth has
been tinkering with com- Before You Start If the compilation is successful, it creates a new file called
puters and software since
he got a Vic-20 back in the Although it can be helpful to use the TypeScript playground index.js. For common editors, TypeScript is often enabled
early ’80s. As a Microsoft (https://shawnl.ink/ts-playground), you’ll likely want to set by default. For example, in Visual Studio Code, you’ll get
MVP since 2003, he’s also up your computer and code editors for TypeScript. Although IntelliSense and error checking without any extensions, as
involved with Microsoft many of you will use TypeScript in other frameworks and seen in Figure 1 and Figure 2.
as an ASP.NET Insider and libraries (such as Angular, Vue, and React), you might be
ClientDev Insider. He’s interested in learning how to use TypeScript directly. To get Visual Studio works in a similar way. By default, TypeScript
the author of over twenty started, TypeScript requires Node and NPM to be installed. is enabled, as seen in Figure 3.
Pluralsight courses, written
eight books, an interna- No extensions are required. It just works. The default be-
tional conference speaker, havior in Visual Studio is to compile TypeScript when you
and one of the Wilder Minds. compile your project. It can be overzealous and try to com-
You can reach him at his pile every TypeScript file in any libraries you’re using (e.g.,
blog at http://wildermuth. NPM’s node_modules). You may not be using Visual Studio
com. He’s also making to compile your TypeScript (e.g., you’re using the Type-
his first, feature-length Script compiler to turn your Type-
documentary about
Script into JavaScript on the
software developers
today called “Hello World:
The Film.” You can see
more about it at http://
helloworldfilm.com.
<Project Sdk="...">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework
<Nullable>enable</Nullable> Figure 1: Error checking in Visual Studio code
<TypeScriptCompileBlocked>
true
</TypeScriptCompileBlocked>
...
</PropertyGroup>
...
</Project>
Now that you’re set up to use TypeScript, let’s dive into lan-
guage.
let a = "Shawn";
console.log(typeof a); // string
a = 1;
console.log(typeof a); // number
function writeName(name) {
// Could be empty, null, or undefined When you’re writing a small project, the benefits are likely to
if (name) { be small. But when you’re creating a large-scale JavaScript
console.log(name); project, this additional type checking can provide enormous
} benefits.
}
Instead, TypeScript is all about making JavaScript more scal- Use the typed functionaddToCollection("one");
able. Although creating languages that compile down to
JavaScript isn’t a new idea, TypeScript is different because addToCollection("two");
JavaScript is valid TypeScript. Because TypeScript is a super- addToCollection(3); // TypeScript Error
set of JavaScript, it only adds new features to JavaScript to
make it easier to manage large projects. Let’s see an example: Although the type safety functionality in TypeScript is in
the source file, none of it translates into the JavaScript. So
const element = why does it matter? The TypeScript benefits are about doing
document.getElementById("someObject"); type-checking at compilation time without trying to make
JavaScript into a strongly typed language. This is why it’s
element.addEventListener("click", so beneficial to large projects where the level of complexity
function () { can benefit from some level of compiler checking against
element.classList.toggle("selected"); the assumptions in your code.
});
Another benefit of TypeScript is that you can write your code
This is valid TypeScript. What does it output? without thinking about the JavaScript environment. Let’s
start with another small piece of TypeScript:
"use strict";
const element = // TypeScript
document.getElementById("someObject");
class Animal {
element.addEventListener("click", name = "";
function () { age = 0;
element.classList.toggle("selected"); fed = false;
});
feed(food: string) {
Do you see the difference? Only the “use strict” is added. this.fed = true;
This simple piece of JavaScript is completely valid Type- }
Script. That’s the magic of the language. You can opt into as }
little or as much of the benefits of TypeScript as you want.
Let’s look at another example, this time, using type infor- In this case, you’re creating a class, which isn’t supported
mation in the TypeScript: in older versions of JavaScript. When you build TypeScript,
you can specify the target JavaScript level. For example, if
// TypeScript you build it for ECMAScript 2015, you’re using that version
of JavaScript classes:
// The collection
const theList = new Array<string>(); // ECMAScript 2015
3 theName = 1;
~~~~~~~
Found 1 error in index.ts:3
In most cases, you only need to specify the type if you‘re Figure 4:
not assigning an initial value. This type information can be Inferring
useful in functions too: array types
In this case, any type passed in as a message would work. // Using an ellipse for consiseness
But there are times when you want to constrain the types. function formatName(...) : void {
If you want to only allow numbers and strings, you might
write complex code to test the datatype with an any type, Using types for parameter types and return values should
but instead you could use union types. These are types that expose whether you’re using functions incorrectly.
have a pattern matching as the type checking. These types
are separated by the or operator (i.e, |): Now that I’ve talked about types in general, let’s talk about
defining your own types.
function write(message: string | number) {
console.log(message); Type Definitions
} The first way to define a type is with the type keyword. Here,
you can create a named type with a definition of what you ex-
write("Foo"); pect. For example, you could define a type for the Person type:
write(1);
write(true); // fails type Person = {
firstName: string,
Unlike other statically typed languages, this allows you to lastName: string,
What Isn’t TypeScript specify patterns for types that may not fit into the limita- age: number
tions of strongly typing. };
I think it’s important to
understand what TypeScript
is not. Although TypeScript Functions Once a type is defined, you can use it to type to define vari-
is a compiler to convert Because Functions are a central part of any JavaScript proj- ables:
code to JavaScript, that’s ect, you won’t be surprised that functions can contain type
the end of its responsibility. checking too. Let’s start with a simple JavaScript function: let people: Person[];
That means it doesn’t bundle
your projects, or do code function formatName(firstName, lastName) { This defines the variable people as accepting an array of
splitting, or anything that if (!firstName || !lastName) return null; type Person, but it only defines the type, not initialization.
bundlers (such as WebPack, return `${lastName}, ${firstName}`; For example, if you wanted to initialize it, you’d need to
Rollup, etc.) do. Unless you’re } assign a value:
using a bundler’s plug-in for
TypeScript, TypeScript is about let fullName = formatName("Shawn", "Wildermuth"); let people: Person[] = [];
generating JavaScript.
If you define this in TypeScript, it infers that the firstName Alternatively, you can use a generic declaration (much like
and lastName are any objects and that the function returns you might know from other languages):
a string. Although this is mostly correct, it’s not exactly cor-
rect. Let’s type those parameters: let people = new Array<Person>();
function formatName(firstName: string, In any of these cases, you’ll see that the TypeScript compiler
lastName: string) { is going to type check that the shape of the Person is as-
if (!firstName || !lastName) return null; signed to this array:
return `${lastName}, ${firstName}`;
} // Fails as this shape isn't the same as Person
people.push({
let fullName = formatName("Shawn", "Wildermuth"); fullName: "Shawn Wildermuth",
age: 53
That helps confirm that you have to send in strings for the });
names. But you’re still inferring the return type. Let’s add
the return type: // This Works
people.push({
function formatName(firstName: string, firstName: "Shawn",
lastName: string) : string { lastName: "Wildermuth",
if (!firstName || !lastName) return null; age: 53
return `${lastName}, ${firstName}`; });
}
Using type definitions simply defines the shape that’s re-
Note that the return type isn’t before the function but after quired. Note that you can still use an anonymous object
the function and after a colon. Although this return isn’t here, as long as it matches the type pattern.
needed (it’s inferred), you might know more than the Type-
Script inference. In this case, you know that you can return Let’s look at some other ways to define types.
a null or a string, so you can use a union type:
Classes
// Using an ellipse for consiseness If you’re coming from most major languages, you’re used
function formatName(...) : string | null { to using classes as the central part of your development
Type Libraries
One of the benefits of TypeScript that may not be obvious at
first is to consume something called Type Libraries. When
you write TypeScript, it’s checking for self-confirming type
information. In other words, your classes, functions, and
types will be checked within your project. What about other
frameworks or libraries that you’re using? If you’re working
within an NPM-enabled project, you might have some librar-
ies. For example, the moment* library could be added to
your project like so:
When you use the library, how does TypeScript help there?
Many of these libraries have added type information in
the form of Type Libraries (typically {projectName}.g.ts).
In the moment project, it contains a moment.g.ts. This
means that when you use moment in your own project, it
knows about the interface and types from this library (even
though it’s JavaScript). Most editors (Visual Studio Code,
tool called Grafana. In this article, I’ll walk you through how There, you’ll find the instructions to download Grafana for
to get started with Grafana, and how you can use it to build the OS that you’re running.
interesting dashboards. I’ll also describe two projects that
you can build using Grafana: For Windows, you can download the Windows installer
(https://dl.grafana.com/enterprise/release/grafana-enter-
• How to create dynamic auto-updated time series prise-8.5.2.windows-amd64.msi) and then run it.
charts
• How to display real-time sensor data using MQTT For Mac, you can download and install Grafana directly us-
ing Terminal:
Wei-Meng Lee
weimenglee@learn2develop.net
Installing Grafana $ curl -O
http://www.learn2develop.net The first good news about Grafana is that it’s free to use for https://dl.grafana.com/enterprise/release/grafana-
@weimenglee most use cases, except where you need enterprise features enterprise-8.5.2.darwin-amd64.tar.gz
such as advanced security, reporting, etc. If you want to
Wei-Meng Lee is a technolo-
gist and founder of Devel- modify the source code of Grafana, be sure to check the $ tar -zxvf grafana-enterprise-8.5.2.darwin-
oper Learning Solutions licensing info at https://grafana.com/licensing/. amd64.tar.gz
(www.learn2develop.net),
a technology company spe- To download the free enterprise version of Grafana, go to Once the above steps are done, you should now find a folder
cializing in hands-on train- https://grafana.com/grafana/download (see Figure 1). named Grafana-8.5.2 in your home directory. On Windows,
ing on the latest technolo-
gies. Wei-Meng has many
years of training experiences
and his training courses
place special emphasis
on the learning-by-doing
approach. His hands-on
approach to learning
programming makes
understanding the subject
much easier than read-
ing books, tutorials, and
documentation. His name
regularly appears in online
and print publications such
as DevX.com, MobiForge.
com, and CODE Magazine.
Logging in to Grafana
Once Grafana has been installed on your computer, open it
using a Web browser using the following URL: http://local-
host:3000/login (see Figure 2).
In the Table Data Import wizard that appears, enter the path
of the CSV file that you’ve downloaded and follow the steps.
Specify that the data of the CSV file be loaded onto the In-
surance database and you also have the option to name the
table. If you take the default options, the content of the
CSV file is loaded onto a table named Insurance. You should
now be able to see the imported table and its content (see
Figure 5). Figure 9: Configure the MySQL data source to connect to the Insurance database.
Type in MySQL and then click on the MySQL data source that
appears (see Figure 8).
In Grafana, a dashboard is a set of
Enter the details of the MySQL connection as shown in Figure 9.
one or more panels organized and
On the bottom of the page, click Save & test to ensure that the arranged into one or more rows.
connection to the MySQL database server is working correctly. Figure 10: Add a new dashboard.
Creating Panels
With the dashboard created, you’re now ready to add a pan-
el to your dashboard.
Click the Add Panel icon and then the Add a new panel
button to add a panel to your dashboard (see Figure 12).
SELECT
now() as time,
count(region) AS value,
Figure 12: Add a new panel to the dashboard. region as metric
FROM Insurance
GROUP BY region
In addition to the pie chart, you can also display bar chart
(see Figure 15).
You can also display the data as Stat (statistics; see Figure 16).
When you’re done with the panel, clicking on the Apply but-
ton at the top of the page returns you to the dashboard.
Your dashboard now has one panel (see Figure 18).
From this point, you can add additional panels to your dash-
board.
tz = timezone.utc)
In the real-world, your stock
df = pd.DataFrame(
data will more likely come
{
from databases that 'value':np.random.uniform(
are updated dynamically. 2500,3200,len(days))
}, index = days)
display(df)
For this article, I’ll pre-generate the prices for four days, df.to_csv('stocks_goog.csv')
from one day before the current day, to three days after
the current day. First, generate the simulated stock prices The simulated stock price is saved as stocks_goog.csv. Next,
for GOOG: generate the prices for AAPL:
from datetime import timedelta, timezone from datetime import timedelta, timezone
import datetime import datetime
import numpy as np import numpy as np
import pandas as pddate_today = \ import pandas as pddate_today = \
datetime.datetime.now() datetime.datetime.now()
Before you can run the REST API, you need to install
the grafana-pandas-datasource module:
$ python demo.py
[
{
"datapoints": [
[
3169.441653300435,
1647486052594
],
[
2748.501265212758,
1647486057594 Figure 23: Configure the SimpleJson data source.
],
[
3195.3559754568632, [
1647486062594 3098.521949302881,
], 1647486347594
... ]
[ ],
2744.0582066482057, "target": "value"
1647486342594 }
], ]
You can control how often you want Grafana to fetch the
data for you by selecting the date range (see Figure 26).
{
'$gt': datetime.datetime(2022, 3, 17, 5, 48,
18, 555000,
tzinfo=<UTC>),
'$lte': datetime.datetime(2022, 3, 17, 5, 53,
18, 555000,
tzinfo=<UTC>)
}
The chart will be updated with the data for the last five min-
Figure 24: Add a new panel to the dashboard. utes (see Figure 27).
Configuring a Variable
You can configure a variable in Grafana for your dashboard to
allow users to display the chart for different stock symbols.
Click the Variables section on the left and then click Add
variable (see Figure 30).
Figure 27: Display the data fetched from the REST API.
Back in the dashboard, select Edit for the Panel (see Figure 32).
Click Apply. In the textbox next to the stock variable, enter AAPL.
You should now see the chart for AAPL (see Figure 34).
• Node.js
• yarn
• Go
• Mage
Don’t worry if you’re not familiar with these tools and language.
I’ll show you how to install them in the following sections.
Install Node.js
Download and install Node.js from https://nodejs.org/
en/download/. Once Node.js is installed on your system,
type the following command in Command Prompt to in-
stall yarn:
Figure 30: Add a variable to the current dashboard. npm install —g yarn
Figure 31: Configure the variable to be added to the dashboard. Figure 32: Editing the panel
Download Mage
Go to the source of Mage at https://github.com/magefile/
mage. Click on the Code button and select Download ZIP
(see Figure 38).
go env
You can locate the path for GOPATH from the output:
...
set GOOS=windows
set GOPATH=C:\Users\Wei-Meng Lee\go Figure 33: Using the newly added variable in the panel
set GOPRIVATE=
...
cd C:\Users\Wei-Meng Lee\Desktop\mage-master
go run bootstrap.go
{
“name”: “grafana-mqtt-datasource”,
Figure 36: Select how often you want the chart to refresh. “version”: “0.0.1-dev”,
“description”: “MQTT Datasource Plugin”,
“scripts”: { “build”: “del /F /Q dist && grafana-
toolkit plugin:build && mage build:backend”,
...
cd C:\Users\Wei-Meng Lee\Desktop\mqtt-datasource-main
yarn build
yarn install
[plugins]
Figure 38: Download the source code for Mage. enable_alpha = false
app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers
to identify plugins to load even if they are
unsigned. Plugins with modified signatures are never
loaded.
allow_loading_unsigned_plugins = grafana-mqtt-
datasource
MQTTBROKER = 'test.mosquitto.org'
PORT = 1883
TOPIC = "home/temp/room1/storeroom"
mqttc = mqtt.Client("python_pub") Figure 40: Add the MQTT data source to Grafana.
mqttc.connect(MQTTBROKER, PORT)
while True:
MESSAGE = str(np.random.uniform(20,30))
mqttc.publish(TOPIC, MESSAGE)
print("Published to " + MQTTBROKER + ': ' +
TOPIC + ':' + MESSAGE)
time.sleep(3)
• Host: test.mosquitto.org
• Port: 1883
SPONSORED SIDEBAR:
Need FREE
Project Advice?
CODE Can Help!
No strings free advice on
a new or existing software
development projects.
CODE Consulting experts
have experience in cloud,
Web, desktop, mobile,
microservices, containers,
and DevOps projects.
Schedule your free hour
of CODE call with our
expert consultants today.
For more information
visit www.codemag.com/
consulting or email us at
info@codemag.com.
Figure 44: Display the data from MQTT as a line chart.
Next, create a new Dashboard and add a new Panel (see Summary
Figure 42): In this article, you’ve seen how to use Grafana to build
dashboards with a minimal amount of code needed. Instead
Configure the panel with the following (see also Figure 43): of code, all you need is some basic SQL skills (of course,
there will be cases where you need to write complex SQL
• Visualization: Time Series queries to extract your data). In addition, I’ve walked you
• Data source: MQTT through two projects where you learned how to dynamically
• Topic: home/temp/room1/storeroom update your chart from a REST API, as well as fetch data
from a MQTT data source. Have fun!
Click Apply when done.
Wei-Meng Lee
You should now see the chart plotted and updated every
three seconds (see Figure 44). If you can see the chart,
this means that you’re able to receive the data through the
MQTT data source.
language of Excel, but with a twist: Under the hood of this guage in which LET() and LAMBDA() are available, VSA code
uniquely powerful language is the full reach and power of can interact with the outside world from both directions:
the .NET framework. the inside out and the outside in. A standalone scripting en-
gine at the command line for general-purpose programming
VSA is implemented on top of .NET and woven directly into accompanies the add-in and comes with a plethora of useful
the fabric of the storied Office suite via the Accelerate for Scheme and .NET wrapper libraries for this very purpose.
Microsoft 365 add-in. Initial focus is on Excel, but eventu-
ally all the applications that are part of the suite will benefit In this article, I’ll walk you through the rationale for mak-
from its core value proposition. The add-in supports in-ap- ing Scheme on .NET a first-class extension/automation lan-
Bob Calco plication scripting in VSA via a proper REPL (see Figure 1), guage of the Office suite by way of a hands-on primer. First,
Bob.Calco@apexdatasolutions.com available from within the host Office application (and acces- I’ll cover some basic facts about Scheme as I ease into re-
@BobCalco sible at the command line as well for external processing). viewing VSA as a first-class language for user-defined func-
tion (UDF) development in Excel. Then, I’ll explore some of
Bob is a seasoned enter- the more powerful techniques around wrapping .NET librar-
In the case of Excel, an additional pretty-printing, syntax-
prise architect and a proven
highlighting Lambda Editor (see Figure 2) makes writing ies for use in VSA-powered Office solutions. Finally, I’ll take
software innovator with
user-defined functions (UDFs) in Scheme both easy and a step back and cover syntax extensions, perhaps the most
over 20 years of experience
designing, building, and convenient. mystifying feature of the language, and how to interact with
leading teams to deliver them in the built-in and command-line REPL.
large-scale, enterprise In this latter capacity within Excel, Visual Scheme for Appli-
cations compliments Microsoft’s recent addition of the LET()
solutions in the healthcare,
financial services, and retail and LAMBDA() functions to their formula expression lan- Why Scheme?
sectors. For the past five guage by providing a flavor of the same functional program- At first glance, Scheme might seem an odd choice of a lan-
years, Bob has focused ming approach to creating UDFs, but with an external enter- guage to tag team with VBA and Excel’s formula expression
on data sharing and prise integration focus. Unlike the formula expression lan- language. On closer inspection, and especially considering
interoperability both
within and between large,
distributed enterprise sys-
tems. Bob was the primary
visionary and architect for
the VistA.js Platform within
the VA, a Class 1 enterprise
microservices framework
that enabled data fed-
eration and reconciliation
across the VA’s 154 medical
centers. Currently, he leads
the Apex Team to address
current industry challenges
associated with Provider
Data Management/Provider
Directories.
Figure 1: The REPL is where you test out ideas before you commit them to code.
Microsoft's new, explicitly “functional programming” di- Many top-flight universities teach Scheme to first-year
rection with Excel’s formula expression language, Scheme computer science majors with no previous background in
seems the inevitable choice. programming, because anyone can grasp its basic syntax
in just a few hours. It’s very easy to learn (and to teach),
with an approachable and consistent core syntax based on
Lisp S-expressions that’s both very succinct and surprisingly
The choice of providing a Lisp malleable. Mastering Scheme is a bit like mastering chess,
language is a conscious decision with the exception that even a complete beginner can do
amazing things in Scheme right away.
to put the programmer in control
of the language’s features and The Scheme that Accelerate for Microsoft 365 integrates
into Office goes by the name “Visual Scheme for Applica-
functions for a given domain. tions,” or VSA, making its mission clear: to be the perfect
companion to VBA, by adding metaprogramming power and
.NET reach to the Office/VBA solution developer’s arsenal
Scheme is a seasoned dialect in the venerable Lisp family of tools.
of computer programming languages. Quoting Guy Steele,
one of Scheme’s inventors, “If you give someone FORTRAN, It’s worth noting briefly that Scheme is used elsewhere
he has FORTRAN. If you give someone Lisp, he has any in the real world. For example, the Scheme programming
language he pleases” (Friedman, D. The Reasoned Schemer, language is the GNU project’s official application extension
Second Edition, MIT Press, 1998). The choice of providing a language, where it goes by the name of Guile (see: http://
Lisp language is a conscious decision to put the program- gnu.org/software/guile). Another implementation of
mer in control of the language’s features and functions Scheme, called MIT Scheme, is the programming language
for a given domain. The bottom-up style of programming of choice for teaching, using several advanced textbooks on
it encourages results in every Lisp application becoming a diverse engineering topics, including classical mechanics
domain-specific language (DSL). This is because of a fea- (Structure and Interpretation of Classical Mechanics, by G.J.
ture that all Lisps have, called macros (although Scheme’s Sussman and J. Wisdom), differential geometry (Functional
flavor of this feature is unique to it, and referred to as Differential Geometry, by G.J. Sussman and J. Wisdom), and
“syntax extension” instead). I’ll show an example of this software engineering (Software Design for Flexibility—How to
below. Avoid Programming Yourself into a Corner, by C. Hanson, and
Figure 3 is a screenshot of an example spreadsheet provid- Listing 2 (shown in the Lambda Editor in Figure 4) is the
ed in the download zip file (available from CODEMag.com) “words->sentence” lambda expression, which uses join-
that captures several important idioms of programming in words to produce all but the final period on the sentence.
Scheme as well as a simple example of .NET interoperability. String-append is used to add that. This expression is vari-
The challenge is to compose sentences from words listed in adic, which you can tell by the fact that the sole argument is
2D regions. In the first region, the words are ordered by row, the word “words” with no parentheses around it. This means
which is Excel’s default evaluation order when feeding a re- that it can take zero or more arguments, and wraps what-
gion to a user-defined function. The second region orders ever is (or isn’t) sent up into a nice list that =apply can use.
the words by column.
Listing 3 (shown in the Lambda Editor in Figure 5) uses the
There are four listings where lambda expressions are added =eval function provided by the add-in with a lambda expres-
to the sheet, corresponding to the numbers in Figure 3. sion that, in turn, calls the built-in =apply form. The issue
here is that =apply can only accept a single list, of whatever
Listing 1 is the “join-words” lambda expression. In the sheet,
this is created using the =define function provided by the
add-in. It allows you to give a Scheme name to a lambda Listing 1: the “join-words” lambda expression
expression. The resulting expression in A7 tells you that the (lambda (words delimiter)
Scheme name and the arity (or number of arguments) it takes. (clr-static-call
The hash is computed every time the code is altered to trig- String (Join String Object[]) delimiter (list->vector words)))
ger Excel’s calculation engine (depending on what options for
auto-calculation you have set up) and can be ignored.
Listing 2: the “words->sentence” lambda expression
Note the clr-static-call form. This is one of several .NET interop (lambda words
forms that are built in, making it easy for you to call into the (string-append (join-words words " ") "."))
.NET runtime directly. In this case, you’re calling the static Join
method of the String class in the System namespace.
Listing 3: using (flatten lol) in an inline “apply” lambda
The tricky bit here is that, as you can see by the signature, (lambda lst
Join expects an array, not a list. So, you call the built-in (apply words->sentence (flatten lst)))
Figure 4: The “words->sentence” lambda expression uses join-words to construct a sentence from the “words” argument.
Figure 6: Flatten-by-column provides by-column flattening semantics. The ability to choose list ordering against 2D
regions can come in handy, and not just for Mary and her little lambda!
Although somewhat contrived, this example shows some The tutorial does a good job of developing the code, so I’ll
important idioms and how they map to conventional Excel just comment on the important highlights of Listing 5.
behavior that’s important to understand.
First, blockchain.sls is a canonical library definition per the
R6RS specification, with which VSA and the underlying Iron-
Never Block the Chain Scheme implementation is 99% compliant. There are some
The add-in comes with a tutorial walking you through creat- niche aspects of continuations—a powerful Scheme feature
ing a blockchain-like feature in Excel. Figure 7 is a screen- that allows the language to be used to program constructs
shot of the first part of that tutorial. of other languages—that simply can’t be implemented on
the CLR because of low-level restrictions.
Rather than duplicate the steps here, instead I'll briefly cov-
er the salient aspects of the code that’s produced by the end It exports and imports several symbols from the (iron-
of the tutorial (see Listing 5). The two things that should scheme clr) namespace. The various clr-* forms encoun-
be paid most attention are: tered in Listing 5 are part of a very straightforward CLR
interoperability API that you, as a VSA coder, will get to
• The direct interop with .NET via clr-* functions know quickly, because the code here exercises most of it and
• How.NET abstractions are wrapped in idiomatic is relatively easy to understand.
Scheme code in a library, conformant with the R6RS
Scheme specification The (clr-using …) form works just like the C# using or
VB.NET imports statements. It allows the library to refer-
All of the code to make this example work is stored in the ence symbols in the respective namespaces.
blockchain.sls library file (the full contents of which are
provided in Listing 5). The only code that you’ll find in the
sample spreadsheet that’s included in the download is a Listing 4: using (flatten-by-column lol) in an inline “apply” lambda
lambda expression to grab the genesis block hash in B2.
(lambda lst
(lambda () (apply words->sentence
(genesis-block-hash)) (flatten-by-column lst)))
Figure 7: Reaching into the System.Security.Cryptography namespace of .NET makes it easy to model the basic concept
and value proposition of a blockchain right inside Excel.
Listing 5: blockchain.sls This function takes a string, and calls into the .NET System.
(library (blockchain) Security.Cryptography and System.Text namespaces to pro-
duce a SHA256 hash of the string, and is massaged to ap-
(export string->sha256 pear in the required format. It’s worth taking time to work
data-mine
genesis-block
through this tutorial, because it’s representative of what
genesis-block-hash) developers will do to surface .NET in a concise, easy-to-use
way in the context of Excel formula expressions.
(import (ironscheme)
(ironscheme clr))
(clr-using System.Text)
Anything You Can Do, I Can Do Meta
(clr-using System.Security.Cryptography) Finally, let’s take a look at a Scheme syntax extension.
(define genesis-block
(make-parameter "This is my genesis block.")) While the team was wrapping .NET APIs, it became desir-
able to iterate over collections implementing the IEnumer-
(define genesis-block-hash able interface in a cleaner syntax. Listing 6 provides the
(lambda () full code. What follows will be only a high-level description,
(string->sha256 (genesis-block))))
as an entire series of articles could be written to properly
(define string->sha256 explain how you might write code like this. It cannot be
(lambda (str) stressed enough just how powerful syntax extensions are
(let* ((utf8 (clr-static-prop-get Encoding UTF8)) in Scheme.
(bytes (clr-call UTF8Encoding
(GetBytes String)
utf8 str)) Most .NET developers will recall the sense of wonder and
(hash-fn (clr-new SHA256Managed)) awe the first time the C# compiler team released LINQ, or
(raw-result “Language Integrated Query.” Most will also remember the
(clr-call SHA256Managed
(ComputeHash System.Byte[]) gradual evolution of LINQ into the powerful, general-pur-
hash-fn bytes)) pose DSL that it is today. What I am about to say is not meant
(bits-str to diminish that sense of awe, but rather to rekindle it.
(clr-static-call BitConverter
ToString
raw-result)) In Scheme, a mostly complete version of LINQ can be imple-
(clean-bits (clr-call String mented in a weekend in about 900 lines of pure Scheme code.
Replace
bits-str "-" "")) You heard that right. This may sound blasphemous to a .NET
(lower-bits (clr-call String
ToLower developer. However, any veteran Lisper or Schemer will know
clean-bits))) that this is a fair observation. We are used to such power at
lower-bits))) our fingertips—it’s the whole point of the language.
(define data-mine
(lambda (current-block-data previous-block-hash) We know this because that’s the true story of the (iron-
(let* ((combined-data scheme linq) library that’s included as part of VSA. This is
(string-append current-block-data NOT a wrapper of .NET’s LINQ features, which are specific to
"+"
previous-block-hash))
compilers that implement them, but rather a pure Scheme
(result (string->sha256 combined-data))) implementation of generic iterators against pure Scheme
result))) collections with most of the familiar syntax sugar .NET de-
) velopers will recognize instantly.
The first example is the wrapper of the AllNodes property (define-enumerable graph/triples
of a Graph class as defined in dotNetRDF. (RDF plays an im- (case-lambda
portant role in the Professional edition but is also available [()
in the Standard edition.) Basically, what the (define-enu- (clr-prop-get Graph Triples (current-graph))]
merable…) syntax needs is a function to obtain the fresh
collection either from the (current-graph) by default, or a [(graph)
specific graph instance passed in via the one-argument ver- (begin
sion of the method. (if (or (null? graph)
(not (graph? graph)))
(define-enumerable graph/all-nodes (error
(case-lambda "graph must be a non-null Graph.")
[() (if (not (eq? graph (current-graph)))
(clr-prop-get Graph AllNodes (current-graph))] (current-graph graph)))
[(graph) (clr-prop-get Graph Triples graph))]) as
(begin TreeIndexedTripleCollection)
(if (or (null? graph)
(not (graph? graph))) These allow the user of the wrapper code to write the fol-
(error lowing:
"graph must be a non-null Graph.")
(if (not (eq? graph (current-graph))) (foreach t in (graph/triples)
(current-graph graph))) (display t)
(clr-prop-get Graph AllNodes graph))])) (newline))
(print-list d) (print-list p)
(print-list e) (print-list q)
(print-list f) (print-list r)
(print-list g) (print-list t)
Listing 7 provides a fuller flavor of LINQ in VSA, ecosystem of the .NET Runtime in all its incarna-
all of which will now also work with .NET types tions means that Office solution developers sud-
that implement IEnumerable if you use define- denly have access to two broad and deep solution
enumerable, as you did above, to expose them spaces that previously were unavailable. Jul/Aug 2022
to IronScheme’s iterator framework. These are Volume 23 Issue 4
borrowed from the documentation in (iron- The sturdy yet shape-shifting Scheme language
scheme linq) written by Llewelyn Pritchard, the offers a way to harness and control the raw power Group Publisher
main developer of IronScheme on which VSA is of the Common Language Runtime. It will be ex- Markus Egger
based. citing to see how industrious Office power users, Associate Publisher
VBA coders, Schemers, and .NET developers work Rick Strahl
together to leverage it and achieve greater re-
I Said All That So I Can Say This… turns for themselves and their clients.
Editor-in-Chief
Rod Paddock
This novel combination of a proven function-
Managing Editor
al programming language designed from the Bob Calco Ellen Whitney
ground-up for metaprogramming with the vast
Contributing Editor
John V. Petersen
Content Editor
Melanie Spiller
(Continued from 74) And if you’re in a regulated environment (SOX, Editorial Contributors
FDA, EPA, etc.), those rules and regulations, by Otto Dobretsberger
Jim Duffy
things that are contained by some other thing. and through regulatory agencies, bring many Jeff Etter
Content can be tangible like a book, painting, or considerations as to how you will distribute your Mike Yeager
computer. Content can be information. Content content.
Writers In This Issue
can be an ability. Content can be a service. And of Bob Calco Bilal Haidar
course, content can be software. In the era of the Inspection and adaptation are key concepts in Joydip Kanjilal Wei-Meng Lee
cloud, the phrase “software as a service” (SaaS) both agile and continuous improvement. That’s John Petersen Paul D. Sheriff
Shawn Wildermuth
is nearly ubiquitous. Even Office 365 is a promi- the circular aspect of the business of software
nent example of SaaS content. Whatever service or development. It's ironic that businesses often la- Technical Reviewers
product a business provides to the public to drive ment about how they must continue to keep pace Markus Egger
Rod Paddock
its earnings, that’s content. How a business deliv- with their own industries. Keeping pace often
ers that content is typically in the domain of some requires new and modified services and products Production
Friedl Raffeiner Grafik Studio
software application to at least some degree. (content). And that often means that the way we www.frigraf.it
build that software content must change as well. Graphic Layout
Agility is the means by which that software is Friedl Raffeiner Grafik Studio in collaboration
built. If this seems like a circle with no beginning Changing how we build software often gets short with onsight (www.onsightdesign.info)
or no ending, that’s because that’s exactly what shrift. It’s true that technology must serve the Printing
our world of software development is today. In business. But in order to serve the business, the Fry Communications, Inc.
order to break out of this back and forth in favor business must also serve and maintain its tech- 800 West Church Rd.
Mechanicsburg, PA 17055
of a practical tactical prescription, the manifesto nology. Although the business is the primary
itself provides a clear answer: Rely on use cases driver, each must service the other simultaneous- Advertising Sales
to drive process and tool selection. ly. That’s where the relationship between agility Tammy Ferguson
832-717-4445 ext 26
and content distribution comes in. tammy@codemag.com
In agile, we identify the items that are planned,
and their selection is informed and driven by the We must continuously search for the truth with Circulation & Distribution
General Circulation: EPS Software Corp.
items that are in response to change. our theories and abstractions. On one hand, we Newsstand: American News Company (ANC)
must widen our lens so that our abstractions have
In a previous column (CODA: On Tools and their as broad an application as possible. At the same Subscriptions
Subscription Manager
Selection in the March/April 2022 edition of CODE time, we must place limits on how abstract our Colleen Cade
Magazine), I described a system and approach for abstractions can be because there must be prac- ccade@codemag.com
tool selection to avoid what I call “tool salad.” Per- tical application. The latter requires that we also
US subscriptions are US $29.99 for one year. Subscriptions
haps a good process and approach may be to adopt narrow our lens and lower our altitude. outside the US are US $50.99. Payments should be made
domain-driven design (DDD) and a tool that sup- in US dollars drawn on a US bank. American Express,
ports DDD. For example, your business may have All of this seems rather counter-intuitive and con- MasterCard, Visa, and Discover credit cards accepted.
Back issues are available. For subscription information,
multiple service lines, each in their own domain and tradictory. If we accept that one view informs the e-mail subscriptions@codemag.com.
perhaps consisting of multiple sub-domains. Fur- other and vice versa, and we require that every-
ther, your business may have several cross-cutting thing be substantive (avoiding marketing chatter Subscribe online at
www.codemag.com
domains like HR, accounting, legal, and compliance. and quick summaries), we can undertake the seri-
To manage these facets in an agile manner, such that ous hard work of solving today’s complex problems. CODE Developer Magazine
you can respond quickly to change, requires a plan. Solving problems involves a certain amount of in- 6605 Cypresswood Drive, Ste 425, Spring, Texas 77379
Phone: 832-717-4445
formed trial and error to make a positive impact.
Perhaps that plan consists of implementing DDD And agility applied to a business’s content distri-
and DevOps. To speed delivery, you may consider bution is a means to achieve that positive impact.
continuous integration and delivery (CI/CD).
How do you implement such things? Clean code John V. Petersen
(SOLID) programming practices are one such way.
you understand that I’m always in search of two isolation, these two things are indeed indepen- items on the left to the entire exclusion of the
basic things. First, finding the clearest path from dent abstract things. They’re very much akin to things on the right.
the abstract to the concrete. Theory is important, concepts like design patterns and domain-driven
but only if it has practical application. Practical design. If there’s no practical application for There are infinite ways to practice agile. The only re-
should be long on value, short on ceremony. those abstract things, what good are they? quirement is that whatever mode of practice is im-
Application, to be practical and useful, must be plemented, that mode must support the manifesto
clear and straightforward. We see this with agile today. A shop proclaims, and such support must be demonstratable through
“We’re agile.” Or “We practice agility.” But all objective evidence. Too often, the manifesto is mis-
That’s the value proposition to anything. It’s that too often when we try to pin them down on how read and misunderstood to imply or explicitly state
value proposition that gets to the second item, they’re agile, we’re greeted with crickets. This that “agile means no documentation.” A plain read-
the broadest possible application. The irony here usually happens when agility is but an aspiration- ing of the manifest clearly disproves that assertion.
is that in order to find the broad application, it al thing. If we understand at an abstract level
requires making the concrete abstract. At the same what different and complementary things are, Another misunderstanding is “agile means no plan-
time, in order to make the abstract have practical how they relate to one another and in combina- ning.” If we look to established agile frameworks,
application, it must be made concrete. One feeds tion, and how they, as a combined unit, relate like scrum, that recognize sprint planning as a neces-
into the other simultaneously, like an Escher draw- to other things, then perhaps then we can begin sary event, again, we can easily conclude that agility
ing with no beginning and no end. It’s more like to understand how these different things can be and planning are not mutually exclusive and antago-
the two sides of a coin, which is part of the pri- joined as two sides of a coin to fuel the business. nistic to each other. To the degree that we engage
mary currency in business and technology today. with planning, that planning should be tempered by
Generally, necessary things in business come in Let’s try that with agility and content distribu- what we engage in while responding to change.
pairs—complementary pairs, much like tactics and tion here.
strategy. Tactics must support and be informed If there were no plan, all we’d do is react, not
by a coherent strategy and any coherent strategy respond. Reactions are quick and reflexive, not
must be tempered by coherent actionable tactics. Agility considered and thoughtful. It’s through interac-
Agility is defined in the Agile Manifesto (ag- tions with our customers that we plan enough to
ilemanifesto.org), which is a set of principles. build working software. A document that purports
That’s a very important point. In other words, to articulate what proposed software is to accom-
Agility and content Agile isn’t what we think it is. Agile is what it plish is just that, a document. The document may
distribution have become says it is, as defined in the manifesto. Agile isn’t deliver a good, aspirational story, but it doesn’t
a practice, it’s a collection of principles. To prac- yield the same value that working software would
quite misunderstood. tice Agile principles, there must be some con- That story, at best, is a means to some other end.
crete application. Agile principles state: As a communication device, there is most certainly
value. The end result must be some value proposi-
• Individuals and interactions over processes tion that the business will realize and understand:
That leads to the two things I’m keenly interested in and tools that is, delivered and working software (working
today: agility and content distribution, two things • Working software over comprehensive doc- defined as conforming to a defined specification)
that have largely become the stuff of middle- umentation
management marketing reductionism. Both agil- • Customer collaboration over contract ne- Serving the business is what technology is sup-
ity and content distribution, like blockchain, have gotiation posed to be about. And agility is one means by
become misunderstood, despite their importance, • Responding to change over following a plan which that can be done. And that means can only
and despite the substance they represent. This have efficacy if it’s properly matched with what
pair, I believe, is a good example of the two-sided The Agile Manifesto concludes with: …[w]hile the business does. In my opinion, every busi-
coin metaphor and, when broken down, illustrates there is value in the things on the right, we ness’s value proposition is found in how it dis-
what I further believe to be the proper lens through value the items on the left more. The “left” tributes its content.
which to view the intersectionality of business and and “right” here are the things that are stated
technology and therefrom, realize the value propo- before and after the word “over” respectively.
sition that agility and content distribution present. Ultimately, what is of acceptable value is de- Content Distribution
termined by many things that, among other Traditionally, we see the phrase “content distribu-
From a practical standpoint, one or the other things, are specific to your firm’s and the proj- tion” solely in the media context. Think HBO Max,
cannot be viewed in isolation. It’s the same line ect’s context. The big point is that too often, Tik Tok, Netflix, etc. “Content” is simply those
that’s between practice and theory or, put an- the manifesto’s concluding sentence is forgot-
other way, the concrete versus the abstract. In ten. Put another way, we don’t only consider the (Continued on page 73)
VERTICAL FOCUS.
GLOBAL REACH.
LEARN
Expansive education program with
solutions-oriented presentations & workshops
from UAS thought-leaders
CONNECT
Facilitated networking, matchmaking, and
focused roundtables, with drone industry
professionals from around the globe
Registration
is open! EXPERIENCE
Use code SAVE100 for Cutting-edge UAS solutions providers,
$100 off a Full Conference Pass live outdoor drone demonstrations
or a FREE Exhibit Hall Pass. & exclusive training
expouav.com
Construction Drone Energy Forestry Infrastructure Mining Public Safety Security Surveying
Delivery & Utilities & Agriculture & Transportation & Aggregates & Emergency Services & Mapping
shutters
tock/Lu
cky-pho
tograp
her
NEED
MORE OF THIS?
Is slow outdated software stealing way too much of your free time? We can help.
We specialize in updating legacy business applications to modern technologies.
CODE Consulting has top-tier developers available with in-depth experience in .NET,
web development, desktop development (WPF), Blazor, Azure, mobile apps, IoT and more.
Contact us today for a complimentary one hour tech consultation. No strings. No commitment. Just CODE.
codemag.com/modernize
832-717-4445 ext. 9 • info@codemag.com