How can i write a compatible c# code for this?
I know i can do projection like this:
var projection = Builders<BsonDocument>.Projection.Include("title");
But no idea how to project the last name to get the author's last name after a lookup aggregation
db.books.aggregate(
[
{
$project: {
title: 1,
lastName: "$author.lastName",
}
}
]
)
Try this one
var project = new BsonDocument
{
{
"$project",
new BsonDocument
{
{"title", 1},
{"lastName", "$author.lastName"},
}
}
};
var pipelineLast = new[] { project };
var resultLast = db.books.Aggregate<BsonDocument>(pipelineLast);
var matchingExamples = await resultLast.ToListAsync();
foreach (var example in matchingExamples)
{
// Display the result
}
assuming your author entity is embedded inside the book entity, here's a strongly typed solution. if your author is a referenced entity, let me know and i'll update my answer.
using MongoDB.Entities;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Book : Entity
{
public string Title { get; set; }
public Author Author { get; set; }
}
public class Author
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
(new Book
{
Title = "a book title goes here",
Author = new Author
{
FirstName = "First Name",
LastName = "Last Name"
}
}).Save();
var res = DB.Queryable<Book>()
.Select(b => new
{
Title = b.Title,
LastName = b.Author.LastName
}).ToArray();
foreach (var b in res)
{
Console.WriteLine($"title: {b.Title} / lastname: {b.LastName}");
}
Console.Read();
}
}
}
the above code is using my library MongoDB.Entities for brevity. simply replace DB.Queryable<Book>() with collection.AsQueryable() for the official driver.
Related
I have this code
var now = DateTime.UtcNow;
var currentRoleInsights = dbContext.DocumentInsights.Where(dr =>
dr.DocumentID == data.ID &&
insightIDsToDelete.Contains(dr.InsightID)).ToList();
foreach (var r in currentRoleInsights)
{
r.StatusID = StatusType.Deleted;
r.DeletedByAMSUserID = amsUserID;
r.DateDeleted = now;
}
Now if I query again using the code below:
var data = dbContext.DocumentInsights.Where(dr =>
dr.DocumentID == data.ID &&
insightIDsToDelete.Contains(dr.InsightID)).ToList();
will I get the data that is already updated with its status?
Note I did not yet call the dbContext.SaveChanges() and I don't want my code to look like this
var now = DateTime.UtcNow;
var currentRoleInsights = dbContext.DocumentInsights.Where(dr =>
dr.DocumentID == data.ID &&
insightIDsToDelete.Contains(dr.InsightID)).ToList();
foreach (var r in currentRoleInsights)
{
r.StatusID = StatusType.Deleted;
r.DeletedByAMSUserID = amsUserID;
r.DateDeleted = now;
}
dbContext.SaveChanges()
var data = dbContext.DocumentInsights.Where(dr =>
dr.DocumentID == data.ID &&
insightIDsToDelete.Contains(dr.InsightID)).ToList();
// modify data
will I get the data that is already updated with its status?
yes
// #nuget: EntityFramework
using System;
using System.Data.Entity;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
public class Program
{
public static void Main()
{
InsertData();
using (var context = new BookStore())
{
var authors = context.Authors.Include(a => a.Books).ToList();
DisplayData(authors);
foreach(var a in authors){
a.Books.ToList().ForEach(x=>x.Title = x.Title + " 2019");
}
Console.WriteLine();
authors = context.Authors.Include(a => a.Books).ToList();
DisplayData(authors);
}
}
public static void DisplayData(List<Author> list)
{
foreach(var author in list)
{
Console.WriteLine("Author Name: " + author.Name);
Console.WriteLine("\tBook List:");
foreach(var book in author.Books)
{
Console.WriteLine("\t\t" + book.Title);
}
}
}
public static void InsertData()
{
using (var context = new BookStore())
{
Author author1 = new Author()
{
Name = "Mark",
Books = new List<Book>
{
new Book() { Title = "Fundamentals of Computer Programming with C#"},
new Book() { Title = "Java: A Beginner's Guide"},
}
};
Author author2 = new Author()
{
Name = "Andy",
Books = new List<Book>
{
new Book() { Title = "SQL: The Ultimate Beginners Guide"}
}
};
Author author3 = new Author()
{
Name = "Johny",
Books = new List<Book>
{
new Book() { Title = "Learn VB.NET"},
new Book() { Title = "C# Fundamentals for Absolute Beginners"},
}
};
context.Authors.Add(author1);
context.Authors.Add(author2);
context.Authors.Add(author3);
context.SaveChanges();
}
}
public class BookStore : DbContext
{
public BookStore() : base(FiddleHelper.GetConnectionStringSqlServer())
{
}
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
}
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
[ForeignKey("AuthorId")]
public Author Author { get; set; }
}
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; }
public ICollection<Book> Books { get; set; }
}
}
copy and past the code above to https://dotnetfiddle.net/
This question is quite on the same principles as this one but I'd like to create an index using strongly typed approach on an object property when this object is nested in an array of the collection.
I can use:
new CreateIndexModel<T>( Builders<T>.IndexKeys.Ascending( a ) )
where a is an Expression which accesses to a direct property.
But I've found nothing similar to:
Builders<Library>.Filter.ElemMatch(x => x.Author.Books, b => b.IsVerified == false));
so that I can define as index some field of an object nested in an array which is a member of the collection.
Is that possible to do it and how?
Consider the following data model:
public class Course
{
public string Name { get; set; }
public string Teacher { get; set; }
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public ReadOnlyCollection<Course> Courses { get; set; }
}
You can create an ascending multikey index on the field Courses in the following manner:
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class Program
{
private static MongoClient Client;
private static IMongoDatabase Database;
private static IMongoCollection<Student> Collection;
public static async Task Main(string[] args)
{
Client = new MongoClient();
Database = Client.GetDatabase("test-index");
Collection = Database.GetCollection<Student>("students");
var courses1 = new List<Course>()
{
new Course { Name = "Math", Teacher = "Bob" }
}.AsReadOnly();
var courses2 = new List<Course>()
{
new Course { Name = "Computer Science", Teacher = "Alice" }
}.AsReadOnly();
var mark = new Student
{
Name = "Mark",
Courses = courses1,
Age = 20
};
var lucas = new Student
{
Name = "Lucas",
Courses = courses2,
Age = 22
};
await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);
var model = new CreateIndexModel<Student>(
Builders<Student>.IndexKeys.Ascending(s => s.Courses));
await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);
Console.WriteLine("All done !");
}
}
}
This query is served by the index you created:
db.students.find({Courses: {"Name": "Math", "Teacher": "Bob"}})
If you don't want to create an index on the entire array Courses, but instead you want an index on the field Name of the nested object (the Course object), this is the way to go:
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class Program
{
private static MongoClient Client;
private static IMongoDatabase Database;
private static IMongoCollection<Student> Collection;
public static async Task Main(string[] args)
{
Client = new MongoClient();
Database = Client.GetDatabase("test-index");
Collection = Database.GetCollection<Student>("students");
var courses1 = new List<Course>()
{
new Course { Name = "Math", Teacher = "Bob" }
}.AsReadOnly();
var courses2 = new List<Course>()
{
new Course { Name = "Computer Science", Teacher = "Alice" }
}.AsReadOnly();
var mark = new Student
{
Name = "Mark",
Courses = courses1,
Age = 20
};
var lucas = new Student
{
Name = "Lucas",
Courses = courses2,
Age = 22
};
await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);
var model = new CreateIndexModel<Student>(
Builders<Student>.IndexKeys.Ascending("Courses.Name"));
await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);
Console.WriteLine("All done !");
}
}
}
This query is served by the index you created: db.students.explain("executionStats").find({"Courses.Name": "Math"})
A possible way to avoid the usage of magic strings in my second example is exploiting the power of the nameof C# operator:
$"{nameof(Student.Courses)}.{nameof(Course.Name)}"
here's the strongly typed way to created indexes for nested fields using the MongoDB.Entities convenience library. [disclaimer: i'm the author]
using MongoDB.Entities;
using System.Collections.Generic;
namespace StackOverflow
{
public class Program
{
public class Parent : Entity
{
public Child[] Children { get; set; }
}
public class Child
{
public List<Friend> Friends { get; set; }
}
public class Friend
{
public string Name { get; set; }
}
static void Main(string[] args)
{
new DB("test");
DB.Index<Parent>()
.Key(p => p.Children[-1].Friends[-1].Name, KeyType.Ascending)
.Create();
}
}
}
the above creates an ascending index on the name field which is nested two levels deep with the following command:
db.Parent.createIndex({
"Children.Friends.Name": NumberInt("1")
}, {
name: "Children.Friends.Name(Asc)",
background: true
})
I have got a method with a single parameter of string class that I need to use to access another class in my project.
The following will be what I want it to do. Note that this syntax gives errors.
public string getId(string name) {
string Id = name.GetId();
return Id;
}
Assuming that the user enters "Joe" as the name, one would go to the class Joe.cs, which looks like this.
public class Joe {
public string Id = 32;
public string GetId() {
return Id;
}
}
What I want to happen here is for the first method to be able to get the GetId method from Joe if Joe is entered as a parameter. How would I do this? Thank you all.
This might point you in a better direction
The idea is to have a class called User that holds user information (funnily enough)
This way you can have a list of users (not a class for each one), as such you can easily look up a user and mess with them as much as you want
public class User
{
public string UserName { get; set; }
public string FavoriteColor { get; set; }
}
public class Program
{
// A List to hold users
private static List<User> _users = new List<User>();
private static void Main(string[] args)
{
// lets add some people
_users.Add(new User() { UserName = "Bob",FavoriteColor = "Red" });
_users.Add(new User() { UserName = "Joe", FavoriteColor = "Green" });
_users.Add(new User() { UserName = "Fred", FavoriteColor = "Blue" });
// use a linq query to find someone
var user = _users.FirstOrDefault(x => x.UserName == "Bob");
// do they exist?
if (user != null)
{
// omg yay, gimme teh color!
Console.WriteLine(user.FavoriteColor);
}
}
}
Output
Red
You can take it a step further and ask the user to look up other users (what a time to be a alive!)
Console.WriteLine("Enter a user (case sensitive)");
var userName = Console.ReadLine();
var user = _users.FirstOrDefault(x => x.UserName == userName);
if (user != null)
{
Console.WriteLine(user.FavoriteColor);
}
else
{
Console.WriteLine("Game over, you failed");
}
Console.ReadLine();
You could build a Class that can contain the details you want to store, then build a Manager class that exposes public methods that can extract informations from the stored objects:
public class Friend : IComparable<Friend>
{
public Friend() { }
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int FriendshipLevel { get; set; }
int IComparable<Friend>.CompareTo(Friend other)
{
if (other.FriendshipLevel > this.FriendshipLevel) return -1;
else return (other.FriendshipLevel == this.FriendshipLevel) ? 0 : 1;
}
}
public class MyFriends : List<Friend>
{
public MyFriends() { }
public int? GetID(string FriendName)
{
return this.Where(f => f.FirstName == FriendName).FirstOrDefault()?.ID;
}
public Friend GetFriendByID(int FriendID)
{
return this.Where(f => f.ID == FriendID).FirstOrDefault();
}
}
Build a sample class:
MyFriends myFriends = new MyFriends()
{
new Friend() { ID = 32, FirstName = "Joe", LastName = "Doe", FriendshipLevel = 100},
new Friend() { ID = 21, FirstName = "Jim", LastName = "Bull", FriendshipLevel = 10},
new Friend() { ID = 10, FirstName = "Jack", LastName = "Smith", FriendshipLevel = 50},
};
Then you can extract informations on single/multiple objects using the public methods of the "Manager" class:
int? ID = myFriends.GetID("Joe");
if (ID.HasValue) // Friend Found
Console.WriteLine(ID);
//Search a friend by ID
Friend aFriend = myFriends.GetFriendByID(32);
if (aFriend != null)
Console.WriteLine($"{aFriend.FirstName} {aFriend.LastName}");
Or you can use LINQ to get/aggregate the required informations directly if there isn't a public method that fits:
// Get your best friends using LINQ directly
List<Friend> goodFriends = myFriends.Where(f => f.FriendshipLevel > 49).ToList();
goodFriends.ForEach((f) => Console.WriteLine($"{f.FirstName} {f.LastName}"));
//Best friend
Friend bestFriend = myFriends.Max();
With respect to your Question : GetId method from Joe if Joe is entered as a
parameter.
You can achieve that in the following ways also:
2. Method Using Named Method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = new PersonHandler(GetIdOfPerson);
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
// This is the GetId Method.
static string GetIdOfPerson(Person person)
{
string Id = person.Id.ToString();
return Id;
}
}
}
2. Method Using Anonymous Moethod.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = delegate(Person person)
{
string id = person.Id.ToString();
return id;
};
// Retrieving all person Id's.
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
}
}
2. Method Using a Lambda Expression.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = (Person person) => person.Id.ToString();
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
}
}
Output:
I got this code and it works without problems. But i sense there is much better way to do this.
namespace Repositories
{
public class AuthorRepository : IAuthorRepository
{
public List<Author> GetAllFromRepo()
{
using (AppContext myDB = new AppContext())
{
List<Author> authorsFromRepo = new List<Author>();
foreach (var item in myDB.Authors)
{
authorsFromRepo.Add(new Author()
{
Books = new List<Book>(),
ID = item.ID,
FirstName = item.FirstName,
LastName = item.LastName
});
}
return authorsFromRepo.ToList();
}
}
}
}
When i try something along the lines of this:
public List<Author> GetAllFromRepo()
{
using (AppContext myDB = new AppContext())
{
List<Author> authorsFromRepo = new List<Author>();
authorsFromRepo = myDB.Authors.ToList();
return authorsFromRepo;
}
}
I always get this error:
Value cannot be null.
Parameter name: source
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source
Source Error: Line 33: return authors.Select(x => new AuthorViewModel()
Any Help?
The model where the error takes me
namespace Services
{
public class AuthorService : IAuthorService
{
private readonly IAuthorRepository _AuthorRepository;
public AuthorService(IAuthorRepository authorRepository)
{
_AuthorRepository = authorRepository;
}
public List<AuthorViewModel> GetAll()
{
List<Author> authors = _AuthorRepository.GetAllFromRepo();
return authors.Select(x => new AuthorViewModel()
{
ID = x.ID,
FullName = $"{x.FirstName } {x.LastName} ",
Books = x.Books.Select(g => new BookViewModel()
{
ID = g.ID,
Name = g.Name
}).ToList()
}).ToList();
}
}
}
To add again, everything works fine if i use the first example of code.
When i try something shorter like
return myDB.Authors.ToList();
i get the error.
when i change to:
return authors.Select(x => new AuthorViewModel()
{
ID = x.ID,
FullName = $"{x.FirstName } {x.LastName} ",
Books = {}
}).ToList();
It works then... but this means it doesn't read the author books...
I had to change the model for Authors to
namespace Entities
{
public class Author
{
// this is what a had to add here
// From here
public Author()
{
Books = new List<Book>();
}
// to here
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Book> Books { get; set; }
}
}
So It wont give me any errors when x.Books is null.
I've been playing with using the following code as a way to allow users to build custom data-sets/charts at run-time in an ASP.NET MVC 5 web application. The gist of it is that there is some boilerplate code which the user's LINQ query (as a string) is naively injected into, then the whole string is run through a VBCodeCompiler.
I've tried checking for "End<anyamountofwhitespace>Function" and also checking the number of methods in the compiled assembly, neither of which scream "I am totally confident that there aren't injection vulnerabilities here!".
Is there a better way to validate the LINQ query string?
If you want to try the example code, you'll need to install the following packages...
install-package entityframework
install-package newtonsoft.json
using System;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic;
using System.CodeDom.Compiler;
using Model;
using System.Collections.Generic;
using System.Data.Entity;
using Newtonsoft.Json;
namespace _30742268 {
class Program {
const String queryWrapperCode = #"
Imports System.Linq
Imports System.Data.Entity
Imports Model
Public Class DynamicQuery
Implements IDynamicQuery
Public Function Run(data As IContext) As IQueryable Implements IDynamicQuery.Run
Return {0}
End Function
End Class
";
static void Main(String[] args) {
using (var provider = new VBCodeProvider()) {
var parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("EntityFramework.dll");
parameters.ReferencedAssemblies.Add("30742268.exe");
parameters.GenerateInMemory = true;
Console.WriteLine("Enter LINQ queries, 'demo' for an example, 'exit' to stop:");
for (;;) {
try {
var dynamicQueryString = Console.ReadLine();
if (dynamicQueryString == "exit")
return;
if (dynamicQueryString == "demo")
Console.WriteLine(dynamicQueryString = "from person in data.People where person.Name.Length = 4");
var results = provider.CompileAssemblyFromSource(parameters, String.Format(queryWrapperCode, dynamicQueryString));
if (results.Errors.HasErrors) {
var sb = new StringBuilder();
foreach (CompilerError error in results.Errors) {
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
var assembly = results.CompiledAssembly;
var assemblyType = assembly.GetTypes().Single(x => typeof (IDynamicQuery).IsAssignableFrom(x));
var constructorInfo = assemblyType.GetConstructor(new Type[] {});
var dynamicQuery = (IDynamicQuery) constructorInfo.Invoke(null);
using (var context = new Context()) {
dynamic result = dynamicQuery.Run(context);
foreach (var person in result)
Console.WriteLine(person);
}
}
catch (Exception exception) {
Console.WriteLine(exception);
}
}
}
}
}
}
namespace Model {
public interface IDynamicQuery {
IQueryable Run(IContext context);
}
public abstract class Entity {
public override String ToString() {
return JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
}
}
public class Person : Entity {
public Int64 Id { get; protected set; }
public String Name { get; set; }
public virtual Home Home { get; set; }
}
public class Home : Entity {
public Int64 Id { get; protected set; }
public String Address { get; set; }
public virtual ICollection<Person> Inhabitants { get; set; }
}
public interface IContext {
IQueryable<Person> People { get; set; }
IQueryable<Home> Homes { get; set; }
}
public class Context : DbContext, IContext {
public virtual DbSet<Person> People { get; set; }
public virtual DbSet<Home> Homes { get; set; }
IQueryable<Person> IContext.People {
get { return People; }
set { People = (DbSet<Person>)value; }
}
IQueryable<Home> IContext.Homes {
get { return Homes; }
set { Homes = (DbSet<Home>)value; }
}
public Context() {
Configuration.ProxyCreationEnabled = false;
Database.SetInitializer(new ContextInitializer());
}
}
class ContextInitializer : DropCreateDatabaseAlways<Context> {
protected override void Seed(Context context) {
var fakeSt = new Home {Address = "123 Fake St."};
var alabamaRd = new Home {Address = "1337 Alabama Rd."};
var hitchhikersLn = new Home {Address = "42 Hitchhiker's Ln."};
foreach (var home in new[] {fakeSt, alabamaRd, hitchhikersLn})
context.Homes.Add(home);
context.People.Add(new Person { Home = fakeSt , Name = "Nick" });
context.People.Add(new Person { Home = fakeSt , Name = "Paul" });
context.People.Add(new Person { Home = fakeSt , Name = "John" });
context.People.Add(new Person { Home = fakeSt , Name = "Henry" });
context.People.Add(new Person { Home = alabamaRd , Name = "Douglas" });
context.People.Add(new Person { Home = alabamaRd , Name = "Peter" });
context.People.Add(new Person { Home = alabamaRd , Name = "Joshua" });
context.People.Add(new Person { Home = hitchhikersLn, Name = "Anne" });
context.People.Add(new Person { Home = hitchhikersLn, Name = "Boris" });
context.People.Add(new Person { Home = hitchhikersLn, Name = "Nicholes" });
context.People.Add(new Person { Home = hitchhikersLn, Name = "Betty" });
context.SaveChanges();
}
}
}