C# OnDeserializing with Mongodb - c#

I have a quick question. Is it possible to use MongoDB with the OnDeserializing attribute or something like that?
MongoClient client { get; } = new MongoClient("mongodb://localhost:27017");
var Userscollection = db.GetCollection<UserModel>("Users");
var userfind = (await Userscollection.FindAsync(x => x.UserId == "UserId"));
var user = userfind.FirstOrDefault();
My UserModel class has a function with the OnDeserializing attribute but it doesn't fire on Find and fetching the item of the user.
[OnDeserializing]
void TestFunc(StreamingContext context)
{
}
Is there any way to fire it automatically or any similar method to detect in the constructor of the class if the class is creating by my codes or using the MongoDB serializer/deserializer?

OK, After poking a lot with attributes and lots of tries finally I found a solution for my case.
I commented above that creating 2 constructors doesn't work because settings values are going to be done after running constructors.
But, There's a workaround! BsonConstructor Attribute
create a simple constructor or with arguments for your own (if you need otherwise you can skip it)
Then create another constructor but using the BsonConstructor attribute like below.
[BsonConstructor()]
public Postmodel(ObjectId postid, ObjectId userid)
{
//Called by the BSon Serialize/Deserialize
}
But surely this constructor will not be what you need because all of the properties are null or having default values so you need to pass some argument too.
The sample below will give you the values of properties you need so you can do whatever you want in the constructor.
[BsonConstructor(nameof(Id), nameof(UserId))]
public Postmodel(ObjectId postid, ObjectId userid)
{
}
Id and UserId are two properties in my class.
You can also simply use
[BsonConstructor("Id", "UserId")]
instead, But be careful, Changing properties name or removing them in the development won't notify you to fix your values, so using nameof(PropertyName) is much safer.

Related

Getting the right class by attribute based on string value

public ActionResult AddComplianceForm(string TemplateName)
{
}
In this ASP.net MVC5 application there is a folder Templates that contains a bunch of different classes that all have different TemplateName attributes. The first part of this method needs to find the class that has a TemplateName matching the string passed in. I then need to create an instance of whatever template matched. I am very new to working with attributes in C# so help would be very appreciated. I mostly need to know how to access that folder of classes in the program to look into it.
What you are trying to do is called "Reflection" in C#.
Below is the link to another answer that shows how to get all the classes in a namespace (I'm assuming that the physical folder implies the use of a unique namespace for the classes contained in the folder.)
Link to StackOverflow answer
**Btw you should look up reflection performance and see if it makes sense in your case. You may want to use a factory pattern instead.
This will work:
public class HomeController : Controller
{
public ActionResult AddComplianceForm(string TemplateName)
{
Assembly assembly = Assembly.Load("Testy20161006"); //assembly name
Type t = assembly.GetType("Testy20161006.Templates." + TemplateName); //namespace + class name
Object obj = (Object)Activator.CreateInstance(t);
return View();
}
Don't just use reflection
The typical way someone would deal with this is with Reflection and run-time type discovery/binding. However, this is probably a poor starting point in this exact situation. The template name is passed in as an action argument, presumably through binding to a value in the request string, and you don't want c# code that will instantiate whatever class is passed in from the web!!! That would be a serious security issue known as an insecure direct object reference.
Create a list
To mitigate the risk, the proper approach is to check the argument against a whitelist. Well, if we have a white list already, we may as well associate each item in the list with a lambda expression that returns the object you want.
class MyController
{
static private readonly Dictionary<string,Func<BaseTemplate>> _templateList = new Dictionary<string,Func<BaseTemplate>>();
static MyController()
{
_templateList.Add("ATemplate", () => return new ATemplate());
_templateList.Add("SomeOtherTemplate", () => return new SomeOtherTemplate());
_templateList.Add("JustOneMore", () => return new JustOneMore());
}
public ActionResult AddComplianceForm(string TemplateName)
{
BaseTemplate template;
try
{
template = _templateList[TemplateName]();
}
catch (KeyNotFoundException exception)
{
RedirectToAction("MyController", "InvalidTemplateError");
}
DoSomethingWithTemplate(template);
}
}
Create the list using Reflection
But what if you have a crap ton of templates? You don't want to hard code all those dictionary entries, right?
Well, you could tag each of the templates with a custom attribute, e.g. [TemplateAttribute], and then populate the list this way:
foreach (Assembly b in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in b.GetTypes())
{
var a = Attribute.GetCustomAttribute(t, typeof(TemplateAttribute));
if (a != null)
{
var localType = t; //Avoid closure on loop variable
_templateList.Add(t.Name, () => Activator.CreateInstance(localType) as BaseTemplate);
}
}
}
This will automatically iterate through all the types that are loaded for your application and find the ones with the TemplateAttribute. Only those types will be allowed in the action argument.
Notes:
In these examples I assume all of your templates inherit from a BaseTemplate, but if they have no ancestor in common (not recommended) I guess you could just use object.
In these examples, I store the list and implement the code in the controller, but if you are going for well-structured code you should consider moving all that stuff into some sort of factory class and just pass the string in from the controller.

Ignoring / Not Mapping certain POCO properties in NEST 2.0

I am having a hard time finding good detail on NEST 2.0, the wrapper for Elasticsearch 2.2 I am using. My question is this: Can I do a bulk insert on an object (class with public data members) and map that to Elasticsearch where only the mapped fields between my C# class and the ES server mapping will save? And it will not add the additional fields in my class I do not want?
Right now I have a class of with strings and doubles and lists of other classes in it. I want to save the strings and doubles, but NOT include the Lists on my bulk inserts to Elasticsearch. It wants to save every piece of data in every field of my class. Is there a class member attribute or some other way to say "do not add this field if it has no mapping" that I have missed? I hope so.
You can ignore properties of your POCO in a number of ways with NEST 2.x. Let's use the following POCO as an example
using Nest;
using Newtonsoft.Json;
[ElasticsearchType(Name = "company")]
public class Company
{
public string Name { get; set; }
[String(Ignore = true)]
public string IgnoreViaAttribute { get; set; }
public string IgnoreViaSettings { get;set; }
[JsonIgnore]
public string IgnoreViaSerializerSpecificAttribute { get; set; }
}
1.Using the Ignore property on a derived ElasticsearchPropertyAttribute type (in our example, StringAttribute on IgnoreViaAttribute property) applied to the property that should be ignored on the POCO
2.Using the .InferMappingFor<TDocument>(Func<ClrTypeMappingDescriptor<TDocument>, IClrTypeMapping<TDocument>> selector) on the connection settings
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.InferMappingFor<Company>(i => i
.Ignore(p => p.IgnoreViaSettings)
);
var client = new ElasticClient(settings);
These settings are cached per instance of ConnectionSettings.
3.Using an ignore attribute applied to the POCO property that is understood by the IElasticsearchSerializer used, and inspected inside of the CreatePropertyMapping() on the serializer. In the case of the default JsonNetSerializer, this is the Json.NET JsonIgnoreAttribute. In our example, this is demonstrated by the attribute applied to the IgnoreViaSerializerSpecificAttribute property.
What I found by digging around a bit and testing a small class is that the following structure did indeed hide the attributes when you post a class with NEST 2.0 C#. If you do this just above the class member you wish to ignore when doing a bulk add that covers it.
[String(Ignore = true)]

Entity Framework Checking Date in Class

I am using Entity Framework in an ASP.Net MVC Project. The point of the project was to create a web version of an existing older desktop project written in vb6. This vb6 project saves dates as 11/11/1911 whenever a blank date is needed to the table because I have been told by the vb6 programmer that vb6 dates cannot be set to null.
Obviously in my classes I have dates such as:
public DateTime? ExampleDate { get;set; }
This causes the field to appear as 11/11/1911 when ideally it should be null and despite my insistence to change vb6 to simply null the dates the programmer is telling me it can't be done. I know I can create manual properties in the class to check the field and return a value accordingly and mark it as [NotMapped] but there are a huge amount of dates across the tables and I don't want to be creating an extra property for every datetime nor do I want to be making a manual check in my code after loading a record and adding extra unnecessary code.
So what I would like to do if it's possible is somehow create my own datatype or method I can use in the class directly i.e. changing:
public DateTime? ExampleDate { get;set; }
To something like:
public CustomDateTime? ExampleDate { get;set; }
And have the CustomDateTime check and return accordingly. Can anyone tell me if this is possible or recommend another solution?
EDIT:
What about some kind of custom data attribute or similar?
[CustomDateTime]
public DateTime? ExampleDate { get;set; }
I tried with a custom validator but this only triggers on saving and I need to trigger the check when reading.
If you really can't modify the DB to have meaningful values, a good idea might be to have a DataAccess Objet and a Business Object.
I'm using this answer as a reference, How to map Data Access to Business Logic objects in Entity Framework. He explains the idea very well.
This will add some complexity to your code but your entities will stay clean. And, if later the DB values are updated and '11/11/1911' is replaced by NULL you will just need to update your mapping.
I would also recommend not adding additional fields for this purpose to your Entities, as you want to keep them clean. If you're stuck with the values in your database representing what you would consider a null DateTime value then I would employ an extension method.
This way you don't have to repeat your code and you can keep your entities clean... something like this:
namespace Project.DateTimeExtensions
{
public static class DateTimeExtensions
{
public static DateTime? FromVb6DateTime(this DateTime? dateTime)
{
// Is the DateTime a VB6 null representation, if so, return null
return (dateTime.ToString("dd/MM/yyyy") == "11/11/1911") ? dateTime : null;
}
}
}
The you can ensure that the values are converted to null under certain conditions:
// Using the extension namespace
using Project.DateTimeExtensions;
public class MyClass()
{
public IDataService DataService;
public MyClass(IDataService dataService)
{
this.DataService = dataService
}
public void MyMethod()
{
// Get the date time value however you would normally
var vb6DateTime = this.DataService.GetDate();
// Convert the value using the extension method
var convertedDateTime = vb6DateTime.FromVb6DateTime();
}
}

How do I use WebAPI/Rest correctly when other params are needed

I am new to WebAPI and rest and am trying to do things correctly. By default if I were to access something such as User I would call api/user/5 if I wanted user 5. This would go to my User controller to Get(int num) I think. But I know I will often need other params passed as well. Currently I have Get(JObject data), but that data param is for other parameters. I will need other optional params whether I am sending an ID or wanting a list of everything. How do I go about organizing methods properly with WebAPI? Am I misunderstanding something?
To clarify:
This question is more about REST than dynamic objects, though they play a part:
How do I get a single resource vs a list of resources when I need additional params. I see those concepts as two separate methods, but the additional params complicate it in my mind when routing is involved.
Use attribute routing
For example -
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
or
[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }
if you need to return a list, create a method that returns a list, otherwise return the specific item requested
Look into using JToken or the even more dynamic 'dynamic' (Taken from here)
"
JSON and JavaScript is really dynamic, though, and often it's a hassle to try to "deserialize" really dynamic JSON objects into strongly-typed .NET structures. JSON.NET and ASP.NET Web API's model binding offer a happy medium - a middle ground - called JToken.
public class ContactController : ApiController
{
public JToken Post(JToken contact)
{
return contact;
}
}
Using JToken gives a dynamic container but also a DOM-like navigation model. But if that's not dynamic enough for me, why can't my method's parameter just take a "dynamic."
C# is statically typed, sure, but that doesn't mean I can't statically type something dynamic. ;)
Again, note the watch window.
Using dynamic to catch JSON post payloads
public class ContactController : ApiController
{
public dynamic Post(dynamic contact)
{
return contact;
}
}
"
I think you should make a new object for each WebAPI function that will handle the request. You can make the parameters optional with nullable properties.
[HttpPost]
public void SampleFunction(SampleFunctionModel model)
{
}
where SampleFunctionModel is:
public class SampleFunctionModel
{
public int? Id { get; set; }
public string Name { get; set; }
}

Updating an inner list using MongoDB

I have an Object
public class Object1{
public List<Object2> {get;set;}
}
public class Object2{
public Name{get;set;}
public Address{get;set;}
}
I have a feature where the user can update just one instance of Object2. So my code for saving Object2 looks like
[HttpPost]
public ActionResult SaveObject2(Object2 obj2)
{
if (obj2.Id == null){
//Add Logic
obj1.Obj2List.Add(obj2)
}
else{
// Update logic
}
}
But obj2.Id is never null.Id is of type ObjectId. How can i check for logic to see if need to insert or update ? I am using asp.net MVC 3 and Mongo DB using the official C# drivers.
Thanks
The ObjectId type is a struct, not a class - so it's never going to be null. Check for ObjectId.Empty, instead.
A word of caution, though: I suppose that you are storing the id of Object2 in some hidden field between requests. If that is the case, be aware that malicious user can easily change the ID by using an HTTP proxy (such as Fiddler), thus tricking you into believing that the Object2 is being updated instead of added.
Depending on context of what you are trying to do, I would suggest performing some additional checks to more reliably determine if you should insert or update your object.

Categories

Resources