Author Archives: Robert Haken

About Robert Haken

Software Architect, Founder at HAVIT, Microsoft MVP - ASP.NET/IIS

Office365 Teams: PowerShell automation of team membership

Office365 Academic (Education) does not support organization-wide teams. To get over this limitation, you can maintain the membership by a simple PowerShell script:

Get-MsolUser -All | Where-Object { $_.Licenses.ServiceStatus.ServicePlan.ServiceName -match "TEAMS"} | ForEach-Object { [pscustomobject]@{User = $PSItem.UserPrincipalName} } | Add-TeamUser -GroupId f29dbfc0-d118-4480-a6f7-22048a0809d0 -Role Member

In short:

  1. Take all Azure Active Directory users.
  2. Filter them to those having a Teams license assigned.
  3. Transform the data to input usable for Add-TeamUser cmdlet.
  4. Add the users to the team (you will get the GroupId by using Get-Team cmdlet)

You can run the script repeatedly, just the missing users will be added. You can modify the script easily and apply any additional conditions when adding the users to the group.

Might help:

Install-Module MicrosoftTeams
Install-Module MSOnline

Connect-MicrosoftTeams
Connect-MsolService

Get-Team

Blazor WebAssembly with gRPC-Web code-first approach

Do you like WCF-like approach and need to cover communication in between ASP.NET Core service and Blazor WebAssembly client? Use code-first with gRPC-Web! You can try the it right now by following a few simple steps (GitHub repo):

1. Blazor.Server – Prepare the ASP.NET Core host

Add NuGet packages:

  1. Grpc.AspNetCore.Web (prerelease)
  2. protobuf-net.Grpc.AspNetCore

Register CodeFirstGrpc() and GrpcWeb() services in Startup.cs ConfigureServices() method:

services.AddCodeFirstGrpc(config => { config.ResponseCompressionLevel = System.IO.Compression.CompressionLevel.Optimal; });

Add GrpcWeb middleware in between UseRouting() and UseEndpoints():

app.UseGrpcWeb(new GrpcWebOptions() { DefaultEnabled = true });

2. Blazor.Shared – Define the service contract (code-first)

Add System.ServiceModel.Primitives NuGet package.

Define the interface of your service:

[ServiceContract]
public interface IMyService
{
	Task DoSomething(MyServiceRequest request);

}

[DataContract]
public class MyServiceResult
{
	[DataMember(Order = 1)]
	public string NewText { get; set; }

	[DataMember(Order = 2)]
	public int NewValue { get; set; }
}

[DataContract]
public class MyServiceRequest
{
	[DataMember(Order = 1)]
	public string Text { get; set; }

	[DataMember(Order = 2)]
	public int Value { get; set; }
}

3. Blazor.Server – Implement and publish the service

Implement your service:

public class MyService : IMyService
{
	public Task DoSomething(MyServiceRequest request)
	{
		return Task.FromResult(new MyServiceResult()
		{
			NewText = request.Text + " from server",
			NewValue = request.Value + 1
		});
	}
}

Publish the service in Startup.cs:

app.UseEndpoints(endpoints =>
{
	endpoints.MapGrpcService();
	// ...
}

4. Blazor.Client (Blazor Web Assembly) – consume the service

Add NuGet packages:

  1. Grpc.Net.Client
  2. Grpc.Net.Client.Web (prerelease)
  3. protobuf-net.Grpc

Consume the service in your razor file:

var handler = new Grpc.Net.Client.Web.GrpcWebHandler(Grpc.Net.Client.Web.GrpcWebMode.GrpcWeb, new HttpClientHandler());
using (var channel = Grpc.Net.Client.GrpcChannel.ForAddress("https://localhost:44383/", new Grpc.Net.Client.GrpcChannelOptions() { HttpClient = new HttpClient(handler) }))
{
	var testFacade = channel.CreateGrpcService();
	this.result = await testFacade.DoSomething(request);
}

(You can move the plumbing to ConfigureServices() and use pure dependency injection in your razor files.)

References

  1. Steve Sanderson: Using gRPC-Web with Blazor WebAssembly
  2. Use gRPC in browser apps | Microsoft Docs
  3. protobuf-net.Grpc – Getting Started

Blazor Component Lifecycle Diagram

Blazor Component Lifecycle Diagram

Get familiar with basic Blazor (Razor) Component Lifestyle Methods:

  • SetParametersAsync
  • OnInitialized, OnInitializedAsync
  • OnParametersSet, OnParametersSetAsync
  • event handlers, event callbacks
  • BuildRenderTree
  • OnAfterPreRender, OnAfterPreRenderAsync
  • IDisposable.Dispose

 

My upgrade from Lenovo X1 Carbon 4gen to 6gen

Image result for lenovo x1 carbon 6th generation

My Confguration (20KH006MMC)

  • Intel Core i7 8550U Kaby Lake Refresh (Intel UHD Graphics 620)
  • 14″ LED 2560×1440 IPS HDR antireflex, 500 nits
  • RAM 16GB LPDDR3
  • 1TB SSD (M.2 PCIe NVMe)
  • 4G LTE

First Impressions

My first impressions of 6th gen in comparison to 4th gen:

  • The X1 Carbon 6gen looks great, the quality is almost perfect.
  • It is a little bit smaller and super-light (1,2kg).
  • The display is glossy. I usually prefer matte, but this one seems to be exceptional. It looks much more “bright”, although the red color is weird (over-saturated?).
  • It has rubber/soft texture (for me already known from my former T440s – looks good but is difficult to maintain + the edges will probably get bare soon).
  • The keyboard is softer and does not make any weird noises (MyX1-4gen produced rattling noises when typing). The typing seems to be more comfortable than the X1-4gen. The keyboard rattling was in fact the major deficiency I had with 4gen.
  • It produces more heat. Noticeably less comfortable to have it on a lap.
  • The 6gen battery gets exhausted pretty quickly. No improvement in comparison to 4gen.
  • Wake-up from sleep is pretty quick. It is ready right as you open the lid.
  • In Sleep mode it stays quite warm (= consumes battery).

Sleep Mode

Well, the last two items seems to be interconnected. The X1 6gen comes with so called “Standby (S0 Low Power Idle) Network Connected” power profile which keeps the notebook half-alive (able to download updates etc.). After switching the Sleep mode to S3 (switch sleep mode from “Windows 10” to “Linux” in BIOS) it is cold during Sleep. The wake-up is a little bit slower in S3 than in S0, but still pretty fast.

SQL: Index statistics update date

Simple query can help you get basic insights on when the index statistics where updated:

SELECT
		o.name AS TableName,
		i.name AS IndexName,
		STATS_DATE(i.object_id, i.index_id) AS StatisticsUpdate
	FROM sys.objects o
		INNER JOIN sys.indexes i ON (o.object_id = i.object_id)
	WHERE
		(i.type > 0)
		AND (o.type_desc NOT IN ('INTERNAL_TABLE', 'SYSTEM_TABLE'))
	ORDER BY TableName, IndexName
	-- ORDER BY StatisticsUpdate

See also: