My book Mastering ASP.NET Core 2.0 is now available from Packt Publishing. If you are interested in buying the ebook, you can use this discount code, which will give you a 40% discount:
MANC2EB40
Remember – this is for the ebook only. Enjoy!
My book Mastering ASP.NET Core 2.0 is now available from Packt Publishing. If you are interested in buying the ebook, you can use this discount code, which will give you a 40% discount:
MANC2EB40
Remember – this is for the ebook only. Enjoy!
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:
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.
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;
}
}
}
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!
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();
}
}
var view = this.View(“ViewName”);
var html = view.ToHtml();
Have fun!
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;
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);
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!