Soft Deletes with Entity Framework Core 2 – Part 2

My previous post explained how to setup soft deletes for queries automatically. This time, I’m going to talk about the other part: actually replacing a delete for an update that sets the is deleted column.

The key here is to intercept the SaveChanges method, find out all entities that are ISoftDeletable and are marked for deletion and then set their IsDeleted property and change their state to be modified instead of deleted:

public override int SaveChanges()
{
foreach (var entry in this.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted))
{
if (entry.Entity is ISoftDeletable)
{
entry.Property(_isDeletedProperty).CurrentValue = true;
entry.State = EntityState.Modified;
}
}

return base.SaveChanges();
}
This way Entity Framework will know what to do and just update them instead of deleting them. Next time, they won’t be loaded as they have been marked as deleted.

Soft Deletes with Entity Framework Core 2 – Part 1

Entity Framework Core 2, already covered here, includes a cool feature called global filters. By leveraging global filters, we can apply restrictions automatically to entities, either loaded directly or through a collection reference. If we add this to shadow properties (in the case of relational databases, columns that exist in a table but not on the POCO model), we can do pretty cool stuff.

In this example, I am going to create a soft delete global filter to all entities in the model that implement a marker interface ISoftDeletable.

public interface ISoftDeletable
{
}
We just need to override the DbContext’s OnModelCreating method to automatically scan all known entities to see which implement this interface and then create the restriction automatically:
private const string _isDeletedProperty = "IsDeleted";
private static readonly MethodInfo _propertyMethod = typeof(EF).GetMethod(nameof(EF.Property), BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(typeof(bool));

private static LambdaExpression GetIsDeletedRestriction(Type type)
{
var parm = Expression.Parameter(type, "it");
var prop = Expression.Call(_propertyMethod, parm, Expression.Constant(_isDeletedProperty));
var condition = Expression.MakeBinary(ExpressionType.Equal, prop, Expression.Constant(false));
var lambda = Expression.Lambda(condition, parm);
return lambda;
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
if (typeof(ISoftDeletable).IsAssignableFrom(entity.ClrType) == true)
{
entity.AddProperty(_isDeletedProperty, typeof(bool));

modelBuilder
.Entity(entity.ClrType)
.HasQueryFilter(GetIsDeletedRestriction(entity.ClrType));
}
}

base.OnModelCreating(modelBuilder);
}
So, for each entity known from the context we add a shadow property called IsDeleted of type bool. Of course, needless to say, it must also exist on the database. The reason I’m making it a shadow property is to avoid people tampering with the entities, by setting or unsetting its value. This way, the restriction is always performed and it is invisible to us. After we create the property, we add a restriction to the entity’s type.
Simple, don’t you think? This way, if you want to enable or disable it for a number of entities, just have them implement the ISoftDeletable interface.

Mastering ASP.NET Core 2.0

Mastering ASP.NET Core 2.0 Book Cover

My new book is out! It is called Mastering ASP.NET Core 2.0 and was edited by Packt Publishing, as my previous one, Entity Framework Core Cookbook – Second Edition.

It was big challenge – the book has over 480 pages – and it spanned multiple .NET Core versions – 1.1 and 2.0. I tried to cover the most important things, even with some detail. The chapters are:

  1. Getting Started with ASP.NET Core: .NET Core, ASP.NET Core, platforms, DI & IoC, MVC pattern, OWIN, hosting, environments
  2. Configuration: providers
  3. Routing: templates, handlers, constraints, areas, error handling
  4. Controllers and Actions: controller lifecycle, API controllers, versioning, documentation, globalization, binding
  5. Views: areas, layouts, Razor pages, globalization
  6. Using Forms and Models: metadata, templates, binding, validation
  7. Security: authentication, authorization, anti-forgery, CORS, HTTPS
  8. Reusable Components: partial views, view components, tag helpers and tag helper components
  9. Filters: authorization, resource, action, result, exception, Razor page
  10. Logging, Tracing and Diagnostics: custom middleware, logging, DiagnosticSource, ELM, AppInsights, HealthCheck
  11. Testing: xUnit, integration tests, UI tests with Selenium
  12. Client-Side Development: Bower, Node.js/NPM, Gulp, Grunt, TypeScript, LESS
  13. Improving the Performance and Scalability: asynchronous methods, profiling, bundling and minification, caching, response compression
  14. Real-Time Communication: SignalR
  15. Other Topics: areas, static files, application lifetime events, conventions, embedded resources, hosting extensions, URL rewriting
  16. Deployment: Visual Studio, IIS, Azure, AWS, Nginx, Apache, Docker, Windows Service

Whenever there are important differences, I mention the differences between ASP.NET Core 1.x and 2.x, although I think this will be less important over time.

Overall, it was an exciting task, but not one without obstacles. I must thank the team at Packt Publishing, namely, Siddhi Chavan and Abhishek Sharma for all their patience and support.

Do have a look and share your feedback! It is available from the Packt Publishing site, Amazon and others, either in hardcopy or ebook format. The source code is available at https://github.com/PacktPublishing/Mastering-ASP.NET-Core-2.0.

Entity Framework Core Extensions: Getting Primary Keys and Dirty Properties

Two simple hacks: finding the primary key values:

public static IDictionary<string, object> GetKeys(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var primaryKey = entry.Metadata.FindPrimaryKey();
var keys = primaryKey.Properties.ToDictionary(x => x.Name, x => x.PropertyInfo.GetValue(entity));

return keys;
}

This returns a dictionary because some entities may have composite primary keys.

As for getting the dirty (modified) properties’ names for an entity:

public static IEnumerable<string> GetDirtyProperties(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var originalValues = entry.OriginalValues;
var currentValues = entry.CurrentValues;

foreach (var prop in originalValues.Properties)
{
if (object.Equals(originalValues[prop.Name], currentValues[prop.Name]) == false)
{
yield return prop.Name;
}
}
}
Picking on the last one, if we wish to reset an entity to it’s original values:
public static void Reset(this DbContext ctx, object entity)
{
if (ctx == null)
{
throw new ArgumentNullException(nameof(ctx));
}

if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}

var entry = ctx.Entry(entity);
var originalValues = entry.OriginalValues;
var currentValues = entry.CurrentValues;

foreach (var prop in originalValues.Properties)
{
currentValues[prop.Name] = originalValues[prop.Name];
}

entry.State = EntityState.Unchanged;
}

Mind you, this one will not reset collections or references, just plain properties.

Hope it helps!

Getting the HTML for a ViewResult in ASP.NET Core

This is another post tagged “hack”: this time, how to get the HTML for a rendered view, in code. This is a standard solution that does not use any kind of reflection (or other) magic.

So, the idea is to pick up a ViewResult (it will also work for a PartialViewResult, but, alas, they do not share a common class – see #6984) and call some method, say, ToHtml, to get the rendered output. This method can look like this:

public static class ViewResultExtensions
{
public static string ToHtml(this ViewResult result, HttpContext httpContext)
{
var feature = httpContext.Features.Get<IRoutingFeature>();
var routeData = feature.RouteData;
var viewName = result.ViewName ?? routeData.Values["action"] as string;
var actionContext = new ActionContext(httpContext, routeData, new ControllerActionDescriptor());
var options = httpContext.RequestServices.GetRequiredService<IOptions<MvcViewOptions>>();
var htmlHelperOptions = options.Value.HtmlHelperOptions;
var viewEngineResult = result.ViewEngine?.FindView(actionContext, viewName, true) ?? options.Value.ViewEngines.Select(x => x.FindView(actionContext, viewName, true)).FirstOrDefault(x => x != null);
var view = viewEngineResult.View;
var builder = new StringBuilder();

using (var output = new StringWriter(builder))
{
var viewContext = new ViewContext(actionContext, view, result.ViewData, result.TempData, output, htmlHelperOptions);

view
.RenderAsync(viewContext)
.GetAwaiter()
.GetResult();
}

return builder.ToString();
}
}
To use it, just do:
var view = this.View(“ViewName”);
var html = view.ToHtml();

Have fun! Winking smile

This is the eight post in a series of posts about bringing the features that were present in Entity Framework pre-Core into EF Core. The others are:

  • Part 1: Introduction, Find, Getting an Entity’s Id Programmatically, Reload, Local, Evict

  • Part 2: Explicit Loading

  • Part 3: Validations

  • Part 4: Conventions

  • Part 5: Getting the SQL for a Query

  • Part 6: Lazy Loading

  • Part 7: Entity Configuration in Mapping Classes

This time I’m going to talk about the possibility to log the generated SQL to the output. In the past, we could do it by assigning a writer to DbContext.Database.Log:

ctx.Database.Log = Console.Log;
This would cause all SQL statements to be sent to the console. We can do something similar in EF Core, by leveraging the logging framework:
var loggerFactory = new LoggerFactory()
.AddDebug((categoryName, logLevel) => (logLevel == LogLevel.Information) && (categoryName == DbLoggerCategory.Database.Command.Name))
.AddConsole((categoryName, logLevel) => (logLevel == LogLevel.Information) && (categoryName == DbLoggerCategory.Database.Command.Name));

var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
.UseLoggerFactory(loggerFactory)
.UseSqlServer(@"<connectionString>");

var ctx = new MyContext(optionsBuilder.Options);
We are creating a new logger factory and populating it with the console and debug providers. You will need to add the Microsoft.Extensions.Logging.Console and Microsoft.Extensions.Logging.Debug NuGet packages (you don’t need both). For each of these providers, we are filtering the output by LogLevel.Information and category DbLoggerCategory.Database.Command.Name (“Microsoft.EntityFrameworkCore.Database.Command”), which are the values used when outputting SQL.

Then, we are creating a DbContextOptionsBuilder and in it we are replacing the default logger factory with our own. We then use it to create an option which we pass to our DbContext’s constructor, or we could do the same in the OnConfiguring method.

The output will look something like this:

SELECT [p].[ProjectId], [p].[CreatedAt], [p].[CreatedBy], [p].[CustomerId], [p].[Description], [p].[End], [p].[Name], [p].[Start], [p].[UpdatedAt], [p].[UpdatedBy]
FROM [Projects] AS [p]

Hope this helps! Winking smile

 

Postal.NET Revisited

Some of you may be aware of my Postal.NET project. It is an open-source library for writing decoupled applications that is available in GitHub and in Nuget. I already blogged about it before.

I recently updated it so that it targets .NET Standard 2.0 and also fixed the namespaces for its companion projects so that they are all under PostalNET. Current version for all projects is 1.2.0.

In a nutshell, Postal.NET publishes messages to channels and topics, either synchronously or asynchronously, and interested parties can subscribe to these.

Besides the core Postal.NET project, there are a few others:

  • PostalConventions.NET: configurable conventions for channels and topics
  • PostalCqrs.NET: a Command-Query Responsibility Segregation library built on top of Postal.NET
  • PostalInterceptor.NET: interception for message publishing
  • PostalRX.NET: Reactive Extensions
  • PostalRequestResponse.NET: a request-response wrapper
  • PostalWhen.NET: “when this, do that”

I still need to write unit tests, but there are many samples available that hopefully will be enough to get you started. If you have interest, please have a look and share your thoughts, either here or by creating issues in GitHub!

User Interface Unit Tests with .NET Core

In a previous post I talked about doing unit tests with .NET Core. What I didn’t cover was unit testing for the user interface (UI). It is actually something quite common, and we will see how we can do it now.

Selenium is a portable software-testing framework for web applications and there is a .NET port of it. We will be using the Selenium.WebDriver package, plus one or more of the following, to target different browsers:

Selenium offers a “minimum” contract that works across all browsers.

I won’t go through all of it, but I will give you some examples on how it works. We start by instantiating a driver:

using (var driver = (IWebDriver) new ChromeDriver(Environment.CurrentDirectory))
{
//...
}
Notice the Environment.CurrentDirectory parameter; it specifies the path where the driver can find the chromedriver.exe file, or geckodriver.exe for Firefox or MicrosoftWebDriver.exe, in the case of IE/Edge. These executables are added automatically by the Nuget packages. If you don’t dispose of the driver, the window will remain open after the unit test finishes. You can also call Quit at any time.
Now, we can navigate to some page:
driver
.Navigate()
.GoToUrl("http://www.google.com");
And find some element from its name:
var elm = driver.FindElement(By.Name("q"));
Besides the name, we can also search by:
  • Id: By.Id
  • CSS class: By.ClassName
  • CSS selector: By.CssSelector
  • Tag name: By.TagName
  • Link text: By.LinkText
  • Partial link text: By.PartialLinkText
  • XPath: By.XPath

Once we find an element, we can access its properties:

var attr = elm.GetAttribute("class");
var css = elm.GetCssValue("display");
var prop = elm.GetProperty("enabled");

Then we can send it text strokes:

elm.SendKeys("asp.net core");

Or click on it:

elm.Click();
As we know, page loading can take some time, so, we can configure the default time to wait for it, probably before we do a GoToUrl:
var timeouts = driver.Manage().Timeouts();
timeouts.ImplicitWait = TimeSpan.FromSeconds(1);
timeouts.PageLoad = TimeSpan.FromSeconds(5);
ImplicitWait is just a time that Selenium waits before searching for an element.
If we need to wait for some period of time, like, until some AJAX request finishes, we can do this:
var waitForElement = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

var logo = waitForElement.Until(ExpectedConditions.ElementIsVisible(By.Id("hplogo")));
The condition passed to ExpectedConditions can be one of:
  • AlertIsPresent
  • AlertState
  • ElementExists
  • ElementIsVisible
  • ElementSelectionStateToBe
  • ElementToBeClickable
  • ElementToBeSelected
  • FrameToBeAvailableAndSwitchToIt
  • InvisibilityOfElementLocated
  • InvisibilityOfElementWithText
  • PresenceOfAllElementsLocatedBy
  • StalenessOf
  • TextToBePresentInElement
  • TextToBePresentInElementLocated
  • TextToBePresentInElementValue
  • TitleContains
  • TitleIs
  • UrlContains
  • UrlMatches
  • UrlToBe
  • VisibilityOfAllElementsLocatedBy
As you can see, you have a wealth of conditions that you can use. If the condition is not met before the timer expires, then the value returned by Until is null.
This can be a nice complement to your unit tests. Hope you find this useful! Winking smile

SignalR in ASP.NET Core

Introduction

SignalR is a Microsoft .NET library for implementing real-time web sites. It uses a number of techniques to achieve bi-directional communication between server and client; servers can push messages to connected clients anytime.

It was available in pre-Core ASP.NET and now a pre-release version was made available for ASP.NET Core. I already talked a few times about SignalR.

Installation

You will need to install the Microsoft.AspNetCore.SignalR.Client and Microsoft.AspNetCore.SignalR Nuget pre-release packages. Also, you will need NPM (Node Package Manager). After you install NPM, you need to get the @aspnet/signalr-client package, after which, you need to get the signalr-client-1.0.0-alpha1-final.js file (the version may be different) from the node_modules\@aspnet\signalr-client\dist\browser folder and place it somewhere underneath the wwwroot folder, so that you can reference it from your pages.

Next, we need to register the required services in ConfigureServices:, before Use

services.AddSignalR();

We will be implementing a simple chat client, so, we will register a chat hub, in the Configure method:

app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("chat");
});

A note: UseSignalR must be called before UseMvc!

You can do this for any number of hubs. as long as you have different endpoints. More on this in a moment.

In your view or layout file, add a reference to the signalr-client-1.0.0-alpha1-final.js file:

<script src="libs/signalr-client/signalr-client-1.0.0-alpha1-final.js"></script>

Implementing a Hub

A hub is a class that inherits from (you guessed it) Hub. In it you add methods that may be called by JavaScript. Since we will be implementing a chat hub, we will have this:

public class ChatHub : Hub
{
public async Task Send(string message)
{
await this.Clients.All.InvokeAsync("Send", message);
}
}

As you can see, we have a single method, Send, which, for this example, takes a single parameter, message. You do not need to pass the same parameters on the broadcast call (InvokeAsync), you can send whatever you want.

Going back to the client side, add this code after the reference to the SignalR JavaScript file:

    <script>
     
        var transportType = signalR.TransportType.WebSockets;
        //can also be ServerSentEvents or LongPolling
        var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
        var chatHub = new signalR.HttpConnection(`http://${document.location.host}/chat`, { transport: transportType, logger: logger });
        var chatConnection = new signalR.HubConnection(chatHub, logger);
     
        chatConnection.onClosed = e => {
            console.log('connection closed');
       };
    
       chatConnection.on('Send', (message) => {
           console.log('received message');
       });
    
       chatConnection.start().catch(err => {
           console.log('connection error');
       });
    
       function send(message) {
           chatConnection.invoke('Send', message);
       }
    
</script>

Notice this:

  1. A connection is created pointing to the current URL plus the chat suffix, which is the same that was registered in the MapHub call
  2. It is initialized with a specific transport, in this case, WebSockets, but this is not required, that is, you can let SignalR figure out for itself what works; for some operating systems, such as Windows 7, you may not be able to use WebSockets, so you have to pick either LongPolling or ServerSentEvents
  3. The connection needs to be initialized by calling start
  4. There is an handler for the Send method which takes the same single parameter (message) as the ChatHub’s Send method

So, whenever someone accesses this page and calls the JavaScript send function, it invokes the Send method on the ChatHub class. This class basically broadcasts this message to all connected clients (Clients.All). It is also possible to send messages to a specific group (we’ll see how to get there):

await this.Clients.Group("groupName").InvokeAsync("Send", message);
or to a specific client:
await this.Clients.Client("id").InvokeAsync("Send", message);
You can add a user, identified by a connection id and and a ClaimsPrincipal, if using authentication, as this:
public override Task OnConnectedAsync()
{
this.Groups.AddAsync(this.Context.ConnectionId, "groupName");

return base.OnConnectedAsync();
}
Yes, the OnConnectedAsync is called whenever a new user connects, and there is a similar method, OnDisconnectedAsync, for when someone disconnects:
public override Task OnDisconnectedAsync(Exception exception)
{
return base.OnDisconnectedAsync(exception);
}
The exception parameter is only non-null if there was some exception while disconnecting.
The Context property offers two properties, ConnectionId and User. User is only set if the current user is authenticated, but ConnectionId is always set, and does not change, for the same user.

Another example, imagine you wanted to send timer ticks into all connected clients, through a timer hub. You could do this in the Configure method:

TimerCallback callback = (x) => {
var hub = serviceProvider.GetService<IHubContext<TimerHub>>();
hub.Clients.All.InvokeAsync("Notify", DateTime.Now);
};

var timer = new Timer(callback);
timer.Change(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(10));
Here we are starting a Timer and, from there, we are getting a reference to the timer hub and calling its Notify method with the current timestamp. The TimerHub class is just this:
public class TimerHub : Hub
{
}
Notice that this class has no public method, because it is not meant to be callable by JavaScript, it merely is used to broadcast messages from the outside (the Timer callback).

Sending Messages Into a Hub

Finally, it is also possible to send messages from the outside into a hub. When using a controller, you need to inject into it an instance of IHubContext<ChatHub>, from which you can send messages into the hub, which will then be broadcast to where appropriate:
private readonly IHubContext<ChatHub> _context;

[HttpGet("Send/{message}")]
public IActionResult Send(string message)
{
//for everyone
this._context.Clients.All.InvokeAsync("Send", message);
//for a single group
this._context.Clients.Group("groupName").InvokeAsync("Send", message);
//for a single client
this._context.Clients.Client("id").InvokeAsync("Send", message);

return this.Ok();
}

Note that this is not the same as accessing the ChatHub class, you cannot easily do that, but, rather, the chat hub’s connections.

Conclusion

SignalR has not been released yet, and it may still undergo some changes. For now, things appear to be working. On a future post I will talk more about SignalR, including its extensibility mechanisms and some more advanced scenarios. Stay tuned!