I have .Net Core 3.1 Web Api. I am using System.Text.Json serializer since it became a default for .Net Core 3.x applications. I have set global enum to string converter as follows:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())));
...
}
Enums are converted to string for controller responses (which makes sense since I configure on AddControllers() method). But if I try to manually serialize an object it still serializes enum as int. Sample below:
public class TestClass
{
public void Test()
{
var data = JsonSerializer.Serialize(new MyObject { Enum1 = MyEnum.Value1, Enum2 = MyEnum.Value2 });
}
public class MyObject
{
public MyEnum Enum1 { get; set; }
public MyEnum Enum2 { get; set; }
}
public enum MyEnum
{
Value1 = 1,
Value2 = 2
}
}
OUTPUT:
data [string]: "{\"Enum1\":1,\"Enum2\":2}"
If I add conversion attribute manually then it serializes as desired:
public class MyObject
{
[JsonConverter(typeof(JsonStringEnumConverter))]
public MyEnum Enum1 { get; set; }
public MyEnum Enum2 { get; set; }
}
OUTPUT
data [string]: "{\"Enum1\":\"Value1\",\"Enum2\":2}"
Is there a way to set global json serialization options (enum to string conversion) that would apply for manual serialization as well?
Or maybe I should just always stick to the explicit attributes?
Currently you can't. JsonSerializer being a static class with only purely static methods, default options cannot be set. You can check this open issue on Github. It seems that it has been designed this way for better performance.
As an alternative, you can create your own static class
public static class CustomJsonSerializer
{
private static JsonSerializerOptions serializerSettings =
new JsonSerializerOptions { /* whatever you need */};
public static T Deserialize<T>(this string json)
{
return JsonSerializer.Deserialize<T>(json, serializerSettings );
}
// etc.
}
I managed to get this to work by adding NewtonsoftJson and then register StringEnumConverter:
services
.AddControllers(jsonOptions => jsonOptions.ReturnHttpNotAcceptable = true)
.AddNewtonsoftJson(jsonOptions =>
{
jsonOptions.SerializerSettings.Converters.Add((JsonConverter) new StringEnumConverter());
}
Related
I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});
I have a web method that accepts object
[WebMethod]
public static void GetObject(object data)
{
}
Also, I have 2 classes:
class ConnectionString
{
public string ConnectionString { get; set; }
public DatabaseType DatabaseType { get; set; }
}
class Path
{
public string Path { get; set; }
public bool IsNetwork { get; set; }
}
On client side, using javascript, i defined 2 similar classes as well:
function ConnectionString() {
this.ConnectionString = '';
this.DatabaseType = 0;
};
function Path() {
this.Path = '';
this.IsNetwork = false;
};
Now, according to user decision, he can ether choose to create log in database or file system. When I send data to the method, my object resulted as null. If I create method
for each object, it works. Is there a way to unbox or desirialize from OBJECT type to ?
You need to create two method overloads that each take in one of the possible classes. In the current implementation the engine does not know what classes should be put in the WSDL...
If you are using WCF you could use [KnownType] attribute to specify which classes your method supports.
I want to mock my Repository object in such a way that it can still do actual DB retrieve operations. Only for Saving operations, I wanted to setup to return mock data since I don't want it to save into the DB.
How should I do it?
Thanks.
Maybe you should make your Save operation virtual and override it in a subclass which you use in your tests rather than using Moq?
First of all, your unit tests should never actually go out to the database (it is all right for integration tests, but that is a larger topic). What you want to do is pretty straightforward with Moq, though:
public class MyRepo
{
public virtual string Save(MyClass foo)
{
// perform save...
}
}
public class MyService
{
public MyRepo Repo { get; set; }
public string VerifyAndSave(MyClass foo)
{
// verify foo...
return new Repo.Save(foo);
}
}
public class MyClass()
{
public string SomeData { get; set; }
}
Notice the virtual modifiers on the methods--these are important for Moq to be able to stub them.
In your tests you could then do something like this:
[TestClass]
public class SomeTests
{
private Mock<MyRepo> MockRepo { get; set; }
private MyService Target { get; set; }
[TestInitialize]
public void Setup()
{
MockRepo = new Mock<MyRepo>();
Target = new MyService();
Target.Repo = MockRepo.Object;
}
[TestMethod]
public void MyTest()
{
const string expectedOutput = "SAVED";
MyClass exampleData = new MyClass();
MockRepo.Setup(x => x.Save(It.IsAny<MyClass>())).Returns(expectedOutput);
Target.VerifyAndSave(exampleData);
MockRepo.Verify(x => x.Save(It.IsAny<MyClass>()));
}
}
The chained calls of Setup and Returns in this case would guarantee that the calling method (i.e. VerifyAndSave) would see the value that you specified--"SAVED" in this case.
For more examples, take a look at the Moq quickstart docs.
I am trying to test a property that is nested in a child class.
I always get an error.
Am I missing something?
Is it possible to test a child property in moq.
I have the following
[Test]
public void Should_be_able_to_test_orderCollection()
{
var orderViewMock = new Mock<IOrderView>();
orderViewMock.SetupGet(o => o.Customer.OrderDataCollection.Count).Returns(2);
orderViewMock.SetupSet(o => o.Customer.OrderDataCollection[1].OrderId = 1);
orderViewMock.VerifySet(o => o.Customer.OrderDataCollection[1].OrderId=1);
}
public class CustomerTestHelper
{
public static CustomerInfo GetCustomer()
{
return new CustomerInfo
{
OrderDataCollection = new OrderCollection
{
new Order {OrderId = 1},
new Order {OrderId = 2}
}
};
}
}
public class CustomerInfo
{
public OrderCollection OrderDataCollection { get; set; }
}
public class OrderCollection:List<Order>
{
}
public class Order
{
public int OrderId { get; set; }
}
public interface IOrderView
{
CustomerInfo Customer { get; set; }
}
You can't mock the OrderDataCollection property of CustomerInfo because it's a non-virtual property on a concrete class.
The best way to fix this would be to extract an interface from CustomerInfo and let IOrderView return that instead:
public interface IOrderView
{
ICustomerInfo Customer { get; set; }
}
It is definitely possible if you have the right abstractions. You need to mock your Customer and its children too, for your example to work, like:
var customerMock = new Mock<ICustomer>();
orderViewMock.SetupGet(o => o.Customer).Returns(customerMock.Object);
etc. for the entire hierarchy of child objects you want to control with mocks. Hope it makes sense.
/Klaus
You will get a runtime error, as you've found:
System.ArgumentException: Invalid setup on a non-overridable member:
o => o.Customer.OrderDataCollection.Count
at Moq.Mock.ThrowIfCantOverride(Expression setup, MethodInfo methodInfo)
You can mock the IOrderView and return any CustomerInfo instance you want, but you're also trying to mock CustomerInfo and OrderCollection. As Mark Seemann mentioned, you can only mock interfaces and virtual properties/methods. This will hold true for almost any mocking/isolation framework except for Typemock (commercial).
As others have already stated, one way to solve the problem is to return an interface for the customer.
We've been experimenting with StructureMap, and I'm having trouble grasping how to handle situations where a single interface has multiple implementations. The code below shows an example where we have two databases that are both accessible from a single service.
public class SomeController : Controller
{
private ISomeService _service;
private IClientRepository _repository;
protected IContext _masterContext;
protected IContext _clientContext;
public SomeController(ISomeService service, ISomeRepository repository
, IContext masterCon, IContext clientCon)
{
_service = service;
_repository = repository;
_masterContext = masterCon;
_clientContext = clientCon;
}
}
public class SomeService : ISomeService
{
private IContext _masterContext;
private IContext _clientContext;
public SomeService(IContext masterContext, IContext clientContext)
{
masterContext = _masterContext;
clientContext = _clientContext;
}
}
public class ClientRepository : IClientRepository
{
private IContext _clientContext;
public ClientRepository(IContext clientContext)
{
_clientContext = clientContext;
}
}
public class MasterContext : IContext
{
public MasterContext(String connString)
//<snip, snip> implement 3rd party data context
}
public class ClientContext : IContext
{
public ClientContext(String connString)
//<snip, snip> implement 3rd party data context
}
StructureMap worked GREAT when we had a single context (database), but how do I tell it how to resolve the 2nd? Note: in most situations we wouldn't have a service handling 2 databases (but may have a controller handling 2 connections, i.e. 2 repositories accessing 2 different databases), but it still doesn't seem to make it easier.
I'm half ready to just give up on using an IoC framework and go back to poor man's DI.
Is it not possible to have an IClientContext and an IMasterContext, possibly inheriting from IContext. My feeling is that the code would be doing one of two very different things depending on whether you were talking to the 'Master' or 'Client' database.
In Unity you can have named registrations, allowing you to effectively register more than a class for a given interface. So you could do (typing by heart, check the actual Unity documentation if interested):
container.RegisterType<IContext, MasterContext>("Master");
container.RegisterType<IContext, ClientContext>("Client");
and then the constructor for SomeService would be:
public SomeService(
[Dependency("Master")]IContext masterContext,
[Dependency("Client")]IContext clientContext)
{
//...
}
The drawback is that in this way your service class is no longer independent of the DI framework used, but depending on the project that may be ok.
This can be a little difficult if you're relying on StructureMap to resolve the dependencies automatically. The first solution (and what I'd err towards) is to make use of marker interfaces like Richard mentions in his answer then just register them. You can then explicitly specify whether you want your client or master context there.
The second way is to make use of named registrations, then specify the constructor params explicitly.
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
});
ForRequestedType<SomeController>().TheDefault.Is.ConstructedBy(
i => new SomeController(i.GetInstance<ISomeService>(),
i.GetInstance<IClientRepository>(),
i.GetInstance<IContext>("Master"),
i.GetInstance<IContext>("Client")));
Not particularly nice but it does the job and ultimately if it's only in one or two places it might be OK.
If you want to resolve differently on namespace / assembly you could try something like this:-
ForRequestedType<IContext>().AddInstances(
i => {
i.OfConcreteType<ClientContext>().WithName("Client");
i.OfConcreteType<MasterContext>().WithName("Master");
}).TheDefault.Is.Conditional(c => {
c.If(con => con.ParentType.Namespace.EndsWith("Client"))
.ThenIt.Is.TheInstanceNamed("Client");
c.If(con => con.ParentType.Namespace.EndsWith("Master"))
.ThenIt.Is.TheInstanceNamed("Master");
c.TheDefault.Is.OfConcreteType<ClientContext>();
});
Where the predicate on ParentType can refer to Assembly (or whatever you want really)
In case someone stumble in this problem, you can achieve it using factory pattern.
Service extension
public static class ServiceFactoryExtensions
{
public static void RegisterSqlFactory(this IServiceCollection serviceCollection)
{
serviceCollection.Configure<MsSqlOption>(option => option.ConnectionString = "Mssql connection string");
serviceCollection.Configure<MySqlOption>(option => option.ConnectionString = "Mysql connection string");
serviceCollection.Configure<PostgreOption>(option => option.ConnectionString = "Postgrel connection string");
serviceCollection.AddSingleton<ISqlDatabase, MsSql>();
serviceCollection.AddSingleton<ISqlDatabase, Postgre>();
serviceCollection.AddSingleton<ISqlDatabase, MySql>();
serviceCollection.AddSingleton<Func<IEnumerable<ISqlDatabase>>>(serviceProvider => () => serviceProvider.GetService<IEnumerable<ISqlDatabase>>());
serviceCollection.AddSingleton<ISqlDatabaseFactory, SqlDatabaseFactory>();
}
}
Factory class
public class SqlDatabaseFactory : ISqlDatabaseFactory
{
private readonly Func<IEnumerable<ISqlDatabase>> _factory;
public SqlDatabaseFactory(Func<IEnumerable<ISqlDatabase>> factory)
{
_factory = factory;
}
public ISqlDatabase CreateSql(SqlType sqlType)
{
var databases = _factory();
var sqlDatabase = databases.FirstOrDefault(x => x.DatabaseName == sqlType);
if (sqlDatabase == null)
throw new NotImplementedException($"Sql type {nameof(sqlType)} is not implemented");
return sqlDatabase;
}
}
Sql classes
public class MsSql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MsSql;
public string Connecionstring { get; private set; }
public MsSql(IOptions<MsSqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class Postgre : ISqlDatabase
{
public SqlType DatabaseName => SqlType.Postgre;
public string Connecionstring { get; private set; }
public Postgre(IOptions<PostgreOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public class MySql : ISqlDatabase
{
public SqlType DatabaseName => SqlType.MySql;
public string Connecionstring { get; private set; }
public MySql(IOptions<MySqlOption> option)
{
Connecionstring = option.Value.ConnectionString;
}
}
public interface ISqlDatabase
{
string Connecionstring { get; }
SqlType DatabaseName { get; }
}
public enum SqlType
{
MsSql,
Postgre,
MySql
}
Usage
internal class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.RegisterSqlFactory();
var provider = serviceCollection.BuildServiceProvider();
var sqlFactory = provider.GetService<ISqlDatabaseFactory>();
var mySql = sqlFactory.CreateSql(SqlType.MySql);
var msSql = sqlFactory.CreateSql(SqlType.MsSql);
var postgre = sqlFactory.CreateSql(SqlType.Postgre);
Console.WriteLine($"Database Type : {mySql.DatabaseName}, Connectionstring: {mySql.Connecionstring}");
Console.WriteLine($"Database Type : {msSql.DatabaseName}, Connectionstring: {msSql.Connecionstring}");
Console.WriteLine($"Database Type : {postgre.DatabaseName}, Connectionstring: {postgre.Connecionstring}");
Console.ReadKey();
}
}
Output
Dependencies:
.Net Core 3.1
Microsoft.Extensions.DependencyInjection;
Microsoft.Extensions.Options;
System
System.Collections.Generic
System.Linq;