How can I fix a deprecated user in Symfony 4? - symfony

I get the following error message:
User Deprecated: Passing configuration options directly to the
constructor is deprecated since Symfony 4.2, use the default context
instead.
This is the code Symfony is giving as trace for the problem:
$serializer = new Serializer(array(new DateTimeNormalizer('d.m.Y'), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
But I do not understand how to use a default context

You have to use directly the service.
class DefaultController extends AbstractController
{
public function index(SerializerInterface $serializer)
{
// keep reading for usage examples
}
}
https://symfony.com/doc/current/serializer.html

According to the documentation you have to use it like this:
-$serializer = new Serializer(array(new DateTimeNormalizer('d.m.Y'), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
-$serializer->serialize($myObject, 'json')
+$serializer = new Serializer(array(new DateTimeNormalizer(), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
+$serializer->serialize($myObject, 'json', [DateTimeNormalizer::FORMAT_KEY => 'd.m.Y'])

I checked out the code that caused the deprecation (the constructor of the DateTimeNormalizer class):
public function __construct($defaultContext = [], \DateTimeZone $timezone = null)
{
$this->defaultContext = [
self::FORMAT_KEY => \DateTime::RFC3339,
self::TIMEZONE_KEY => null,
];
if (!\is_array($defaultContext)) {
#trigger_error('Passing configuration options directly to the constructor is deprecated since Symfony 4.2, use the default context instead.', E_USER_DEPRECATED);
$defaultContext = [self::FORMAT_KEY => (string) $defaultContext];
$defaultContext[self::TIMEZONE_KEY] = $timezone;
}
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
}
So basically it comes down to changing the parameters passed to DateTimeNormalizer (in my case I had a CompilerPass that did set the parameter, but in the example below I will keep it generic):
new DateTimeNormalizer('d.m.Y'); // old, causing the deprecation notice
// new and NOT causing the deprecation notice
new DateTimeNormalizer([
DateTimeNormalizer::FORMAT_KEY => 'd.m.Y'
]);

Related

System.Text.JSON is serializing my property names with a lower case leading character [duplicate]

In ASP.NET Core 3.0 Web API project, how do you specify System.Text.Json serialization options to serialize/deserialize Pascal Case properties to Camel Case and vice versa automatically?
Given a model with Pascal Case properties such as:
public class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
And code to use System.Text.Json to deserialize a JSON string to type of Person class:
var json = "{\"firstname\":\"John\",\"lastname\":\"Smith\"}";
var person = JsonSerializer.Deserialize<Person>(json);
Does not successfully deserialize unless JsonPropertyName is used with each property like:
public class Person
{
[JsonPropertyName("firstname")]
public string Firstname { get; set; }
[JsonPropertyName("lastname")]
public string Lastname { get; set; }
}
I tried the following in startup.cs, but it did not help in terms of still needing JsonPropertyName:
services.AddMvc().AddJsonOptions(options =>
{
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
// also the following given it's a Web API project
services.AddControllers().AddJsonOptions(options => {
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
How can you set Camel Case serialize/deserialize in ASP.NET Core 3.0 using the new System.Text.Json namespace?
Thanks!
AddJsonOptions() would config System.Text.Json only for MVC. If you want to use JsonSerializer in your own code you should pass the config to it.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
var json = "{\"firstname\":\"John\",\"lastname\":\"Smith\"}";
var person = JsonSerializer.Parse<Person>(json, options);
If you want camelCase serialization use this code in Startup.cs: (for example firstName)
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
If you want PascalCase serialization use this code in Startup.cs: (for example FirstName)
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy= null;
);
In startup.cs:
// keeps the casing to that of the model when serializing to json
// (default is converting to camelCase)
services.AddMvc()
.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
This means you don't need to import newtonsoft.json.
The only other option for options.JsonSerializerOptions.PropertyNamingPolicy is JsonNamingPolicy.CamelCase. There do not seem to be any other JsonNamingPolicy naming policy options, such as snake_case or PascalCase.
You can use PropertyNameCaseInsensitive. You need to pass it as a parameter to the deserializer.
var json = "{\"firstname\":\"John\",\"lastname\":\"Smith\"}";
var options = new JsonSerializerOptions() { PropertyNameCaseInsensitive = true };
var person = JsonSerializer.Deserialize<Person>(json, options);
which (from the docs):
Gets or sets a value that determines whether a property's name uses a
case-insensitive comparison during deserialization. The default value
is false
So, it doesn't specify camelCase or PascalCase but it will use case-insensitive comparison.
The below will configure System.Text.Json for Json passed through a controller endpoint:
services.AddControllers()
.AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
You can still set it application wide by installing Microsoft.AspNetCore.Mvc.NewtonsoftJson Nuget Package, which allows you to use the previous Json serializer implementation :
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
Credits to Poke, answer found here :
Where did IMvcBuilder AddJsonOptions go in .Net Core 3.0?
.NET Core 7 Minimal API solution
To prevent pascal case properties from being renamed to camel case during serialization, use the ConfigureHttpJsonOptions method of the builder's service property.
builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = null);
To force conversion to camel case (default behavior), use:
builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase);
Try this!
In StartUp.cs inside the ConfigureServices method write:
services.AddMvc()
.AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNamingPolicy
= JsonNamingPolicy.CamelCase);
You need namespaces such as Newtonsoft.Json.Serialization & System.Text.Json

.Net Core dependency injection manually dispose object created using the implementationFactory Func

Is there a way to dispose objects after creating them using the implementationFactory? Like so:
services.AddTransient(x => {
var objectA = new ObjectA(); //objectA needs to be disposed after the use of objectB
return objectA.ObjectB;
});
I think this is the only proper way right?
services.AddTransient(x => new ObjectA());
services.AddTransient(x => {
var objectA = x.GetService<ObjectA>();
return objectA.ObjectB;
});

Controller unit test in slim3

At the outset, I would like to say - I'm new in unit testing in PHP (phpunit).
In my new project (slim3 framework) I would like to test my controllers for example LoginController.
My idea is (in unit test method)
Create instance of LoginController
Mock some services in controller (DI)
Execute method which is response for request (in my controllers method __invoke)
My problem is about parameters for __invoke method.
In Slim3 callable method for request has two first params:
RequestInterface $request and ResponseInterface $response
How can I create this parameters in my unit test class? I was searching for some examples for this issue but without success.
Any suggestions?
I've found some code in Slim3 tests to mock request:
protected function requestFactory()
{
$uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123');
$headers = new Headers();
$cookies = array(
'user' => 'john',
'id' => '123',
);
$env = Slim\Http\Environment::mock();
$serverParams = $env->all();
$body = new Body(fopen('php://temp', 'r+'));
$request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body);
return $request;
}
But I'm not sure that is good way.
Thanks for any help
I wrote up one solution here: https://akrabat.com/testing-slim-framework-actions/
I use Environment::mock() to create a $request and then I can run the action. Making each route callable a class where all dependencies are injected into the constructor makes this all much easier too.
Essentially, a test looks like this:
class EchoActionTest extends \PHPUnit_Framework_TestCase
{
public function testGetRequestReturnsEcho()
{
// instantiate action
$action = new \App\Action\EchoAction();
// We need a request and response object to invoke the action
$environment = \Slim\Http\Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'QUERY_STRING'=>'foo=bar']
);
$request = \Slim\Http\Request::createFromEnvironment($environment);
$response = new \Slim\Http\Response();
// run the controller action and test it
$response = $action($request, $response, []);
$this->assertSame((string)$response->getBody(), '{"foo":"bar"}');
}
}

An expression tree may not contain a call or invocation that uses optional arguments in moq framework

having trouble with moq after updating project which based on .netcore beta 5 and updated to rc1. Before updating, test was worked and now I have error: "An expression tree may not contain a call or invocation that uses optional arguments". Help me please to solve it.
[Fact]
public async void ReturnPostUserAnswerResult()
{
// Arrange
var mockSet = new Mock<DbSet<QuestionsToUsers>>();
var _applicationDbContext = new Mock<ApplicationDbContext>();
_applicationDbContext.Setup(a => a.QuestionToUsers).Returns(mockSet.Object);
var controller = new UserQuizController(_applicationDbContext.Object);
// Act
var result = new QuestionsToUsers()
{
Answer = true,
QuestionID = 99,
TestpackID = 99,
Id = 99,
UserID = "TestUser"
};
await controller.PostUserAnswerResult(result);
mockSet.Verify(m => m.Add(It.IsAny<QuestionsToUsers>())); //here error
}
It has been for a while after question was asked but today I encountered with the same issue.
While I testing masstransit consumer, I need to verify ConsumeContext<> .Defer method.
I just changed
_mockConsumeContext.Verify(cc => cc.Defer(It.IsAny<TimeSpan>()), Times.Once);
to
_mockConsumeContext.VerifySet(cc => cc.Defer(It.IsAny<TimeSpan>()), Times.Once);
It solved my problem, may be can solve yours too.

Unit Test hitting UrlHelper extension method always fails to match route based on route name

... but the same extension method works when the application itself is executing. The UrlHelper extension method itself looks like this:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category", new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = seoCategoryName }, "http");
}
I register that particular route like this in my SoAndSoAreaRegistration class:
context.MapRoute(
"Category",
"category/{seoCategoryName}",
new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = string.Empty }
);
... and I have dropped a breakpoint on that registration to ensure that it gets hit by the test runner, and it does.
When I run the test, I get an ArgumentException, "A route named 'Category' could not be found in the route collection. Parameter name: name".
My guess is that we do not need to specify the route name and enough route parameters (area/controller/action/category name) to construct the route in its entirety as we're doing here, but I can't figure out where the route name disappears to during testing. Removing the category name eliminates the exception and allows the test to pass, but I would still like to understand where the route name disappears to when I'm testing. Simplifying the code like so still blows up at runtime:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category");
}
If I dig through the route collection at runtime, I can find the category route, but there is no evidence of a .Name property, nor do I see the route's name ("Category" with a capital C) anywhere among the UrlHelper's properties (apologies for the goofy obfuscation; better safe than sorry):
Does anyone know how I can write unit tests which hit UrlHelper extension methods which reference routes by their name? Thanks!
Update -
I'll add some of the test initialization, most of which I got from this popular question, lightly modified to account for the fact that the application I'm working with is separated into multiple MVC areas:
private SoAndSoController CreateController()
{
var service = new Mock();
var cookieMgr = new Mock();
var logger = new Mock();
var allRoutes = new RouteCollection();
MvcApplication.RegisterRoutes(allRoutes);
var soAndSoAreaRegistration = new SoAndSoAreaRegistration();
var soAndSoAreaRegistrationContext = new AreaRegistrationContext(soAndSoAreaRegistration.AreaName, new RouteCollection());
soAndSoAreaRegistration.RegisterArea(soAndSoAreaRegistrationContext);
soAndSoAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
var request = new Mock<HttpRequestBase>();
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());
var response = new Mock<HttpResponseBase>();
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
var controller = new SoAndSoController(service.Object, cookieMgr.Object, null, logger.Object, null);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), allRoutes);
return controller;
}
I figured it out. I needed to pass my RouteCollection into the AreaRegistrationContext, rather than passing it a new RouteCollection:
var productFindingAreaRegistrationContext = new AreaRegistrationContext(productFindingAreaRegistration.AreaName, allRoutes);
But that was causing this line to blow up:
productFindingAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
However now this line was no longer needed, so I commented it out. Voila.

Resources