Let's say I have the following document structure:
{
"_id" : id,
"name" : "Tom Cruise",
"movies" : [
{
"movie_id" : id,
"name" : "Mission Impossible",
"yr_released": 1996
},
{
"movie_id" : id,
"name" : "Minority Report",
"yr_released": 2002
}
]
}
Here are my POCO's:
public class Actor
{
[BsonId]
public int Id { get; set; }
[BsonElement("name")]
public string Name { get; set; }
[BsonElement("movies")]
public List<Movies> Movies { get; set; }
}
public class Movies
{
[BsonId]
public int Id { get; set; }
[BsonElement("name")]
public string Name { get; set; }
[BsonElement("yr_released")]
public int YearReleased
}
Let's say the next day, the document gets updated but the last movie name gets changed from "Minority Report" to "Vanilla Sky". I want to find out what changed in the sub documents. This is the sample code that I used (which doesn't work):
var yesterdayQuery = (yesterdayColl.AsQueryable<Actor>()
.Where (b => b.Name.Contains("Cruise"))).ToList();
var todayQuery = (todayColl.AsQueryable<Actor>()
.Where (b => b.Name.Contains("Cruise"))).ToList();
var diff = todayQuery.Except(yesterdayQuery);
Since Mongodb's C# driver doesn't have Except() support, I thought that if I used Linq to Objects as a workaround, I would be able to find the difference. I guess I was wrong. Basically, I would like to find the following differences between the documents:
If a property value has been changed
If a document or sub-document has been deleted
If a sub-document has been added
My question is: How can I write a strongly-typed query using the C# driver (hopefully) to achieve this?
If you want to know what changed and not just if it was changed it isn't really a MogngoDB question.
you need 2 copies of your data. Then you need to go property by property (with reflection if you want it to be more generic) and get the new values.
The old copy can be saved in the DB and the new one only in memory.
I'm not familiar with MongoDB, but I think you would write your class like this:
public class Actor
{
// equal objects must have equal hash codes
public override int GetHashCode()
{
return Id.GetHashCode();
}
// objects with the same Id are equal
public override bool Equals(object obj)
{
Actor other = obj as Actor;
if (other == null)
return false;
return this.Id == other.Id;
}
[BsonId]
public int Id { get; set; }
[BsonElement("name")]
public string Name { get; set; }
[BsonElement("movies")]
public List<Movies> Movies { get; set; }
}
Related
Is this possible? I am trying to avoid a lot of copying and pasting from area to area. I have a search function (I have reduced the code for simplicity).
if (!String.IsNullOrEmpty(filterVM.searchString))
{
var nameSearch = filterVM.searchString.ToLower();
guests = guests.Where(g => g.FirstName.ToLower().StartsWith(nameSearch)
|| g.LastName.ToLower().StartsWith(nameSearch)
)
}
filterVM.FilteredResultsCount = guests.CountAsync();
Guests can change from area to area, but it always has the same base things, like FirstName and LastName,
ex:
public class GuestBasicBase
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string GuestGuid { get; set; } = Guid.NewGuid().ToString();
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then I can have a bigger class for a particular area like
public class AreaOneGuest : GuestBasicBase
{
public int ID {get; set;}
public string ExtraFieldOne { get; set; }
public string ExtraFieldTwo { get; set; }
//Etc
}
I would like to have a function which will return a viewmodel and part of that viewmodel is PaginatedList and the other part is the Filter parameters, like this:
public class GuestBasicBaseIndexVM
{
public PaginatedList<T:GuestBasicBase> Guests { get; set; }
public GuestIndexFilterVM FilterVM { get; set; }
}
And I want a function to return this but take in a larger field, like
public async Task<GuestBasicBaseIndexVM>(T:GuestBasicBase, GuestIndexFilterVM filterVM){
//do search function
return (T where T: GuestBasicBase)
}
Does this question make sense and is it possible? Currently trying on my own and seeing what happens...I feel like it is sort of like the PaginatedList class but I am not certain
Not exactly what I wanted, but here is what I did. changed my BaseViewModel like this:
public class GuestBasicBaseIndexVM
{
public IEnumerable<GuestBasicBase> Guests { get; set; }
//Changed from a PaginatedList
public GuestIndexFilterVM FilterVM { get; set; }
}
and this function:
public static async Task<GuestBasicBaseIndexVM> CreateUpdatedGuestList(GuestIndexFilterVM filterVM, IQueryable<GuestBasicBase> guests)
{
//Code to search through guests and return filtered list and filters viewmodel
}
Then after the Ienumaerable of basic guests is returned I did this to connect them back to the AreaOne Guests
var x = await Helpers.CreateUpdatedGuestList(filterVM, guests);
var hsIDs = x.Guests.Select(v => v.GuestGuid).ToHashSet(); //Filtered GuestGuids to hashset
areaOneGuests = guests.Where(g => hsIDs.Contains(g.GuestGuid)) //This matches up the filtered list of base guests to the actual full guests.
//Then whatever code to do what I want with the AreaOne Guests....
It wasn't exactly what I was trying to do, but still saves me a lot of copying and pasting from area to area with similar base Guest classes. Have not been able to measure any noticeable performance loss/gain doing it this way.
I'm creating a synchronize function between a device and server for a large database. I have a lot of listing tables (the items in a dropdown/picker).
I don't want to write a lot of code and I'm looking for an elegant solution :)
On a device in SQLite I defined listing table like
public class NameTable : IBusinessEntity {
public int Id { get; set; } = 0;
public string Description { get; set; }
}
When I save in database a new record (item) I call this function
public int SaveItem<T>(T item) where T : IBusinessEntity {
lock (locker) {
if (item.Id != 0) {
database.Update(item);
return item.Id;
}
else {
return database.Insert(item);
}
}
}
Now when the device receives a new record from the server the structure is like
public class SyncResult {
public int DeviceId { get; set; }
public int ServerId { get; set; }
public string TableName { get; set; }
public string Description { get; set; }
}
Then I want to save (insert a new record if DeviceId == 0 or update an existing item).
My question is: how can I call SaveItem where T is the TableName from SyncResult?
Thank you in advance for any help (I can offer a beer for that!)
SaveItem is a member of MyDatabase class. Basically my problem is how to pass to SaveItem<T> the T as string.
I don't know if I explained clearly my issue.
You could map a TableName to a Type for example Dictionary<string,Type> Use the Activator class to construct the type. Use reflection to fill the data.
I'm using System.Linq.Dynamic with EntityFramework. My entities are below:
public class Customer
{
public Customer()
{
CustomerInterests = new List<CustomerInterest>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<CustomerInterest> CustomerInterests { get; set; }
}
public class CustomerInterest
{
public int Id { get; set; }
public int CustomerId { get; set; }
public int CourseId { get; set; }
public Course Course { get; set; }
}
public class Course
{
public int Id { get; set; }
public string Name { get; set; }
}
Below is my method:
public dynamic Get(long customerId)
{
var query = DbContext.Customers.Include("CustomerInterests").Include("CustomerInterests.Course").AsQueryable();
return query.Where(filter => filter.Id == customerId).Select("new(id,name,customerInterests)");
}
JSON result:
{
"id": 2003,
"name": "name customer",
"customerInterests": [
{
"customerId": 2003,
"courseId": 2,
"course": null,
"id": 2016
},
{
"customerId": 2003,
"courseId": 3,
"course": null,
"id": 2017
}
]
}
I'm trying to load the property Course, but it's always returning null as you can see in JSON result.
How can I create the selector new(....) to load correctly the property Course. I've already tried new (customerInterests.course) as customerInterests.course without success.
Do not forget that I am trying to navigate Customer (object) -> CustomerInterests (Collection) -> for each item load Course (object).
I would appreciate if you could help me on this matter.
I had faced that before, and my conclusion is: in Lambda extension methods you can load only 1st level of related object.
You can get list of customerInterests but you can't get foreign records for this list.
But you may use LINQ query instead.
var query=from c in DbContext.Customers
from ci in c.CustomerInterests
from co in ci.Courses
where /// your conditions
select new {
id=c.id,
name=c.name,
customerInterests= new {
customerId= ci.customerId,
courseId=ci.courseId ,
courses= new {
Name=co.Name
/// Other Courses attributes
}
}
}
EDITED
If you're using EF7 , you're able to load second level of foreign records by using ThenInclude method
db.Customers.Include( customer => customer.Orders). ThenInclude( order=> order.OrderDetails);
I have the following database table:
ID ParentID LinksTo
---------------------
0 null "1,2"
1 0 "0,2"
2 0 "1"
It's a hierarchical design, where each records points to a parent record from the same table. I know the LinksTo field is a very bad design, but the table is given, I cannot change that. (Note that the LinksTo field creates a sort of many-to-many relationship.)
Using Code-First EF, I have the following model class:
public class Item
{
public int ID { get; set; }
public int ParentID { get; set; }
public string LinksTo { get; set; }
public virtual Item Parent { get; set; }
[InverseProperty("Parent")]
public virtual ICollection<Item> Children { get; set; }
}
Now, how can I add a dynamic property to the model so that I can access the Item collections Item.LinksTo and Item.LinksFrom (the reverse) in code?
I guess I'd do something like this:
private List<Item> _linkedItems;
private void UpdateLinksTo() {
this.LinksTo = string.Join<string>(_linkedItems.Select(i => i.ID.ToString()));
}
[NotMapped]
public ReadOnlyCollection<Item> LinkedItems {
get {
if(_linkedItems == null) {
_linkedItems = db.Items.Where(i => this.LinksTo.Split(',').Select(x => int.Parse(x)).Contains(i.ID)).ToList();
}
return _linkedItems.AsReadOnly();
}
}
[NotMapped]
public void AddLinkedItem(Item item) {
if(!_linkedItems.Select(i => i.ID).Contains(item.ID)) {
_linkedItems.Add(item);
UpdateLinksTo();
}
}
That will give you access to a read-only collection with methods to alter it (you can also make a DeleteLinkedItem method) which is about as good as you're going to do I think.
I haven't checked to even see if this compiles, btw.
I am asking how to create an index based upon two different nested properties on an document. I am executing these queries through C#.
public class LocationCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class ColorCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class TestDocument
{
public int Id {get;set;}
public List<LocationCode> Locations { get; set; }
public List<ColorCode> Colors { get; set; }
}
I have experimented with various AbstractIndexCreationTask, Map, and Map+Reduce, but to no avail.
I would like to be able to do a query such as:
Get all documents where any Locations.Code property is "USA", AND/OR Colors.Code="RED", or on the SeqId property. I dont know whether this would mean I need multiple indexes. Normally I would either be comparing the Code property on both nested classes, or the Seq, but never mixed.
Please could someone point me in the right direction.
Many thanks
Phil
Create your index like this:
public class TestIndex : AbstractIndexCreationTask<TestDocument, TestIndex.IndexEntry>
{
public class IndexEntry
{
public IList<string> LocationCodes { get; set; }
public IList<string> ColorCodes { get; set; }
}
public TestIndex()
{
Map = testDocs =>
from testDoc in testDocs
select new
{
LocationCodes = testDoc.Locations.Select(x=> x.Code),
ColorCodes = testDoc.Colors.Select(x=> x.Code)
};
}
}
Then query it like this:
var q = session.Query<TestIndex.IndexEntry, TestIndex>()
.Where(x => x.LocationCodes.Any(y => y == "USA") &&
x.ColorCodes.Any(y => y == "RED"))
.OfType<TestDocument>();
Full unit test here.