I have gotten a task that contains creating a .Net 4.8 application that contains a "HttpSelfHostServer".
I'm stuck in the quest of assigning "IServiceCollection services" to config.DependencyResolver (of type System.Web.Http.Dependencies.IDependencyResolver)
I would really like not to use autofac or other frameworks, but all guids I can find are pointing toward these frameworks. Isn't Microsoft providing a way through?
I just had to solve the same issue. This is how i did it:
First I created a new facade class to map the IServiceCollection from the host builder to the interface HttpSelfHostConfiguration supports:
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;
using Microsoft.Extensions.DependencyInjection;
namespace IntegrationReceiver.WebApi
{
public class HttpSelfHostDependencyResolver : IDependencyResolver
{
private readonly IServiceProvider sp;
private readonly IServiceScope scope;
public HttpSelfHostDependencyResolver(IServiceProvider sp)
{
this.sp = sp;
this.scope = null;
}
public HttpSelfHostDependencyResolver(IServiceScope scope)
{
this.sp = scope.ServiceProvider;
this.scope = scope;
}
public IDependencyScope BeginScope() => new HttpSelfHostDependencyResolver(sp.CreateScope());
public void Dispose() => scope?.Dispose();
public object GetService(Type serviceType) => sp.GetService(serviceType);
public IEnumerable<object> GetServices(Type serviceType) => sp.GetServices(serviceType);
}
}
This required me to get the latest NuGet package Microsoft.Extensions.DependencyInjection.Abstractions according to an answer here: How do I see all services that a .NET IServiceProvider can provide?
I then registered my HttpSelfHostServer in the service provider with this code:
services.AddSingleton(sp => new HttpSelfHostDependencyResolver(sp));
services.AddSingleton(sp =>
{
//Starting the HttpSelfHostServer with user-level permissions requires to first run a command like
// netsh http add urlacl url=http://+:8080/ user=[DOMAINNAME]\[USERNAME]
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
config.DependencyResolver = sp.GetRequiredService<HttpSelfHostDependencyResolver>();
return new HttpSelfHostServer(config);
});
And finally, to find my ApiController, I had to register that too in the service provider. I did that simply with:
services.AddScoped<HealthCheckController>();
For brewity, I'm just including my api controller below to illustrate how it now gets its dependencies:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
namespace IntegrationReceiver.WebApi
{
public class HealthCheckController : ApiController
{
private readonly ServiceBusRunner serviceBusRunner;
public HealthCheckController(ServiceBusRunner serviceBusRunner)
{
this.serviceBusRunner = serviceBusRunner;
}
[HttpGet]
public async Task<HttpResponseMessage> Get()
{
var response = new
{
serviceBusRunner.RunningTasks,
serviceBusRunner.MaxRunningTasks
};
return await Json(response)
.ExecuteAsync(System.Threading.CancellationToken.None);
}
}
}
This is a pretty dumb-down implementation but works for me until I can upgrade this code to net5.
I hope it helps you too!
On a global level in .NET Core 1.0 (all API responses), how can I configure Startup.cs so that null fields are removed/ignored in JSON responses?
Using Newtonsoft.Json, you can apply the following attribute to a property, but I'd like to avoid having to add it to every single one:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string FieldName { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string OtherName { get; set; }
.NET Core 1.0
In Startup.cs, you can attach JsonOptions to the service collection and set various configurations, including removing null values, there:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings
.NullValueHandling = NullValueHandling.Ignore;
});
}
.NET Core 3.1
Instead of this line:
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
Use:
options.JsonSerializerOptions.IgnoreNullValues = true;
.NET 5.0
Instead of both variants above, use:
options.JsonSerializerOptions.DefaultIgnoreCondition
= JsonIgnoreCondition.WhenWritingNull;
The variant from .NET Core 3.1 still works, but it is marked as NonBrowsable (so you never get the IntelliSense hint about this parameter), so it is very likely that it is going to be obsoleted at some point.
This can also be done per controller in case you don't want to modify the global behavior:
public IActionResult GetSomething()
{
var myObject = GetMyObject();
return new JsonResult(myObject, new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore
});
};
I found that for dotnet core 3 this solves it -
services.AddControllers().AddJsonOptions(options => {
options.JsonSerializerOptions.IgnoreNullValues = true;
});
In net 5, it's actually DefaultIgnoreCondition:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.DefaultIgnoreCondition =
System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
});
}
This will prevent both serializazion and deserialization of any null value without needing any extra attribute on properties.
.Net core 6 with Minimal API:
using Microsoft.AspNetCore.Http.Json;
builder.Services.Configure<JsonOptions>(options =>
options.SerializerOptions.DefaultIgnoreCondition
= JsonIgnoreCondition.WhenWritingDefault | JsonIgnoreCondition.WhenWritingNull);
If you would like to apply this for specific properties and only use System.Text.Json then you can decorate properties like this
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Market { get; set; }
The following works for .NET Core 3.0, in Startup.cs > ConfigureServices():
services.AddMvc()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreNullValues = true;
});
In Asp.Net Core you can also do it in the action method, by returning
return new JsonResult(result, new JsonSerializerOptions
{
IgnoreNullValues = true,
});
In .Net 5 and greater, if you are using AddNewtonsoftJson instead of AddJsonOptions, the setting is as following
services.AddMvc(options =>
{
//any other settings
})
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
});
I used the below in my .net core v3.1 MVC api.
services.AddMvc().AddJsonOptions(options =>
{
options.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
});
One more way in .Net 6, for specific ObjectResult:
public class IdentityErrorResult : BadRequestObjectResult
{
public IdentityErrorResult([ActionResultObjectValue] object? error) : base(error)
{
Formatters.Add(new SystemTextJsonOutputFormatter(new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}));
}
}
in Controller:
public IdentityErrorResult IdentityError(ErrorResponseObject value)
=> new IdentityErrorResult(value);
If you are using .NET 6 and want to get rid of the null values in your REST response, on Program.cs just add the following lines:
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
The code below work for me in .Net core 2.2
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
ASP.NET MVC 5 with EF6 and MySql using Identity 2. I've been doing DBA-like 'bigdata' for quite a few years and now I'm back in the land of code. I understand EF as an architecture but am struggling with my lack of expertise in getting the ground floor laid for my project especially with the MySql wrinkle.
What I have done successfully: Created a project with a MySQLConfiguration, MySqlHistoryContext, and MySqlInitializer, ran a migration to successfully create the tables for Identity, deployed the site to local server and can successfully register a user and log in. GREAT!
Question: I'm lost as far as how to manage two contexts, merge contexts, what ought be my best practice so I can continue to extend Identity objects and add new database tables through POCO models and migrations to the same database and have them work through a context at the controller.
Guidance would be helpful. I believe my issues are in creating the second context which I would prefer use the same connection.
Attached are my class mods from other tutorials and references.
How do I create a new context which would create a new table from a POCO model using a migration??
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
static ApplicationDbContext()
{
Database.SetInitializer(new MySqlInitializer());
}
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Some comments online suggest not needing a MySqlInitializer. I hardcoded the db name so simply "get it working".
public class MySqlInitializer : IDatabaseInitializer<ApplicationDbContext>
{
public void InitializeDatabase(ApplicationDbContext context)
{
if (!context.Database.Exists())
{
context.Database.Create();
}
else
{
var migrationHistoryTableExists = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
string.Format("SELECT COUNT(*) FROM [hardcodeddbname].__MigrationHistory"));
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}
}
public class MySqlConfiguration : DbConfiguration
{
public MySqlConfiguration()
{
SetHistoryContext(
"MySql.Data.MySqlClient", (conn, schema) => new MySqlHistoryContext(conn, schema));
}
}
public class MySqlHistoryContext : HistoryContext
{
public MySqlHistoryContext(
DbConnection existingConnection,
string defaultSchema)
: base(existingConnection, defaultSchema)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<HistoryRow>().Property(h => h.MigrationId).HasMaxLength(100).IsRequired();
modelBuilder.Entity<HistoryRow>().Property(h => h.ContextKey).HasMaxLength(200).IsRequired();
}
}
I cant seem to get the proper service to configure.
The idea is to register all types that have the marker interface IDao and have there base classes be the service.
base class
public abstract class DirectorDaoContract : RepositoryBase<Director>
{
public abstract Director Get(int id);
}
implementing class
public class DirectorDao : DirectorDaoContract,IDao
{
public override Director Get(int directorId)
{
.....
}
}
The Idea being that a
Resolve<DirectorDaoContract>()
would return DirectorDao
my current attempt
container.Register(AllTypes.FromAssemblyNamed(dataAssembly)
.BasedOn<IDao>)
.WithService.Base()
.Configure(c => c.LifeStyle.PerThread));
No matter what I have tried I am unable to get the service correct.
container.Register(AllTypes.FromAssemblyNamed(dataAssembly)
.BasedOn<IDao>()
.WithService.FromInterface()
.Configure(c => c.LifeStyle.PerThread));
try this
container.Register(AllTypes.FromAssemblyNamed(dataAssembly(
.BasedOn(typeof(DirectorDaoContract))
.WithService.Select((t,b) => t.GetInterfaces().Where(d => d == typeof(IDao)).AsEnumerable<Type>());
I'm currently registering a bunch of stuff at one point in my initialisation sequence
Container.Register(AllTypes.FromAssemblyContaining<MyAssembly>()
.BasedOn(typeof(IRepository<>))
.WithService.Self().Configure(c => c.LifeStyle.Transient));
I'm using WithService.Self so that it doesn't automatically pick up AllInterfaces, so that the interface that I will want to add later on as a type forward has not already been added.
I'd like then (later on) to be able to add a type forward to one of the already registered components, and Intellisense on the ForwardedTypes property suggested using .Forward(typof()) e.g.
Container.Register(Component.For<IOtherInterface>()
.Forward(typeof(IOtherInterface))
.ImplementedBy<AlreadyRegisteredType>().LifeStyle.Transient);
Is this possible?
EDIT:
I've been trying to get the stuff that Krzysztof has suggested working so I've generated a test project (below). I've tried various combinations to get the ConfigureFor to forward IMyInterface to MySecondType, but just can't get it to work, when done as a second step after initial registration of my component types. I'm probably being dim, but I'm just not getting how the ConfigureFor command works, and the documentation is a little sketchy (non-existant) on the subject.
namespace TestProject1
{
public class MyType : IMyInterface
{
public virtual string MyProperty { get; set; }
}
public class MySecondType : IMyInterface
{
public virtual string MySecondProperty { get; set; }
}
public interface IMyInterface
{
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
//New Container
var container = new WindsorContainer();
//Register Types
container.Register(
AllTypes.FromAssemblyContaining<MyType>().BasedOn<IMyInterface>().WithService.Self().Configure(
c => c.LifeStyle.Transient));
//Other stuff happens here...
//Now Register our interface as a forward
container.Register(AllTypes.FromAssemblyContaining<IMyInterface>()
.BasedOn<IMyInterface>()
.WithService.Base()
.ConfigureFor<IMyInterface>(r => r.Forward<MySecondType>()).Configure(c => c.LifeStyle.Transient));
var typeA = new MySecondType();
var typeB = container.Resolve<IMyInterface>();
Assert.IsInstanceOfType(typeB.GetType(), typeA.GetType());
}
}
}
What you described in the comment is registering another component for the AlreadyRegisteredType.
If you want to add a forward to the same component use
ConfigureFor<AlreadyRegisteredType>(c => c.Forward<IOtherInterface>())
full example:
Container.Register(AllTypes.FromThisAssembly()
.BasedOn<IEmptyService>()
.WithService.Base()
.ConfigureFor<EmptyServiceComposite>(r => r.Forward<EmptyServiceComposite>()));
Once you registered some type
Container.Register(Component.For<SomeType>());
you can forward some interface to it in this way:
Container.Register(Component.For<ISomeInterface>()
.UsingFactoryMethod<ISomeInterface>(kernel => kernel.Resolve<SomeType>()));