.NET Core 2.0 console application does not compile - asp.net-core-2.0

I want to configure a .NET Core 2.0 CONSOLE application. There is one line that won't compile (see my note in the code).
I have checked so many blogs, websites, etc. This seems to be an approved way of configuring a .NET Core application. Am I missing an assembly reference?
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.IO;
namespace fbs.Console
{
class Program
{
public static IConfiguration configuration;
public static IServiceProvider serviceProvider;
static void Main(string[] args)
{
// Create service collection
ConfigureServices();
var dbService = serviceProvider.GetRequiredService<IDbContext>();
}
private static void ConfigureServices()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
configuration = builder.Build();
var appSettings = new AppSettings();
var section = configuration.GetSection("AppSettings");
IServiceCollection services = new ServiceCollection();
services.AddOptions();
// The following statement does NOT compile???
services.Configure<AppSettings>(section);
services.AddSingleton<IDbContext, DbContext>();
serviceProvider = services.BuildServiceProvider();
}
}
public class AppSettings
{
public string Database { get; set; }
}
public interface IDbContext
{
string Database { get; set; }
}
public class DbContext : IDbContext
{
public string Database { get; set; }
public DbContext(IOptions<AppSettings> appSettings)
{
this.Database = appSettings.Value.Database;
}
}
}
Edit:
The compiler error says:
Error CS1503 Argument 2: cannot convert from 'Microsoft.Extensions.Configuration.IConfigurationSection' to 'System.Action<fbs.Console.AppSettings'.
In order to replicate the problem, just create a new .NET Core 2.0 console application, copy&paste my code and try to compile.

Solved:
Indeed I have been missing to add Microsoft.Extensions.Options.ConfigurationExtensions to the project. I wasn't aware that this assembly is necessary because there seems to be no need to add it as "using" clause. At least, my code is now compiling by just adding the package with NuGet (but no changes to the using statements). I am a bit confused.

Related

How to pass IHttpContextAccessor to DotLiquid's custom tag

My ASP.NET Core application is using dotliquid. I am creating my own custom dotliquid tag and I want to inject IHttpContextAccessor in my custom tag. Based on my research i have to create ITagFactory to create tag and inject IHttpContextAccessor.
my custom factory
public class LiquidTagFactory : DotLiquid.ITagFactory
{
private readonly Type _tagType;
private readonly string _tagName;
private readonly IHttpContextAccessor _contextAccessor;
public string TagName { get { return _tagName; } }
public LiquidTagFactory(Type tagType, string tagName, IHttpContextAccessor contextAccessor)
{
_tagType = tagType;
_tagName = tagName;
_contextAccessor = contextAccessor;
}
public Tag Create()
{
return (Tag)Activator.CreateInstance(_tagType, _contextAccessor);
}
}
my custom tag
public class TextBox : DotLiquid.Tag
{
private string _html = null;
private readonly IHttpContextAccessor _contextAccessor;
public TextBox(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public override void Initialize(string tagName, string markup, List<string> tokens)
{
// here i want to store some values into HttpContext.Items
_contextAccessor.HttpContext.Items.Add("key","somedata");
base.Initialize(tagName, markup, tokens);
}
public override void Render(Context context, TextWriter result)
{
_html = CreateHtml();
result.Write(_html);
base.Render(context, result);
}
}
Then i would register this factory as
Template.RegisterTagFactory(new LiquidTagFactory(typeof(TexBox), "textbox", _httpContextAccessor));
However, i am using ASP.NET Core. I am not sure how do i register custom tag factory using .NET Core's dependency injection?
looking at dotliquid's template code it stores the ITagFactory in private static field
private static Dictionary<string, Tuple<ITagFactory, Type>> Tags { get; set;}
In my case, every request will have its own IHttpContextAccessor, and i don't want to create factory for each request
I think i found it. As per SO we can register IHttpContextAccessor singleton.
As of .NET Core 2.1 there is an extension method that has been added to correctly register an IHttpContextAccessor as a singleton. Simply add as follows in your ConfigureServices() method:
services.AddHttpContextAccessor();
This is equivalent to:
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
and then register the factory in Configure() method in startup. Make sure to pass IHttpContextAccessor as parameter
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<DomainOptions> domainOps, IHttpContextAccessor httpContextAccessor)
{
Template.RegisterTagFactory(new LiquidTagFactory(typeof(TextBox), "textbox", httpContextAccessor));
}

accessing Azure Cloud Storage with the new Configuration in ASP.NET Core 2.0

Is there a way to configure my Azure Cloud storage in ConfigureServices of the Startup class, the same way I configure my DatabaseContext?
var connection = Configuration.GetConnectionString("myDatabaseConnectionString");
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(connection));
I know I could access the connection string from within a controller by adding the IConfiguration instance to the service collection as a singleton object in ConfigureServices and then inject IConfiguration in the controller.
But how do I access this in the following class or is this a bad practice and there is a better way (e.g. in the services.Add ...) ?
public class ClassifiedsToProcess
{
public static void AddMessage(string message)
{
var storageConnectionString = Configuration.Get("AppSettings:ConnectionString");
storageAccount = CloudStorageAccount.Parse(storageConnectionString);
...
}
}
Thanks !
Here is how I solved my problem using IOptions as suggested by Martin Brandl
Controller code
private string makeJson;
private MyAzureQueue MyAzureQueue;
public AdminController(IOptions<AzureOptions> optionsAccessor)
{
MyAzureQueue = new MyAzureQueue(optionsAccessor);
}
public IActionResult Index()
{
MyAzureQueue.AddMessage("my message");
return View();
}
MyAzureQueue class
public interface IMessageRepository
{
void AddMessage(string message);
}
public class MyAzureQueue: IMessageRepository
{
private readonly CloudQueue _queue;
public ClassifiedsToProcess(IOptions<AzureOptions> optionsAccessor)
{
CloudStorageAccount storageAccount = AzureStorageHelper.getStorageAccount(optionsAccessor.Value.StorageConnectionString);
_queue = AzureStorageHelper.GetQueue(storageAccount, "mystorageaccount");
}
public void AddMessage(string message)
{
CloudQueueMessage cloudQueueMessage = new CloudQueueMessage(message);
_queue.AddMessageAsync(cloudQueueMessage);
}
}
Azure Options class
public class AzureOptions
{
public AzureOptions()
{
}
public string StorageConnectionString { get; set; }
}
In the Startup.cs ConfigureServices method I added the following two lines
services.AddOptions();
services.Configure<AzureOptions>(Configuration.GetSection("Azure"));
And this is what my appsettings.json looks like
{
"Azure": {
"StorageConnectionString": "DefaultEndpointsProtocol=https;AccountName=mystorageaccount;AccountKey=XXXXXXXXXXXXXXXXXXXXX;EndpointSuffix=core.windows.net"
}
}
You should try to avoid using IConfiguration directly and use the Option pattern in
ASP.NET Core instead.
In your example, I would e. g. implement an IMessageRepository which uses the storage account to persist the messages.
Your ClassifiedsToProcess class would have a constructor dependency to it and your AddMessage method would used it (it shouldn't be static since you want to access the repository).

Load SQL Connection Str in Asp.net Core 2 / EF Core 2

In Asp.Net.Core.v1 in the inherited DbContextClass I loaded connection string from appsettings.json like this:
private IConfigurationRoot _config;
public MainDbContext(IConfigurationRoot config, DbContextOptions options) : base(options)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:SQLConnectionString"]);
}
with all the config changes in v2, this is now a run-time error.
How do I load/use the SQL DB connection string in EFCore.v2 from appsettings.*.json ?
Follow the example at:
https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
in sections
Create the Database Context
and
Register the context with dependency injection
Note: the name "ConnectionStrings" in JSON is significant
do it like this
private IConfigurationRoot _config;
public MainDbContext(IConfigurationRoot config, DbContextOptions options) : base(options)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_config["Data:SQLConnectionString"]);
}
simply remove: base.OnConfiguring(optionsBuilder);

Configure ServiceStack Base URI

I'm creating a self-hosted REST service using service stack & AppHostHttpListenerBase. I'd like to use a base URI for my services (e.g. "api") like so:
http://myserver/api/service1/param
http://myserver/api/service2/param
How do I do this without defining "api" in each of my routes. In IIS, I can set a virtual directory to isolate the services, but how do I do this when self-hosting?
Here ya go.. (as a bonus this is how you put your service into a plugin.
using BlogEngineService;
using ServiceStack.WebHost.Endpoints;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogEngineWinService
{
public class AppHost : AppHostHttpListenerBase
{
public AppHost() : base("Self Host Service", typeof(AppHost).Assembly) { }
public override void Configure(Funq.Container container)
{
Plugins.Add(new BlogEngine());
}
}
}
This is how you autowire it up
The call appHost.Routes.AddFromAssembly2(typeof(HelloService).Assembly); Is what calls the extension to auto wire.
using ServiceStack.WebHost.Endpoints;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ServiceStack.ServiceInterface;
namespace BlogEngineService
{
public class BlogEngine : IPlugin, IPreInitPlugin
{
public void Register(IAppHost appHost)
{
appHost.RegisterService<HelloService>();
appHost.Routes.AddFromAssembly2(typeof(HelloService).Assembly);
}
public void Configure(IAppHost appHost)
{
}
}
}
This is how you mark the Service Class to give it a prefix.
Simply mark the class with this attribute
using ServiceStack.DataAnnotations;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BlogEngineService
{
public class Hello
{
[PrimaryKey]
public string Bob { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
[PrefixedRoute("/test")]
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Bob};
}
}
}
Create a CS file in your project for the extension..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ServiceStack.Common;
using ServiceStack.Common.Utils;
using ServiceStack.Common.Web;
using ServiceStack.Text;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints;
using ServiceStack.ServiceInterface;
namespace ServiceStack.ServiceInterface
{
public static class ServiceRoutesExtensions
{
/// <summary>
/// Scans the supplied Assemblies to infer REST paths and HTTP verbs.
/// </summary>
///<param name="routes">The <see cref="IServiceRoutes"/> instance.</param>
///<param name="assembliesWithServices">
/// The assemblies with REST services.
/// </param>
/// <returns>The same <see cref="IServiceRoutes"/> instance;
/// never <see langword="null"/>.</returns>
public static IServiceRoutes AddFromAssembly2(this IServiceRoutes routes,
params Assembly[] assembliesWithServices)
{
foreach (Assembly assembly in assembliesWithServices)
{
AddNewApiRoutes(routes, assembly);
}
return routes;
}
private static void AddNewApiRoutes(IServiceRoutes routes, Assembly assembly)
{
var services = assembly.GetExportedTypes()
.Where(t => !t.IsAbstract
&& t.HasInterface(typeof(IService)));
foreach (Type service in services)
{
var allServiceActions = service.GetActions();
foreach (var requestDtoActions in allServiceActions.GroupBy(x => x.GetParameters()[0].ParameterType))
{
var requestType = requestDtoActions.Key;
var hasWildcard = requestDtoActions.Any(x => x.Name.EqualsIgnoreCase(ActionContext.AnyAction));
string allowedVerbs = null; //null == All Routes
if (!hasWildcard)
{
var allowedMethods = new List<string>();
foreach (var action in requestDtoActions)
{
allowedMethods.Add(action.Name.ToUpper());
}
if (allowedMethods.Count == 0) continue;
allowedVerbs = string.Join(" ", allowedMethods.ToArray());
}
if (service.HasAttribute<PrefixedRouteAttribute>())
{
string prefix = "";
PrefixedRouteAttribute a = (PrefixedRouteAttribute)Attribute.GetCustomAttribute(service, typeof(PrefixedRouteAttribute));
if (a.HasPrefix())
{
prefix = a.GetPrefix();
}
routes.AddRoute(requestType, allowedVerbs, prefix);
}
else
{
routes.AddRoute(requestType, allowedVerbs);
}
}
}
}
private static void AddRoute(this IServiceRoutes routes, Type requestType, string allowedVerbs, string prefix = "")
{
var newRoutes = new ServiceStack.ServiceHost.ServiceRoutes();
foreach (var strategy in EndpointHost.Config.RouteNamingConventions)
{
strategy(newRoutes, requestType, allowedVerbs);
}
foreach (var item in newRoutes.RestPaths)
{
string path = item.Path;
if (!string.IsNullOrWhiteSpace(prefix))
{
path = prefix + path;
}
routes.Add(requestType, restPath: path, verbs: allowedVerbs);
}
}
}
public class PrefixedRouteAttribute : Attribute
{
private string _prefix { get; set; }
private bool _hasPrefix { get; set; }
public PrefixedRouteAttribute(string path)
{
if (!string.IsNullOrWhiteSpace(path))
{
this._hasPrefix = true;
this._prefix = path;
//this.Path = string.Format("/{0}{1}", Prefix, Path);
}
}
public bool HasPrefix()
{
return this._hasPrefix;
}
public string GetPrefix()
{
return this._prefix;
}
}
}
ServiceStack's HttpListener hosts expects to be hosted a the root / path as the normal use-case is to have each self-hosted service available on different custom ports.
Since it doesn't currently support hosting at a /custompath, you would have to specify /api/ prefix on all your service routes.
Add an issue if you want to see support for hosting at custom paths.
There is actually an easier solution. In your web.config, update your http-handler to:
<httpHandlers>
<add path="api*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
With the above, all of your service apis must be prefixed with a "/api/". If you have already used "/api/" in any of your routes, you must now remove them or have to specify it twice in your calls.
Reference:
https://github.com/ServiceStack/SocialBootstrapApi
I've found a workaround for this. I've only tested this under self hosting.
Create a 'PrefixedRouteAttribute' class that inherits from RouteAttribute
public class PrefixedRouteAttribute : RouteAttribute
{
public static string Prefix { get; set; }
public PrefixedRouteAttribute(string path) :
base(path)
{
SetPrefix();
}
public PrefixedRouteAttribute(string path, string verbs)
: base(path, verbs)
{
SetPrefix();
}
private void SetPrefix()
{
if (!string.IsNullOrWhiteSpace(Prefix))
{
this.Path = string.Format("/{0}{1}", Prefix, Path);
}
}
}
When you create your AppHost you can set your Prefix
PrefixedRouteAttribute.Prefix = "api";
Then instead of using the [Route] attribute, use the [PrefixRoute] attribute on your classes
[PrefixedRoute("/echo")]
[PrefixedRoute("/echo/{Value*}")]
public class Echo
{
[DataMember]
public string Value { get; set; }
}
This will then work for requests to
/api/echo
/api/echo/1
This could possibly be improved. I don't really like the how I need to set the Prefix via the static property but I couldn't think of a better approach under my setup. The principle of creating the overriding attribute seems sound though, and that is the important part.

How to use Classes As List Collection in Azure AppFabric Caching

H,
I am using the Windows Azure AppFabri Caching.
I have two project in asp.ne t .
One to put data in cache and 2nd to read that cache. These Two project are running on 2 different systems.
I have 4 dll included in these projects.
Microsoft.WindowsFabric.Common.dll
Microsoft.WindowsFabric.Data.Common.dll
Microsoft.ApplicationServer.Caching.Client.dll
Microsoft.ApplicationServer.Caching.Core.dll
Poject 1: To insert data in cache is using the following code:Data is saving to cache successfully
Default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.ApplicationServer.Caching;
public partial class Default2 : System.Web.UI.Page
{
public class Employee
{
public string Name { get; set; }
public decimal Salary { get; set; }
}
protected static DataCacheFactory _factory = null;
protected static DataCache _cache = null;
protected void Page_Load(object sender, EventArgs e)
{
PutDataIntoCache();
}
protected void PutDataIntoCache()
{
List<Employee> emp = new List<Employee>();
try
{
emp.Add(new Employee { Name = "James", Salary = 20000 });
PutCacheItem("55", emp);
}
catch (Exception ex)
{
}
}
protected void PutCacheItem(string key, object value)
{
try
{
_cache = GetDefaultCache();
_cache.Put(key, value);
}
catch (Exception ex)
{
}
}
protected static DataCache GetDefaultCache()
{
_factory = new DataCacheFactory();
_cache = _factory.GetDefaultCache();
return _cache;
}
}
Now I am reading the cache in another project that is running on another system.
Code is given below:
default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.ApplicationServer.Caching;
public partial class Default2 : System.Web.UI.Page
{
protected static DataCacheFactory _factory = null;
protected static DataCache _cache = null;
protected void Page_Load(object sender, EventArgs e)
{
GetDataFromCache();
}
protected void GetDataFromCache()
{
_cache = GetDefaultCache();
string key = "55";
var item = _cache.Get(key);// Error is generation here
}
protected static DataCache GetDefaultCache()
{
_factory = new DataCacheFactory();
_cache = _factory.GetDefaultCache();
return _cache;
}
}
An error is generating on the line _cache.Get(key)
The deserializer cannot load the type to deserialize because type 'System.Collections.Generic.List`1[[_Default2+Employee, App_Web_4xzswv4j, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]' could not be found in assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Check that the type being serialized has the same contract as the type being deserialized and the same assembly is used.
Why this error is coming?
How can Can I uses Classes as List collection to add/read in Cache.
I believe the problem is that your second project doesn't define the Employee class. You should really define a class library with Employee in it and reference that same assembly from both projects.
The way you've set these projects up, then I think ASP is compiling these code-behind pages into temporary assemblies - i.e. the Employee class is defined as being in the assembly App_Web_4xzswv4j, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
Instead, you should put these classes into a class library which can be shared between the projects - this will then have a fixed name, rather than a unique name for each compilation.
As an aside, I think you should also mark your Employee as serializable in order to ensure that it can be serialized and deserialized.

Resources