Configuration Extension Examples

AutoMapper Configuration

using AutoMapper;
using Christopher.Snay.Sample.Configuration.AutoMapperProfiles;
using Christopher.Snay.Sample.Configuration.AutoMapperProfiles.Dtos;
using Microsoft.Extensions.DependencyInjection;

namespace Christopher.Snay.Sample.Configuration
{
	public static class AutoMapperConfiguration
	{
		public static void AddAutoMapperConfiguration(this IServiceCollection services)
		{
			services.AddSingleton(new MapperConfiguration(config =>
			{
				config.AddProfile<SampleMapperProfile1>();
				config.AddProfile<SampleMapperProfile2>();		
			}).CreateMapper());
		}
	}
}

CORS Configuration

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace Christopher.Snay.Sample.Configuration
{
	public static class CorsConfiguration
	{
		public static IApplicationBuilder UseCorsConfiguration(this IApplicationBuilder app)
		{
			return app.UseCors("CorsPolicy");
		}

		public static IServiceCollection AddCorsConfiguration(this IServiceCollection services)
		{
			var origins = new List<string>
			{
				"http://localhost:4200",
				
				"http://sample-server",
			};

			return services.AddCors(options =>
			{
				options.AddPolicy("CorsPolicy", builder =>
				{
					builder
						.WithOrigins(origins.ToArray())
						.AllowAnyMethod()
						.AllowAnyHeader()
						.AllowCredentials();
				});
			});
		}
	}
}

HttpClientFactory Configuration

using System.Net.Http.Headers;
using System.Net.Mime;
using Christopher.Snay.Sample.Services.RetailerServices;
using Microsoft.Extensions.DependencyInjection;

namespace Christopher.Snay.Sample.Configuration
{
	public static class HttpConfiguration
	{
		public static void AddHttpClientConfiguration(this IServiceCollection services)
		{
			services.AddHttpClient(nameof(ISampleService1), x => x.BaseAddress
				= new Uri("https://www.api.sample.com/search"));

			services.AddHttpClient(nameof(ISampleService2), x => x.BaseAddress
				= new Uri("https://www.api.sample2.com/search"));

			services.AddHttpClient(nameof(ISampleService3), x =>
			{
				x.BaseAddress = new Uri("https://www.api.sample3.com/search");
				x.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("Mozilla", "5.0")));
				x.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
				x.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
				x.DefaultRequestHeaders.Host = "www.sample.com";
				x.DefaultRequestHeaders.Connection.Add("keep-alive");
				x.DefaultRequestHeaders.Add("cookie", "Bearer ABC123");
			}
		}
	}
}

Scrape rendered HTML with .NET6 C#

using HtmlAgilityPack;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.PhantomJS;

namespace Christopher.Snay.Sample.Services.Scrapers
{
	internal class ChromeScraper : IChromeScraper
	{
		public HtmlDocument ScrapeHtml(Uri url)
		{
			return ScrapeHtml(url.ToString());
		}

		public HtmlDocument ScrapeHtml(string url)
		{
			HtmlDocument doc = new();

			using (PhantomJSDriverService driverService = PhantomJSDriverService.CreateDefaultService())
			{
				driverService.HideCommandPromptWindow = true;
				driverService.LoadImages = false;
				driverService.IgnoreSslErrors = true;

				driverService.Start();

				using IWebDriver driver = new ChromeDriver();

				driver.Navigate().GoToUrl(url);

				Thread.Sleep(3000);

				doc.LoadHtml(driver.PageSource);
			}

			return doc;
		}
	}

	public interface IChromeScraper
	{
		HtmlDocument ScrapeHtml(string url);
		HtmlDocument ScrapeHtml(Uri url);
	}
}

Requirements – The following executables must be in /bin

  • phantonjs.exe
  • chrome.exe
  • chromedriver.exe
  • + the chrome portable binaries directory, currently named \107.0.5304.88
<!-- To copy directly to bin without being placed in a sub-folder -->
<ItemGroup>
	<ContentWithTargetPath Include="Assets\phantomjs.exe">
		<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		<TargetPath>phantomjs.exe</TargetPath>
	</ContentWithTargetPath>
	<ContentWithTargetPath Include="Assets\chrome.exe">
		<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		<TargetPath>chrome.exe</TargetPath>
	</ContentWithTargetPath>
	<ContentWithTargetPath Include="Assets\chromedriver.exe">
		<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
		<TargetPath>chromedriver.exe</TargetPath>
	</ContentWithTargetPath>

	<None Include="Assets\phantomjs.exe" />
	<None Include="Assets\chrome.exe" />
	<None Include="Assets\chromedriver.exe" />
</ItemGroup>

I’ve used Chrome portable to avoid having to install Chrome. If Chrome is installed, the chrome.exe steps can probably be skipped.

A Better Generic Repository for Entity Framework

using System.Linq.Expressions;
using Christopher.Snay.Sample.DataAccess.Database;
using Microsoft.EntityFrameworkCore;

namespace Christopher.Snay.Sample.DataAccess.Repositories
{
	internal sealed class Repository<TEntity> : IRepository<TEntity> where TEntity : class
	{
		private readonly SampleDbContext _db;

		public Repository(SampleDbContext db)
		{
			_db = db;
		}

		public TEntity? Get(int id)
		{
			return _db.Set<TEntity>().Find(id);
		}

		public IEnumerable<TEntity> All()
		{
			return _db.Set<TEntity>().AsEnumerable();
		}

		public IEnumerable<TEntity> All<TChild>(
			Expression<Func<TEntity, TChild>> includePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate).AsEnumerable();
		}

		public IEnumerable<TEntity> All<TChild, TGrandChild>(
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate)
				.ThenInclude(thenIncludePredicate).AsEnumerable();
		}

		public TEntity? Select(Expression<Func<TEntity, bool>> predicate)
		{
			return _db.Set<TEntity>().FirstOrDefault(predicate);
		}

		public TEntity? Select<TChild>(Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate).FirstOrDefault(predicate);
		}

		public TEntity? Select<TChild, TGrandChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate)
				.ThenInclude(thenIncludePredicate).FirstOrDefault(predicate);
		}

		public IEnumerable<TEntity> SelecyMany(Expression<Func<TEntity, bool>> predicate)
		{
			return _db.Set<TEntity>().Where(predicate).AsEnumerable();
		}

		public IEnumerable<TEntity> SelecyMany<TChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate).Where(predicate).AsEnumerable();
		}

		public IEnumerable<TEntity> SelecyMany<TChild, TGrandChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate)
		{
			return _db.Set<TEntity>().Include(includePredicate)
				.ThenInclude(thenIncludePredicate).Where(predicate).AsEnumerable();
		}

		public void Insert(TEntity entity)
		{
			_db.Set<TEntity>().Add(entity);
		}

		public void Insert(List<TEntity> entities)
		{
			_db.Set<TEntity>().AddRange(entities);
		}

		public void Update(TEntity entity)
		{
			_db.Set<TEntity>().Update(entity);
		}

		public void Update(List<TEntity> entities)
		{
			_db.Set<TEntity>().UpdateRange(entities);
		}

		public void Delete(TEntity entity)
		{
			_db.Set<TEntity>().Remove(entity);
		}

		public void Delete(List<TEntity> entities)
		{
			_db.Set<TEntity>().RemoveRange(entities);
		}

		public int SaveChanges()
		{
			return _db.SaveChanges();
		}
	}

	public interface IRepository<TEntity> where TEntity : class
	{
		TEntity? Get(int id);

		IEnumerable<TEntity> All();
		IEnumerable<TEntity> All<TChild>(Expression<Func<TEntity, TChild>> includePredicate);
		IEnumerable<TEntity> All<TChild, TGrandChild>(
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate);

		TEntity? Select(Expression<Func<TEntity, bool>> predicate);
		TEntity? Select<TChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate);
		TEntity? Select<TChild, TGrandChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate);

		IEnumerable<TEntity> SelecyMany(Expression<Func<TEntity, bool>> predicate);
		IEnumerable<TEntity> SelecyMany<TChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate);
		IEnumerable<TEntity> SelecyMany<TChild, TGrandChild>(
			Expression<Func<TEntity, bool>> predicate,
			Expression<Func<TEntity, TChild>> includePredicate,
			Expression<Func<TChild, TGrandChild>> thenIncludePredicate);

		void Insert(TEntity entity);
		void Insert(List<TEntity> entities);

		void Update(TEntity entity);
		void Update(List<TEntity> entities);

		void Delete(TEntity entity);
		void Delete(List<TEntity> entities);

		int SaveChanges();
	}
}