Store same fields twice during serialization - mongodb-.net-driver

I have a very simple POCO like:
public class Sample()
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I'd like to use a custom Attribute like [AdditionalLowerField] as below
public class Sample()
{
public ObjectId Id { get; set; }
public string FirstName { get; set; }
[AdditionalLowerField]
public string LastName { get; set; }
}
so that in MongoDB LastName field is serialized twice, with an additional lowered case field like this:
{
_id : ......,
FirstName : "Mario",
LastName : "Special Case",
LastNameLower : "special case"
}
I'm trying to figure this out from doc http://docs.mongodb.org/ecosystem/tutorial/serialize-documents-with-the-csharp-driver/#write-a-custom-attribute but it's not so complete and I don't find good examples for this scenario.
Update: I know that an additional read-only Property can be used together with a [BsonElement] attribute to store its value in MongoDB during serialization and avoid to get it back during deserialization like described in opt-in paragraph:
[BsonElement]
public string LastNameLower
{
get { return LastName.ToLowerInvariant(); }
}
But I'd like to avoid creation of additional properties if possible.
Does anybody have experience on this scenario?
Thanks

Related

Convert the elastic search response to a list

I am using elastic 7.17.3 and NEST 7.17.3
I have a elastic response which I am trying to put in a list. I have below method. The method is generic and is called from different model classes.
public class Account
{
[JsonProperty(AnalyticsFields.AccessRegions)]
public string[] AccessRegions { get; set; }
[JsonProperty(AnalyticsFields.AccessUsers)]
public string[] AccessUsers { get; set; }
[JsonProperty(AnalyticsFields.AccountId)]
public string AccountId { get; set; }
[JsonProperty(AnalyticsFields.AccountIdAndName)]
public string AccountIdAndName { get; set; }
[JsonProperty(AnalyticsFields.AdvisorId)]
public string AdvisorId { get; set; }
[JsonProperty(AnalyticsFields.AdvisorIdAndName)]
public string AdvisorIdAndName { get; set; }
}
This is how the method converting the elastic response looks like
private void AddSearchResponseHits<T>(ISearchResponse<T> searchResponse, List<T> results) where T : class
{
results.AddRange(searchResponse.Hits.Select(h => h.Source).ToArray());
}
The problem is the field value becomes null even thought the searchresponse have the complete response from elastic. Please help me out. The field values should load in respective properties in the model class.

"[Required]" Data Annotation Not Enforced on POST Methods

I had the idea to use Data Annotations in order to validate ModelState. This works wonderfully. The problem I am having is that the [Required] Data Annotation is being enforced on [Key] fields on post. Our data layer takes care of setting Id's and we don't want anyone consuming the service to have to worry about Id's. Is there a way around this in WebApi2?
I have looked at this question, and removing the Id field from ModelState in the POST method before checking for valid ModelState would work. The issue with that is that we use a filter for ModelState.
EDIT:
After doing some more research, what I am essentially wanting to do is what the [Bind] attribute does in MVC. After some research, it does not look like this is a feature that has yet been implemented in WebApi. If anyone has any ideas, feel free to post them.
What you can do is to replace your entity with a data transfer object, which is identical to your original entity without the ID field. For example,
The original entity may look like this
public class User
{
[Required]
public Guid UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
and the DTO may look like this
public class UserDto
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
Hope this helps.

EF CodeFirst computed field in CF entity class

I have added computed fields(Active and CreditsLeft) directly into my CodeFirst entity class. Is it good idea to add computed field logic inside CF Entity class?
public class User : Entity
{
public User()
{
Id = Helper.GetRandomInt(9);
DateStamp = DateTime.UtcNow;
TimeZone = TimeZoneInfo.Utc.Id;
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Email { get; set; }
[Required]
[MaxLength(50)]
public string Password { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
[Required]
public DateTime DateStamp { get; set; }
public virtual ICollection<Order> Orders { get; set; }
public virtual ICollection<Statistic> Statistics { get; set; }
public virtual ICollection<Notification> Notifications { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public bool Active
{
get
{
return Orders.Any(c => c.Active && (c.TransactionType == TransactionType.Order || c.TransactionType == TransactionType.Subscription));
}
}
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int CreditsLeft
{
get
{
return Orders.Sum(p => p.Credits != null ? p.Credits.Value : 0);
}
}
}
Is it good idea to add computed field logic inside CF Entity class?
Sure, you can do this, but there are a few things you must take care of.
First, the attribute for a property that is computed by business logic is not [DatabaseGenerated(DatabaseGeneratedOption.Computed)], because this indicates that the value is computed in the database (as in a computed column). You should mark the property by the [NotMapped] attribute. This tells Entity Framework to ignore the property in database mapping.
Second, since both properties use Orders, you must make sure that the orders are loaded or can be lazy loaded when either property is accessed. So you may want to load Users with an Include statement (Include(user => user.Orders)). Or else you must ensure that the context is still alive when Active or CreditsLeft is accessed.
Third, you can't address the properties directly in an EF LINQ query, as in
db.Users.Select(u => u.Active);
because EF will throw an exception that it doesn't know Active. You can address the properties only on materialized user objects in memory.

What's the best way to adapt different data types between insert/select from MongoDB?

I think I am missing a simple solution here. When I run a select (using Linq-style on IQueryable) that returns IQueryable, I may want MyClass to return differently-formatted key/values than I will use in my insert.
Consider as example the below UploadFile class and note properties id and Filetimestamp. When I SET Filetimestamp I want it stored as DateTime, but when I RETRIEVE IQueryable I would like to:
See Filetimestamp returned as String
OR
See a separate property returned as String reflecting Filetimestamp.toString(). This property doesn't have a corresponding key in the database; it's just a decoration of a real key/value from BSON.
Thinking #1 was unlikely, I pursued #2 by adding a get-only field Filetimestamp_str that returns Filetimestamp.toString(). When I select, behavior is as expected. When I insert, I see failures.
I could create a decorator class that I use for gets and all would work - but am I missing something more fundamental and simple here?
public class UploadFile : IUploadFile
{
public Object _id { get; set; }
public String Filepath { get; set; }
public String Filename { get; set; }
public String Filetype { get; set; }
public String Fileauthor { get; set; }
public DateTime Filetimestamp { get; set; }
public Object FileID { get; set; }
// these keys are not in DB
public String Filetimestamp_str { get { return Filetimestamp.ToString(); } }
public String _id_str { get { return _id.ToString(); } }
}
You could create a BsonClassMap, specifying which properties should (not) be serialized. See the Serialization Tutorial. However, according to that tutorial, a read-only property should not be automapped, so I should not expect errors on the insert.

How can I mark properties required to show up different in intellisense

Let's say we have a class like so:
public class Plan
{
public string PlanCode { get; set; } //Required
public string Name { get; private set; }
public string Description { get; set; }
public string SuccessUrl { get; set; }
}
Is there a way to make intellisense show required fields/properties different, such as italicized or a shade of red?
One would be able to determine what properties would be required quickly if calling a Create() method for instance.
Note: When I say show up different in intellisense I don't mean the tool-tip text that you see when the member is highlighted. I specifically mean the text of the member itself.
No, I don't believe you can. You should design your objects with required fields in mind, i.e. constructors.
So any fields that the object requires should be parameters in a constructor.
public class Plan
{
public string PlanCode { get; set; } //Required
public string Name { get; private set; }
public string Description { get; set; }
public string SuccessUrl { get; set; }
public Plan(string planCode)
{
PlanCode = planCode;
}
}
This is allows your code to clearly express its intention. Any developer using the Plan class will know that PlanCode is required, as it is enforced by the constructor.

Resources