How to tell DocumentDB SDK to use camelCase during linq query? - azure-cosmosdb

Considering the document { "userName": "user1" } stored in the User collection, and the following User class:
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
}
With the following JSON.net settings:
JsonConvert.DefaultSettings = () =>
{
return new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
};
When I query with Linq as such:
var t = _client.CreateDocumentQuery<User>(_collection.SelfLink)
.Where(u => u.UserName == "user1").AsDocumentQuery().ExecuteNextAsync();
t.Wait();
var users = t.Result;
var user = users.FirstOrDefault();
user is null. Changing the Document to have a pascal casing or the POCO to use a camel casing solves the issue. Of course I do not want any of those as I want my JSON objects and C# objects to be "standarized".
How can I tell the DocumentDB SDK to map my object's property names using camel casing, similar as JSON.net?

The DocumentDB LINQ provider does not pick up the JsonConvert.DefaultSettings. In general you can use the DefaultSettings to control camelCase, but for those properties you wish to use in a LINQ Where clause must have the name explicitly set using JsonProperty attribute on your DTO.
public class User
{
public string Id { get; set; }
[JsonProperty("userName")]
public string UserName { get; set; }
}
Although a bit tedious and a good source for bugs, it seems to be your only option for now.

In a similar case with Cosmos DB, I was able to set all properties to Camel case for my objects at the class declaration level, as in:
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
}
This is how you tell NewtonSoft.Json to use Camel case for serializing.

In newer SDK's you can control the linq serialization in the following way:
container.GetItemLinqQueryable<T>(
linqSerializerOptions: new CosmosLinqSerializerOptions
{
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
});
Where container is an Microsoft.Azure.Cosmos.Container.

Related

.NET 5.0 Web API won't work with record featuring required properties

I'm using a C# 9.0 record type as a binding model for a .NET 5.0 Web API project. Some of the properties are required.
I'm using the record positional syntax, but am receiving errors.
public record Mail(
System.Guid? Id,
[property: Required]
string From,
[property: Required]
string[] Tos,
[property: Required]
string Subject,
string[]? Ccs,
string[]? Bccs,
[property: Required]
Content[] Contents,
Attachment[]? Attachments
);
This is then exposed as the binding model for my Index action:
public async Task<ActionResult> Index(Service.Models.Mail mailRequest)
{
…
}
Whenever I try to make a request, however, I receive the following error:
Record type 'Service.Models.Mail' has validation metadata defined on property 'Contents' that will be ignored. 'Contents' is a parameter in the record primary constructor and validation metadata must be associated with the constructor parameter.
I tried removing the attribute on the Contents property, but it then fails for the next (prior) property. I tried using [param: …] instead of [property: …], as well as mixing them, but keep getting the same kind of error.
I looked around the web, and haven't found any suggestion of handling annotations differently for C# 9 records. I did my best, but I'm out of ideas—outside of converting my records to POCOs.
I gave up using Positional constructor, and with the verbose full declaration of the properties, it works.
public record Mail
{
public System.Guid? Id { get; init; }
[Required]
public string From { get; init; }
[Required]
public string[] Tos { get; init; }
[Required]
public string Subject { get; init; }
public string[]? Ccs { get; init; }
public string[]? Bccs { get; init; }
[Required]
public Content[] Contents { get; init; }
public Attachment[]? Attachments { get; init; }
public Status? Status { get; init; }
public Mail(Guid? id, string #from, string[] tos, string subject, string[]? ccs, string[]? bccs, Content[] contents, Attachment[]? attachments, Status status)
{
Id = id;
From = #from;
Tos = tos;
Subject = subject;
Ccs = ccs;
Bccs = bccs;
Contents = contents;
Attachments = attachments;
Status = status;
}
}
Try using only [Required] (instead of [property: Required]), for some reason worked for me
For me it started to work by adding the [ApiController] attribute to the controller.
I found something similar on ASP.NET Core Razor pages getting:
InvalidOperationException: Record type 'WebApplication1.Pages.LoginModelNRTB+InputModel' has validation metadata defined on property 'PasswordB' that will be ignored. 'PasswordB' is a parameter in the record primary constructor and validation metadata must be associated with the constructor parameter.
from
Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.ThrowIfRecordTypeHasValidationOnProperties()
After some digging, I found: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ModelBinding/Validation/DefaultComplexObjectValidationStrategy.cs
So maybe as you've done, the verbose declaration is the way forward.
Positional record attributes in ASP.NET Core background
How do I target attributes for a record class? more background
Using FluentValidation and keeping properties with the full declaration seems to work perfectly in my case. I highly recommend trying this highly polished alternative validation library instead of using the pretty old standard data annotations
public record LoginViewModel
{
public string Mail { get; init; }
public string Password { get; init; }
public bool IsPersistent { get; init; }
}
public class LoginValidator : AbstractValidator<LoginViewModel>
{
public LoginValidator()
{
RuleFor(l => l.Mail).NotEmpty().EmailAddress();
RuleFor(l => l.Password).NotEmpty();
}
}

How sensitive is the Google OpenID Discovery Document to change?

What I am trying to do
I am trying to implement Google OpenID Connect as a means to login to an ASP.NET Core 3.1 website using Google's instructions:
https://developers.google.com/identity/protocols/oauth2/openid-connect#server-flow
Under step 2 of the server flow (Send an authentication request to Google) they recommend retrieving information from their OpenID Discovery Document:
You should retrieve the base URI from the Discovery document using the authorization_endpoint metadata value.
I am currently trying to dynamically deserialize the JSON to a Dictionary<string, string> by using Newtonsoft.Json. But it is giving me some issues (can't seem to deserialize a JSON string array) and I am considering changing my strategy to creating a model for the Discovery Document and using System.Text.Json to deserialize.
Now my question is
How sensitive is Google's Discovery Document to changes that would lead to me having to update my DiscoveryDocument.cs model?
Dilemma
With the Newtonsoft.Json way everything will still work, even if Google decides to remove a random key.
But using the System.Text.Json is the easy way out for me now and removes a dependency on the Newtonsoft library, though I may run into trouble later if Google's Discovery Document changes.
I think you will have a much easier time to use the Microsoft.IdentityModel.Protocols and
Microsoft.IdentityModel.Protocols.OpenIdConnect NuGet packages and use the included parser to do it all for you. The items in the document is pretty standardized but not every provider provides all the items.
public class OpenIDSettings : IOpenIDSettings
{
public string Issuer { get; }
public string jwks_uri { get; }
public string authorization_endpoint { get; }
public string token_endpoint { get; }
public string userinfo_endpoint { get; }
public string end_session_endpoint { get; }
public string check_session_iframe { get; }
public string revocation_endpoint { get; }
public string introspection_endpoint { get; }
public string device_authorization_endpoint { get; }
public ICollection<string> scopes_supported { get; }
public ICollection<string> claims_supported { get; }
public OpenIDSettings(string endpoint)
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
$"{endpoint}/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
//If you get an exception here, then provider is not running or reachable
var document = configurationManager.GetConfigurationAsync().Result;
//Add the necessary code to populate the properties in this class
Issuer = document.Issuer;
jwks_uri = document.JwksUri;
authorization_endpoint = document.AuthorizationEndpoint;
token_endpoint = document.TokenEndpoint;
userinfo_endpoint = document.UserInfoEndpoint;
end_session_endpoint = document.EndSessionEndpoint;
check_session_iframe = document.CheckSessionIframe;
scopes_supported = document.ScopesSupported;
claims_supported = document.ClaimsSupported;
if (document.AdditionalData.ContainsKey("revocation_endpoint"))
revocation_endpoint = (string)(document.AdditionalData["revocation_endpoint"]);
if (document.AdditionalData.ContainsKey("introspection_endpoint"))
introspection_endpoint = (string)(document.AdditionalData["introspection_endpoint"]);
if (document.AdditionalData.ContainsKey("device_authorization_endpoint"))
device_authorization_endpoint = (string)(document.AdditionalData["device_authorization_endpoint"]);
}
}

How to specify default property values for owned entity types in Entity Framework Core 2.0?

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.
});

ApplicationUser has a list of ApplicationUser

I have built a new Web Application that uses the template Visual Studio provides and included MVC and Web API. The default authorization mechanism is Identity and the database interaction is done using Entity Framework with Code-first method of creating the database.
I have three requirements:
A user can have a list of Children objects
I do not want to use a "relationship" object
All users already exist on the AspNetUsers table, because they all need to be able to login, so I do not want another table to maintain user data
In theory, multiple parents could have reference to multiple children, but for this example, we will just consider it a one-to-many relationship.
In my application, I need to have an ApplicationUser have a list of ChildUsers as a collection of ApplicationUser such as shown below.
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
public string ShirtSize { get; set; }
public ICollection<ApplicationUser> Children { get; set; }
}
I want these users to be accessible as shown above (a collection of ApplicationUser), not a collection of Relationship object that ties them together such as:
public class Relationship
{
public String ParentId { get;set; }
public String ChildId { get;set; }
}
Can a new table be created and exist on the database without having a code-first model for it to know how to create a relationship table?
What are available solutions to this problem?
After some research, and experimentation, I have found bits and pieces of guidance to arrive at a solution that works.
In order for an intermediate table to be created to maintain the relationship, the ApplicationDbContext OnModelCreating function needs to know what it should look like. I have told it to create a new table that is not bound to an object by using the modelBuilder shown in the code below. Unfortunately, I do not have the links to the articles that guided me to this.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base( "DefaultConnection", throwIfV1Schema: false )
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
base.OnModelCreating( modelBuilder );
modelBuilder.Entity<ApplicationUser>()
.HasMany( p => p.ChildUsers )
.WithMany()
.Map( m =>
{
m.MapLeftKey( "Father_Id" );
m.MapRightKey( "Son_Id" );
m.ToTable( "father_son_relation" );
} );
}
}
Additionally, when you need to add Children to the parent ApplicationUser, you will need to do some tweaking as you are about to insert so that it updates the database correctly. I definitely want the UserManager to do the creation of the user for me, but that means that when I go to add the user to my list of Children with the code below, it tries to add it again and throws an exception because it already exists.
var result = await UserManager.CreateAsync( user, model.Password );
var myUserId = User.Identity.GetUserId();
var users = AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.ChildUsers );
var u2 = users.First();
u2.ChildUsers.Add( user );
await AppDbContext.SaveChangesAsync();
After finding this question, I researched the EntityStates and found that adding the following line before calling SaveChanges resolved the exception and it no longer attempts to add it again.
AppDbContext.Entry( user ).State = EntityState.Unchanged;
TADA!!! Now to select them from the database using EF, you can then use the following code:
AppDbContext.Users.Where( u => u.Id == myUserId ).Include( u => u.Children ).First();
Since I am only getting one level of Children this will work ok, after that you risk circular references.
Comments and ideas to improve the code are welcome.

Interface implementing interface as a View model in ASP.NET MVC 4

I have an Interface IBasicData that implements IVeryBasicData for users information (inherited code):
public interface IBasicData:IVeryBasicData
{
byte[] Password { get; set; }
string Email { get; set; }
}
public interface IVeryBasicData:IUser
{
string Name { get; set; }
string UserId { get; set; }
string Description { get; set; }
string Url { get; set; }
}
public interface IUser
{
DateTime CreationTime { get; set; }
string PartitionKey { get; set; }
string RowKey { get; set; }
}
Then I have a method GetUsers from UsersDataSource that returns an IQueryable<IBasicData>, I want to be able to use this IQueryable as the model for a View. But when I try to do it an Exception comes out: the properties cannot be found when calling them in #Hml.DisplayNameFor(model => model.UserId) for example.
So, I've come with this solution:
foreach (var user in usersDataSource.GetUsers())
{
var addUser = new UserViewModel()
{
CreationTime = user.CreationTime,
Description = user.Description,
Email = user.Email,
Name = user.Name,
PartitionKey = user.PartitionKey,
Password = user.Password,
RowKey = user.RowKey,
Url = user.Url,
UserId = user.UserId
};
usersViewModel.Add(addUser);
}
return View(usersViewModel);
UserViewModel is a class implementing IBasicData. This works, but seems rather ugly to me. Is there a better way to be able to use an IQuaryable<IBasicData> as the View model?
Update your view to take an IEnumerable in the model declaration first.
View
#model IEnumerable<IBasicData>
#{foreach(var user in Model){
//I don't know how you are wanting to render out the data...example only
#user.Email<br/>
#user.Name
}}
Then, in your controller, you should be able to do the following:
var modelData = usersDataSource.GetUsers().Select(user=>new UserViewModel{
CreationTime = user.CreationTime,
Description = user.Description,
Email = user.Email,
Name = user.Name,
PartitionKey = user.PartitionKey,
Password = user.Password,
RowKey = user.RowKey,
Url = user.Url,
UserId = user.UserId
}).ToList();
return View(modelData );
Calling .ToList() causes the query to execute immediately and retrieve the data before going to your View.
Are you sure the exception isn't being thrown because the model binder, operating on objects of interface IBasicData, only knows about the Password and Email properties - the model binder probably uses reflection and is maybe not picking up the base interfaces?
Quote from This article on MSDN Magazine, emphasis mine
For example, even though the Microsoft .NET Framework provides
excellent support for object-oriented principles, the
DefaultModelBinder offers no support for binding to abstract base
classes and interfaces.
To be honest I'd recommend having your ViewModel class just explicitly implement all 3 interfaces, and then keep the projection from your actual EF entity into an instance of this class (i.e usersDataSource.Select(u=>new UserViewModel{...}); and then bind on the view with #model IEnumerable
Interface inheritance doesn't sound like the right approach here; I wouldn't worry about the projection code you have, it's pretty normal and bear in mind it's providing a nice separation between your EF layer and your presentation layer (View).
I do agree with Tommy though on the IEnumerable; once you get out of your EF layer, IEnumerable-ise your data right away so as to materialise your data and close the database connection as quickly as possible.

Resources