MongoDB C#/.NET Driver - How to deserialize UUID and ISODate - c#

I have a document in a MongoDB collection like so:
{
_id: new UUID("31daac77-bcbc-4cd5-bb93-382440f46f16"),
CompanyId: 'XYZ',
RequestDate: ISODate("2023-01-10T07:52:32.840Z")
}
It corresponds to an object like so:
public class ReportRequest
{
public ReportRequest(string companyId)
{
this.Id = Guid.NewGuid();
this.CompanyId = companyId;
this.RequestDate = DateTime.UtcNow;
}
[BsonId]
[BsonElement("_id")]
[BsonGuidRepresentation(GuidRepresentation.Standard)]
public Guid Id { get; }
[BsonElement]
public string CompanyId { get; }
[BsonElement]
[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime RequestDate { get; }
}
I then try to query for all documents with a particular CompanyId:
public async Task PrintAvailableReportDatesAsync(string companyId)
{
var filter = Builders<ReportRequest>.Filter.Eq(r => r.CompanyId, companyId);
var cursor = await _collection.FindAsync(filter);
var reportRequests = await cursor.ToListAsync();
foreach (var req in reportRequests)
Console.WriteLine($"Id: {req.Id.ToString()}, Date: {req.RequestDate}");
}
I would expect to get the following output:
Id: 31daac77-bcbc-4cd5-bb93-382440f46f16, Date: 2023-01-10T07:52:32.840Z
Instead, I get this:
Id: 00000000-0000-0000-0000-000000000000, Date: 0001-01-01T00:00:00
What am I doing wrong?

From what I tested on my local side, those properties' values were assigned within the constructor.
There are 2 approaches to fix it:
Approach 1: Provide the setter to all properties
public class ReportRequest
{
public ReportRequest(string companyId)
{
this.Id = Guid.NewGuid();
this.CompanyId = companyId;
this.RequestDate = DateTime.UtcNow;
}
[BsonId]
[BsonGuidRepresentation(GuidRepresentation.Standard)]
public Guid Id { get; set; }
public string CompanyId { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Utc)]
public DateTime RequestDate { get; set; }
}
Demo
Approach 2: Create a constructor with three parameters
public class ReportRequest
{
...
public ReportRequest(Guid id, string companyId, DateTime requestDate)
{
this.Id = id;
this.CompanyId = companyId;
this.RequestDate = requestDate;
}
...
}
Demo

Related

ASP.NET Core Web API - The name 'jsonString' does not exist in the current context

I am using ASP.NET Core Web API. I have this code:
ViewModel (Dto):
public class MandateDto
{
public DateTime DueDate { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int? MandateId { get; set; }
}
public class TransactionDto
{
public string? RawData { get; set; }
}
EntityMapper:
public class EntityMapper
{
public Mandate FromMandateDtoToMandate(MandateDto mandate)
{
Mandate mandate = new Mandate()
{
DueDate = mandate.DueDate,
StartDate = mandate.StartDate,
EndDate = mandate.EndDate
};
string jsonString = JsonSerializer.Serialize(mandate);
return mandate;
}
public TransactionLog FromMandateDtoToTransactionLog(TransactionDto mandate)
{
return new TransactionLog
{
RawData = jsonString,
};
}
}
MandateService:
public async Task<Mandate> Post(MandateDto mandate)
{
var mapper = new EntityMapper();
var mandate = mapper.FromMandateDtoToMandate(mandate);
var transaction = mapper.FromMandateDtoToTransactionLog(mandate);
try
{
await _unitOfWork.MandateRepository.Insert(mandate);
await _unitOfWork.TransactionRepository.Insert(transaction);
await _unitOfWork.SaveChangesAsync();
}
catch (Exception e)
{
throw new Exception(e.Message);
}
return mandate;
return transaction;
}
As shown in EntityMapper, I want to transfer jsonString from FromMandateDtoToMandate into FromMandateDtoToTransactionLog as in:
RawData = jsonString
I got this error:
Error CS0103 The name 'jsonString' does not exist in the current context
How do I resolve this?
Thanks.
you have to add this line with jsonString otherwise the variable doesn't exist in this scope
public TransactionLog FromMandateDtoToTransactionLog(MandateDto mandate)
{
string jsonString = JsonSerializer.Serialize(mandate); //add this line!
return new TransactionLog
{
RawData = jsonString,
};
}

ASP.NET C# inserting multiple values in single field in MongoDB

I am trying to store names of team members of a project in the same field in MongoDB using ASP.NET.
The value is read correctly but I am having trouble creating a new entry.
Below is my controller. Simply writing the document.insert() doesn't work as it only stores a single value and not a list.
public ActionResult InsertProject(Projectdata model)
{
_dbContext = new MongoContext();
if (Request.Cookies["Userdata"] == null)
{
return this.RedirectToAction("Index", "User");
}
var document2 = _dbContext._database.GetCollection<Mongodata>("mainuser_data");
var users = document2.FindAll().ToList();
var document = _dbContext._database.GetCollection<ProjectItem>("project_data");
string project = model.pname;
var query = Query<ProjectItem>.EQ(model2 => model2.pname, project);
var count = document.FindAs<ProjectItem>(query).Count();
if (count == 0)
{
var result = document.Insert(model);
TempData["Message"] = "Project Added";
return View();
}
else
{
TempData["Message"] = "Project Already Exists";
return View("Insert", model);
}
}
My model:
public class Projectdata
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("pname")]
public string pname { get; set; }
[BsonElement("pdesc")]
public string pdesc { get; set; }
[BsonElement("sdate")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MMM/yyyy}")]
public DateTime sdate { get; set; }
[BsonElement("edate")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MMM/yyyy}")]
public DateTime edate { get; set; }
public List<Team2> teams { get; set; }
[BsonElement("leader")]
public string leader { get; set; }
}
public class Team2
{
[BsonElement("username")]
public string username { get; set; }
}
I can read all multiple values fine but creating a new entry is where I am facing trouble.
Below is the sample data to be added:
{
"_id" : ObjectId("5f93cf48a591c562654d4ded"),
"pname" : "demo1",
"pdesc" : "this is a demo project",
"sdate" : "24-10-2020",
"edate" : "25-10-2020",
"teams" : [
{
"username" : "ManushPandya"
},
{
"username" : "KuntalVakil"
}
],
"leader" : "ManushPandya"
}
How do I store multiple values in teams field? I am using check boxes to let user select those multiple values in a view.

Get PK value by passing in non-PK value using EntityFramework

I'm working on building a Windows Phone 8.1 app that uses a Web API and Entity Framework to connect to a SQL database. With my current setup, I have data objects for my entities in my services project (which contain the PK id property) and models in my phone client project (which don't contain the PK id property in order to enable auto-increment when a new member is created. I'm trying to write a services method that returns the memberID value (PK of Member entity) when the username is passed to it. However, when I do so, I get the following error:
System.InvalidCastException: Unable to cast object of type 'System.Net.Http.StreamContent' to type 'System.IConvertible'.
I'm not sure if I'm trying to do something that doesn't work, or if there's a much easier way to do this, but my code builds successfully. The above error is a run-time error. Here's the code I'm using for my relevant classes:
This is the method that calls my server from my client's backend:
public async void GetMembers(String currUser, String currPass)
{
using (var client = new HttpClient())
{
//MembersListBox.Items.Add("using block entered");
client.BaseAddress = new Uri("http://nflff.azurewebsites.net");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//MembersListBox.Items.Add("client's defaultrequestheaders done");
//gets all members from table
HttpResponseMessage response = await client.GetAsync("api/Members");
//MembersListBox.Items.Add("after response reached");
if (response.IsSuccessStatusCode)
{
//reads all member objs from table as a json string
string s = await response.Content.ReadAsStringAsync();
//how can we pass the user's login credentials (including ID) to other pages? via HttpClient?
//converts string of members into a list of member objs
var deserializedResponse = JsonConvert.DeserializeObject<List<Members>>(s);
foreach (Members member in deserializedResponse)
{
//if current member matches a member found in list
if(member.compareUserAndPassword(currUser, currPass)) {
MembersListBox.Items.Add(currUser + " and " + currPass + " found.");
MembersListBox.Items.Add(member.userName + " " + member.password);
Members currMember = member; //this works
MembersListBox.Items.Add("Current member: " + currMember.ToString());
//how should memberID be remembered for user?
client.DefaultRequestHeaders.Authorization = CreateBasicHeader(currUser, currPass);
MembersListBox.Items.Add(client.DefaultRequestHeaders.Authorization);
HttpResponseMessage memResponse = await client.GetAsync("api/Members/" + currUser);
if (memResponse.IsSuccessStatusCode)
{
MembersListBox.Items.Add("memResponse successful");
}
//should only go to home page if successful
//int memID = Convert.ToInt32(await client.GetAsync("api/Members?MemberStr=" + currUser));
HttpResponseMessage thisMember = await client.GetAsync("api/Members?MemberStr=" + currUser);
var con = thisMember.Content;
var head = thisMember.Headers;
var rm = thisMember.RequestMessage;
int memID = Convert.ToInt32(thisMember.Content);//THIS IS THE LINE THAT GENERATES THE ERROR
//will need to call server's getidbyname method and pass result instead of currMember.memberID
//this.Frame.Navigate(typeof(HomeHub), currMember.memberID);
this.Frame.Navigate(typeof(HomeHub), memID);
}
}
}
//MembersListBox.Items.Add(Members.MembersList.Count);
foreach (var member in Members.MembersList)
{
// MembersListBox.Items.Add(member.ToString());
}
}
}
Here's my controller from my services project:
public class MembersController : ApiController
{
private GatoradeShowerDB db = new GatoradeShowerDB();
// GET: api/Members
public IQueryable<Member> GetMembers()
{
return db.Members;
}
// GET: api/Members/5
[ResponseType(typeof(Member))]
public async Task<IHttpActionResult> GetMember(int id)
{
Member member = await db.Members.FindAsync(id);
if (member == null)
{
return NotFound();
}
return Ok(member);
}
//GET: api/Members?MemberStr={memberStr}
[ResponseType(typeof(Member))]
public async Task<T> GetMemberIDByName<T>(String MemberStr) where T : struct
{
//var member = JsonConvert.DeserializeObject<Member>(MemberStr);
//Member member = await db.Members.FindAsync(MemberStr);
//var userId = ...;
var member = await db.Members.Where(x => x.UserName == MemberStr).ToListAsync();
if (member == null)
{
return (T)Convert.ChangeType(NotFound(), typeof(T));
}
return (T)Convert.ChangeType(member[0].MemberID, typeof(T));
//return member[0].MemberID; //hopefully gets id of first member in async list and returns it
//return Ok(member);
}
//other methods
}
Also, here's my client-side model:
public class Members
{
//[JsonProperty("MemberID")]
//public int memberID { get; private set; }
[JsonProperty("FirstName")]
private string firstName { get; set; }
[JsonProperty("LastName")]
private string lastName { get; set; } //both names should be optional
[JsonProperty("UserName")]
public string userName { get; private set; } //note: I don't think we should track first and last names
[JsonProperty("Password")]
public string password { get; private set; } //will probably need to implement validation in setter so won't be able to auto-implement
[JsonProperty("Email")]
private string email { get; set; }//same with this. will need validation
[JsonProperty("MemberCity")]
private string memberCity { get; set; }
[JsonProperty("MemberState")]
private string memberState { get; set; }
[JsonProperty("MemberZip")]
private string memberZip { get; set; }
[JsonProperty("MemberPhone")]
private string memberPhone { get; set; }//should also be optional
[JsonProperty("FaveTeamID")]
private int faveTeamID { get; set; }
public static List<Members> MembersList = new List<Members>();
public Members(string first, string last, string user, string pass, string email, string city, string state, string zip, string phone, int team)
{
//memberID = id;
firstName = first;
lastName = last;
userName = user;
password = pass;
this.email = email;
memberCity = city;
memberState = state;
memberZip = zip;
memberPhone = phone;
faveTeamID = team;
}
public override string ToString()
{
return "Member: " + userName + " in " + memberCity;
}
public bool compareUserAndPassword(string currUser, string currPass)
{
if (currUser.Equals(userName) && currPass.Equals(password))
{
return true;
}
else
{
return false;
}
}
}
Finally, here's my server-side data object:
public partial class Member
{
public int MemberID { get; set; }
[Required]
[StringLength(255)]
public string FirstName { get; set; }
[Required]
[StringLength(255)]
public string LastName { get; set; }
[Required]
[StringLength(50)]
public string UserName { get; set; }
[Required]
[StringLength(32)]
public string Password { get; set; }
[Required]
[StringLength(50)]
public string Email { get; set; }
[Required]
[StringLength(50)]
public string City { get; set; }
[Required]
[StringLength(30)]
public string State { get; set; }
[Required]
[StringLength(255)]
public string Zip { get; set; }
[Required]
[StringLength(255)]
public string Phone { get; set; }
public int FavTeamID { get; set; }
}
I know this is a lot of code, but I'd rather post too much than too little. One thought I had was to somehow add the MemberID value to the httpClient object and pass that as a parameter to other pages, but I'm not sure if that's possible either. How can I get the MemberID value for a specific member if that value is only stored in the server-side data object but not in the client-side model? I'm open to different approaches as well if there's an easier way to go about this. Thank you in advance for your help.
UPDATE
My issue seems to be originating from my GetMemberIDByName method in my controller. When I debugged it, I got the following error message:
Cannot call action method 'System.Threading.Tasks.Task`1[T] GetMemberIDByNameT' on controller 'WorkingVersionGetItDone.Controllers.MembersController' because the action method is a generic method.
I'm pretty sure your method should look more like this:
public async Task<IHttpActionResult> GetMemberIDByName(string MemberStr)
{
var member = await db.Members.Where(x => x.UserName == MemberStr).ToListAsync();
if (member == null)
{
return NotFound();
}
return Ok(member[0].MemberID);
}
The caller will then either get a 404 (the NotFound()) or a 200 with a payload of the MemberID and you can process appropriately.
HttpResponseMessage memberResponse = await client.GetAsync("api/Members?MemberStr=" + currUser);
if(memberResponse.StatusCode == HttpStatusCode.OK)
{
// got the memberId...
var memberId = Convert.ToInt32( memberResponse.Content.ReadAsStringAsync().Result);
}
else if(memberResponse.StatusCode == HttpStatusCode.NotFound)
{
// member not found...
}

entity framework - two people with the same data

I want to map a two tables in entity framework 6 and need some help! It is for my chat application; I need to map user conversations into the database. Both group and private messages. For this question however, if you help me with the private messaging mapping, I should hopefully work out the group by myself :) anyway....
Each user can talk to any other user. They however share the same data, which is where I am struggling a bit: how to set the keys to the exact same data without duplication. This is what I have so far:
**EDIT - new code *****
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace CodeFirstNewDatabaseSample
{
static void Main(string[] args)
{
using(var db = new PrivateMessageContext())
{
Console.Write("Enter message: ");
var message = Console.ReadLine();
var userFrom = "userFrom";
var userTo = "userTo";
var messageDetail = new PrivateMessageDetail(MessageDate = DateTime.Now, FromUser = userFrom, message = message);
var pm = new PrivateMessageHeader { User1 = userFrom, User2 = userTo, TimeStamp = DateTime.Now };
pm.Messages.Add(messageDetail);
db.PrivateMessages.Add(pm);
db.SaveChanges();
// Display all Blogs from the database
foreach(var pmsg in db.PrivateMessages)
{
var query = pmsg;
Console.WriteLine(pmsg.Message);
}
Console.ReadKey();
}
}
}
public class PrivateMessage
{
public int PrivateMessageId { get; set; }
public string Message { get; set; }
public DateTime TimeStamp { get; set; }
// public int User1Id { get; set; }
public virtual string user1 { get; set; }
// public virtual User user1 { get; set; }
public virtual string user2 { get; set; }
//public int User1Id { get; set; }
// public virtual User user2 { get; set; }
}
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
}
public class PrivateMessageContext : DbContext
{
public DbSet<PrivateMessage> PrivateMessages { get; set; }
}
public class Send
{
/* void Send(userTo, userFrom, message)
{
using (var db = new PrivateMessageContext()) {
var query = from pm in db.PrivateMessages;
foreach(var msg in pm)
{
var user1 = msg.user1;
var user2 = msg.user2;
if ( (user1==userTo && user2==userFrom) || (user1==userFrom && user2==userTo))
{
msg.Message += message;
return;
}
else {
// pair doesn't exist
var PrivateMessage = new PrivateMessage { user1 = userFrom; user2 = userTo; TimeStamp = DateTime.Now; Message = message; };
db.PrivateMessages.Add(PrivateMessage);
db.SaveChanges();
}
}
}*/
}
}
I am now stuck on two things - how to make a callable class which checks if there is previous message history (the Send() ) and how to use the User username instead of strings...
Thank you
*update 3*
static void Main(string[] args)
{
using(var db = new PrivateMessageContext())
{
Console.Write("Enter message: ");
var message = Console.ReadLine();
var userFrom = "userFrom";
var userTo = "userTo";
var messageDetail = new PrivateMessageDetail(MessageDate = DateTime.Now, FromUser = userFrom, message = message);
var pm = new PrivateMessageHeader { User1 = userFrom, User2 = userTo, TimeStamp = DateTime.Now, Message = messageDetail };
db.PrivateMessages.Add(pm);
db.SaveChanges();
// Display all Blogs from the database
foreach(var pmsg in db.PrivateMessages)
{
var query = pmsg;
Console.WriteLine(pmsg.Message);
}
Console.ReadKey();
}
}
}
public class PrivateMessageContext : DbContext
{
public DbSet<PrivateMessageHeader> PrivateMessages { get; set; }
}
What you probably want is some kind of master/detail. What you would do is create a PrivateMessageHeader type, and this would contain the participants in the private message. Then you would have a PrivateMessageDetail type that would contain the actual messages. There would be a 1 to many association between Header and details.
So something like this:
public class PrivateMessageHeader {
public PrivateMessageHeader() { Messages = new List<PrivateMessageDetail>; }
public int PrivateMessageHeaderId {get;set;}
public DateTime ThreadTime {get;set;} // Date of the start of thread
public string User1 {get;set;}
public string User2 {get;set;} // this could be made to a list to allow multiples
public ICollection<PrivateMessageDetail> Messages {get;set;}
}
public class PrivateMessageDetail {
public int PrivateMessageDetailId {get;set;}
public DateTime MessageDate {get;set;}
public string FromUser {get;set;} // Don't need ToUser, it's already in header
public string Message {get;set;}
public PrivateMessageHeader parent {get;set;}
}

Request.CreateResponse returns blank data to postman

I have encountered a problem when trying to call my web api with a post request, a empty array is returned.
My method is:
// POST: Api/v1/transaction/
[HttpPost]
public HttpResponseMessage Post(string user)
{
var userId = new Guid(user);
var transactions = new Collection<TransactionDataTransferObject>();
try
{
var seller = _databaseContext.Sellers.Single(s => s.Id == userId);
var sellerMedias = _databaseContext.Medias.Where(m => m.TakenBy.Id == seller.Id);
foreach (var sellerMedia in sellerMedias)
{
var allLogsForMedia = _databaseContext.Logs.Where(l => l.ObjectReferenceId == sellerMedia.Id);
foreach (var logMedia in allLogsForMedia)
{
var transaction = new TransactionDataTransferObject
{
Date = logMedia.DateTimeInUtc,
Amount = sellerMedia.PriceInSek,
MediaName = sellerMedia.FileName,
UserName = seller.FirstName + " " + seller.LastName
};
transactions.Add(transaction);
}
}
}
catch (Exception exception)
{
return Request.CreateErrorResponse(HttpStatusCode.NotFound, exception);
}
return Request.CreateResponse(HttpStatusCode.OK, transactions);
}
When I debug transactions variable, I see two objects in the collection.
My response to postman is
[
{},
{}
]
What have I done wrong? Where is my data which is sent?
Ok, after some hours of slaming my head in the table i found out that I used
[DataContract] as filter on the ViewModel,TransactionDataTransferObject.
Like this:
[DataContract]
public class TransactionDataTransferObject
{
[Display(Name = "Date")]
public DateTime Date { get; set; }
public string MediaName { get; set; }
public Guid MediaId { get; set; }
public string UserName { get; set; }
public Guid UserId { get; set; }
[Display(Name = "Description")]
public string Discriminator { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
}
Which was wrong in this case...
Thanks for reading!

Categories