Get prev, current, next on MongoDB c# - c#

I have a Collection Students which Stores registered students data in MongoDB. It has StudentName, SaveDate etc. fields. I am displaying the details to my website from my .net framework WebAPI.
What I want to do is Get StudentDetails with prev next student ID's by the order of SaveDate.
My current query gets Student details by student id from below query,
var collection = StudentDB.Collection<Student>("StudyMaterials");
var curfilter = Builders<Student>.Filter.Eq(x => x.studentID, studentID);
studentDetails= collection.Find(curfilter).FirstOrDefault();
This is my Student.cs class,
public class Student
{
[BsonRepresentation(BsonType.String)]
public Guid StudentID {get; set;}
public string StudentName {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid NextStudentID {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid PrevStudentID {get; set;}
}
I want to use aggregation but don't know how to sort by multiple sort definitions on the aggregation framework.

First of all, I don't think you need to keep the students like that. I suggest the following:
public class Student
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public DateTime SaveDate { get; set; }
}
You need to make sure that the documents are sorted by SaveDate. For instance, when inserting new students into the database you can do it like this:
var student = new Student
{
Name = "Sara",
SaveDate = DateTime.UtcNow
};
context.Students.InsertOne(student);
Now, you can get the documents with Skip and Limit:
var skip = 0;
var limit = 1;
var context = new MongoContext();
var filter = Builders<Student>.Filter.Empty;
var result = context.Students.Find(filter).Skip(skip).Limit(limit).FirstOrDefault();
Increment skip to get next and so on.

Related

Auto populate properties in class

Suppose that I have a class name User, is there any ways to populate the class properties and default value in Visual Studio as follow. I feel it is tedious to repeat typing the properties again and again.
Probably there is default shortcut to do this in Visual Studio or any extension that can do this?
Case 1
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
);
case 2
var user = new User();
user.UserID = "", // auto populate with type default value
user.Username = "", // auto populate with type default value
........ // auto populate all properties inside User class
........ // auto populate all properties inside User class
I'm not quite sure what are you trying to achieve here
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
since both new User and db.User are the same type of class/entity. But let's say you have an entity model User and a DTO/View model called UserViewModel and you want automatically to map the values of User to UserViewModel you can use automapper https://docs.automapper.org/en/stable/Getting-started.html Example this is the Entity definition
public class User
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
And we have a UserViewModel to which you want to map the data from User
public class UserViewModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
With AutoMapper you can do this
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>();
}
and then you can use the mapper configuration like this
var user = db.User.Where(x => x.UserID).First();
var userViewModel = configuration.CreateMapper().Map<UserViewModel>(user);
This will automatically populate the property values of User to UserViewModel. Alternatively you can have a view model like this
public class UserViewModel
{
public int Id {get; set;}
public string FullName {get; set;}
public int Age {get; set;}
}
This time not all properties of User and UserViewModel match one to one and you will have to set a custom mapper configuration i.e.
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>()
.ForMember(uvm => uvm.FullName, o => o.MapFrom($"{u.FirstName} {u.LastName}") );
}
This way you are configuring the automatic User to UserViewModel mapping to map the FullName property value by concatenating FirstName and LastName from User
Well I have build a library that could solve this problem for you, called FastDeepCloner
public class User
{
public virtual string Name { get; set; } = "sdjh";
public virtual int PasswordLength { get; set; } = 6;
public Circular Test { get; set; } = new Circular();
}
public class CloneToTest
{
[FastDeepCloner.FastDeepClonerColumn("Name")]
[FastDeepCloner.FastDeepClonerColumn("LastName")] // Or
public string FullName { get; set; }
// You see here the type could be difrrent then the orginal type.
// FastDeepCloner will try to convert it, if it fail then a default value will be inserted insted
public string PasswordLength { get; set; }
// You could add a path insted, remember this only work on none list items.
[FastDeepClonerColumn("Test.myBar.Id")]
public int Id { get; set; }
public Circular Test { get; set; }
}
Now simple use
var user = new User() { Name = "alen toma" };
var cloneTo =new CloneToTest();
FastDeepCloner.DeepCloner.CloneTo(user, cloneTo);
Assert.AreEqual(user.Name, cloneTo.FullName);

C# Mongodb: How to find in nested list of documents and select one field

I have the following models:
public class Car
{
public string Id {get; set;}
public IList<Driver> Drivers {get; set;}
}
public Driver
{
public string Id {get; set;}
public string IsActive {get; set;}
{
I want to find a car by id and select driver's id that is active. So
var carId = "123";
carCollection.Find(c=>c.Id == carId)
How to add selection for active driver's id?
PS. Only one active driver could be.
using mongo driver this can be done like below
var query = Query.And(Query.EQ("Id", carId),
Query.EQ("Drivers.IsActive",
"true"));
MongoCursor<Car> cursor = carCollection.FindAs<Car>(query);
if (cursor.Count() > 0 && cursor.ToList()[0].Drivers != null)
{
// use the cursor.ToList()[0].Drivers
}

EF 6.1 Database.SqlQuery projection into complex type (many-to-many relationship)

Is it in Entity Framework 6.1 possible to use the Database.SqlQuery command to execute a query, containing a many-to-many relation, and map it back to a DTO (using an intermediate DTO - i know this cannot be done in one go)? And how performed would such an action be?
This example is an extremely simplified version of a problem I'm currently facing. I'm just interested of what can can (cannot) be done with Database.SqlQuery.
I know i can use navigational properties (use Linq) but I'm investigating performance on a far more complex query. This is just a very simplified version of what I'm trying to achieve.
Database
DTO
public class EventDto{
public int EventId {get;set;}
public string Name {get;set;}
public string Slug {get;set;}
public List<ArtistDto> Headliners {get;set;}
}
public class ArtistDto{
public int ArtistId {get;set;}
public string Name {get;set;}
public string Bio {get;set;}
}
Temp DTO
public class EventWithHeadlinersDto{
public int EventId {get;set;}
public string Name {get;set;}
public string Slug {get;set;}
public int ArtistId {get;set;}
public string Name {get;set;}
public string Bio {get;set;}
}
Code
return Context.Database.SqlQuery<EventWithHeadlinersDto>(#"
SELECT * FROM [Events] E
LEFT JOIN [Headliners] H ON E.EventId = H.EventId
LEFT JOIN [Artists] A ON H.ArtistId = A.ArtistId
WHERE E.eventid = #eventId",
new SqlParameter("eventId", eventId))
.ToListAsync();
Requires some coding (basically replicating the EF query materialization process), but doable.
First, the temp DTO should be modified to include all the required fields, taking into account the left joins (using nullable types where needed):
public class EventWithHeadlinersDto
{
// Event Info
public int EventId { get; set; }
public string EventName { get; set; }
public string EventSlug { get; set; }
// Artist Info
public int? ArtistId { get; set; }
public string ArtistName { get; set; }
public string ArtistBio { get; set; }
}
Then you should ensure the SQL SELECT includes all the necessary columns, using the aliases when necessary in order to match DTO property names:
var sql = #"
SELECT
E.EventId, E.Name EventName, E.Slug EventSlug,
A.ArtistId, A.Name ArtistName, A.Bio ArtistBio
FROM [Events] E
LEFT JOIN [Headliners] H ON E.EventId = H.EventId
LEFT JOIN [Artists] A ON H.ArtistId = A.ArtistId
WHERE E.EventId = #eventId";
Then execute the sql query and get the result DTO set:
var dataSet = await query.ToListAsync();
Finally transform it to the desired format:
var eventMap = new Dictionary<int, EventDto>();
var artistMap = new Dictionary<int, ArtistDto>();
foreach (var entry in dataSet)
{
EventDto #event;
if (!eventMap.TryGetValue(entry.EventId, out #event))
{
#event = new EventDto
{
EventId = entry.EventId,
Name = entry.EventName,
Slug = entry.EventSlug,
Headliners = new List<ArtistDto>()
};
eventMap.Add(#event.EventId, #event);
}
if (entry.ArtistId != null)
{
ArtistDto artist;
if (!artistMap.TryGetValue(entry.ArtistId.Value, out artist))
{
artist = new ArtistDto
{
ArtistId = entry.ArtistId.Value,
Name = entry.ArtistName,
Bio = entry.ArtistBio,
};
artistMap.Add(artist.ArtistId, artist);
}
#event.Headliners.Add(artist);
}
}
var resultSet = eventMap.Values.ToList();
Of course in the sample case the result set will contain only 0 or 1 items, but the above is applicable regardless of the applied filtering.

C# driver 2.0 Mongodb UpdateOneAsync

public class Student
{
public long StudentId {get; set;}
public string Fname {get; set;}
public string Lname {get; set;}
public List<ObjectId> CoursesList {get; set;}
public int IQ {get;set;}
}
public class Courses
{
[BsonId]
public ObjectId Id { get; set; }
public string CourseNumber{get; set;}
public string CourseName{get; set;}
}
How do I add/append a courser Id to Course list(which may be null for the first time) of a Student object
PS: I know how to set a field using the below command. I am hoping it is on similar lines for the above problem
await StudentCollection.UpdateOneAsync(a => a.StudentId == studentId, Builders<Student>.Update.Set( a => a.IQ,90));
As you've already discovered, the C# code to use $addToSet is:
var filter = Builders<Student>.Filter.Eq(s => s.StudentId, studentId);
var update = Builders<Student>.Update.AddToSet(s => s.CoursesList, courseId);
var result = await collection.UpdateOneAsync(filter, update);
However, $addToSet is not going to work if the CourseList member has been stored in the collection as a null. The server requires that the existing value for $addToSet be an array (it can be an empty array).
The easiest solution is to just store an empty list for CoursesList instead of a null when there are no courses.
This is working for me. When you define "List" like this, it will be empty and works with AddToSet/Push methods.
public List<ObjectId> CoursesList = new List<ObjectId>();
The only case that you have to pay attention is when the array CourseList is null, in this case you have to use the code below (see also here):
var newListOfObject = new List<ObjectId>(){...}
await StudentCollection.UpdateOneAsync(a => a.StudentId == studentId, Builders<Student>.Update.Set( a => a.CoursesList, newListOfObject));
Otherwise you can use AddToSet or Push like explain in the other answers.

Linq create an array of Dto objects, with an array in it

I have a rich NHibernate dbo object that I need to query to extract a keyword and related array of country codes.
To explain:
I have 3 related DBO objects returned from a database, that have the following structure:
class DboCountry{
public int CountryId {get;set;}
public string CountryCode {get;set;}
}
class DboPage{
public int Id {get;set;}
public string Keyword {get;set;}
}
class DboPageCountry{
public int Id {get;set;}
public DboThemePage DboThemePage {get;set;}
public DboCountry DboCountry {get;set;}
}
As such when querying the database I get a List of DboPageCountries that contain multiple repeating DboPage.Keyword that are associated to various DboCountry.CountryCode
What I need to do is take the List of DboPageCountries and create an array of DtoKeywordCountryCode with the following structure:
class DtoKeywordCountryCode{
public string Keyword {get; set;}
public string[] CountryCodes {get; set;}
}
So far, using Linq, I've either been able to group the keywords, but not get the associated country codes, or get keywords and country codes, but not associated as a unique Keyword against an array of applicable CountryCodes.
Any pointers in the right direction much appreciated.
Try this -
var items = new List<DboPageCountry>(); //list of DboPageCountries
var items2 = from x in items.GroupBy(x => x.DboThemePage)
select new DtoKeywordCountryCode { Keyword = x.First().DboThemePage.Keyword, CountryCodes = x.Select(c => c.DboCountry.CountryCode).ToArray() };
DtoThemePage[] dtoThemePageArrayFull = (from tpc in dboThemePageCountryList group tpc by tpc.DboThemePage.Keyword into k
select new DtoThemePage
{
Keyword = k.Key,
CountryCodes = k.Select(c => c.DboCountry.CountryCode).ToArray<string>()
}).ToArray<DtoThemePage>();

Categories