Linq Order list within a list based on parent Id - asp.net

Having a list of items I need to sort/order items within that list based on outter list's id
public class Venue
{
public int Id { get; set; }
public string Code { get; set; }
public IEnumerable<Machines> Machines { get; set; }
}
public class Machine
{
public string Name { get; set; }
}
Suppose I have a list of 3 venues and I want to sort only Venue's Machines where Venue.Id = 1;
Having a list of venues, I want to always sort by Venue Code, THEN sort machines by Name of Venue with Id = 1;
I tried this but doesn't work correctly:
query = query.OrderBy(x => x.Code).ThenBy(y => y.Machines.OrderBy(q => q.Name).Where(x => y.Id == 1))

If you want to sort a property of an object which is an IEnumerable<T> you need to select a new:
query = query
.Select(v => new Venue
{
Id = v.Id,
Code = v.Code,
Machines = v.Id == 1 ? v.Machines.OrderBy(m => m.Name) : v.Machines
})
.OrderBy(v => v.Code);

Related

RavenDB array search returns random results

I'm trying to perform a search on top of a dictionary using the Search method from RavenDB 4. Strangely, if the search term is the word in or it I get random results back. I'm absolutely sure that none of the records contains those words. It also happens when executing the equivalent lucene query on the studio. It works as expected when I enter a valid search term like the employee's name, number, etc.
I've managed to create this simple scenario based on the real one.
Here's the index:
public class Search : AbstractIndexCreationTask<Employee, Page>
{
public Search()
{
Map = employees => from employee in employees
select new
{
Id = employee.Id,
Details = employee.Details
};
Reduce = results => from result in results
group result by new
{
result.Id,
result.Details
}
into g
select new
{
g.Key.Id,
g.Key.Details
};
Index("Details", FieldIndexing.Search);
}
}
Employee class:
public class Employee
{
public string Id { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Adding employees:
details = new Dictionary<string, object>();
details.Add("EmployeeNo", 25);
details.Add("FirstNames", "Yuri");
details.Add("Surname", "Cardoso");
details.Add("PositionCode", "XYZ");
details.Add("PositionTitle", "Developer");
employee = new Employee
{
Details = details
};
session.Store(employee);
session.SaveChanges();
Search method:
var searchTerm = "in";
var result = session
.Query<Page, Search>()
.Search(i => i.Details, $"EmployeeNo:({searchTerm})")
.Search(i => i.Details, $"FirstNames:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"Surname:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"PositionCode:({searchTerm})", options: SearchOptions.Or)
.Search(i => i.Details, $"PositionTitle:({searchTerm})", options: SearchOptions.Or)
.ToList();
Lucene query outputed:
from index 'Search' where search(Details, "EmployeeNo:(it)")
or search(Details, "FirstNames:(it)")
or search(Details, "Surname:(it)")
or search(Details, "PositionCode:(it)")
or search(Details, "PositionTitle:(it)")
Any idea why random results are returned when those specific words are enterered?
The issue is stop words. Certain terms are so common, that they are meaningless for searching using full text search.
is, it, they, are, etc.
They are erased by the query analyzer.
See the discussion here: https://ravendb.net/docs/article-page/4.2/Csharp/indexes/using-analyzers
You can use a whitespace analyzer, instead of the Standard Analyzer, since the former doesn't eliminate stop words.
After getting help from the RavenDB group guys, we've managed to find a solution for my scenario.
Employee:
public class Employee
{
public string Id { get; set; }
public string DepartmentId { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Department:
public class Department
{
public string Id { get; set; }
public string Name { get; set; }
}
Page:
public class Page
{
public string Id { get; set; }
public string Department { get; set; }
public Dictionary<string, object> Details { get; set; }
}
Index (with dynamic fields):
public class Search : AbstractIndexCreationTask<Employee, Page>
{
public Search()
{
Map = employees => from employee in employees
let dept = LoadDocument<Department>(employee.DepartmentId)
select new
{
employee.Id,
Department = dept.Name,
_ = employee.Details.Select(x => CreateField(x.Key, x.Value))
};
Store(x => x.Department, FieldStorage.Yes);
Index(Constants.Documents.Indexing.Fields.AllFields, FieldIndexing.Search);
}
}
Query:
using (var session = DocumentStoreHolder.Store.OpenAsyncSession())
{
var searchTearm = "*yu* *dev*";
var result = await session
.Advanced
.AsyncDocumentQuery<Page, Search>()
.Search("Department", searchTearm)
.Search("EmployeeNo", searchTearm)
.Search("FirstNames", searchTearm)
.Search("Surname", searchTearm)
.Search("PositionCode", searchTearm)
.Search("PositionTitle", searchTearm)
.SelectFields<Page>()
.ToListAsync();
}
Everything seems to be working fine this way, no more random results.
Big thanks to Ayende and Egor.

A circular reference was detected while serializing entities with one to many relationship

How to solve one to many relational issue in asp.net?
I have Topic which contain many playlists.
My code:
public class Topic
{
public int Id { get; set; }
public String Name { get; set; }
public String Image { get; set; }
---> public virtual List<Playlist> Playlist { get; set; }
}
and
public class Playlist
{
public int Id { get; set; }
public String Title { get; set; }
public int TopicId { get; set; }
---> public virtual Topic Topic { get; set; }
}
My controller function
[Route("data/binding/search")]
public JsonResult Search()
{
var search = Request["term"];
var result= from m in _context.Topics where m.Name.Contains(search) select m;
return Json(result, JsonRequestBehavior.AllowGet);
}
When I debug my code I will see an infinite data because Topics will call playlist then playlist will call Topics , again the last called Topic will recall playlist and etc ... !
In general when I just use this relation to print my data in view I got no error and ASP.NET MVC 5 handle the problem .
The problem happens when I tried to print the data as Json I got
Is there any way to prevent an infinite data loop in JSON? I only need the first time of data without call of reference again and again
You are getting the error because your entity classes has circular property references.
To resolve the issue, you should do a projection in your LINQ query to get only the data needed (Topic entity data).
Here is how you project it to an anonymous object with Id, Name and Image properties.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you have a view model to represent the Topic entity data, you can use that in the projection part instead of the anonymous object
public class TopicVm
{
public int Id { set;get;}
public string Name { set;get;}
public string Image { set;get;}
}
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new TopicVm
{
Id = x.Id,
Name = x.Name,
Image = x.Image
});
return Json(result, JsonRequestBehavior.AllowGet);
}
If you want to include the Playlist property data as well, you can do that in your projection part.
public JsonResult Search(string term)
{
var result = _context.Topics
.Where(a => a.Name.Contains(term))
.Select(x => new
{
Id = x.Id,
Name = x.Name,
Image = x.Image,
Playlist = x.Playlist
.Select(p=>new
{
Id = p.Id,
Title = p.Title
})
});
return Json(result, JsonRequestBehavior.AllowGet);
}

Can I do an UPDATE on a JOIN query with OrmLite on ServiceStack?

I want to do an update for a specific field on a table based on the results from a query that includes a join. Using OrmLite with ServiceStack.
My Classes are as follows:
public class Document
{
public int Id { get; set; }
public string BCL_Code { get; set; }
public bool IsActive { get; set; }
public int DocumentTypeId { get; set; }
}
public class DocumentType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
Trying to do the Update on the Join with the following code:
var q = db.From<Document>()
.Join<Document, DocumentType>(
(doc, type) => doc.DocumentTypeId == type.Id)
.Where(d => d.BCL_Code == "FR49")
.And<DocumentType>(t => t.TypeName == "Enrollment Process");
db.UpdateOnly(new Document { IsActive = false }, onlyFields: q);
I know I can update specific fields, and I know how to do joins, but when I try to include a join in the query, and then do an UpdateOnly, I get an error message on the db.UpdateOnly line:
The multi-part identifier "DocumentType.TypeName" could not be bound.
Is it possible to do an Update on a Join Query?
If so, what is the proper way to do it?
There's no Typed APIs for Update From Table in OrmLite yet, you can add a feature request for it.
In the meantime you can use Custom SQL, e.g:
db.ExecuteSql(#"UPDATE Document SET IsActive = #isActive
FROM Document d
INNER JOIN DocumentType t ON (d.DocumentTypeId = t.Id)
WHERE d.BCL_Code = 'FR49'
AND t.TypeName = 'Enrollment Process'",
new { isActive = false });

Code First relationship one to many / many to many

I have a table restaurant. A restaurant can have multiple specialties. And specialties are for example chinees, spahnish, greeks, etc.
Restaurant table/class:
[Table("Restaurant")]
public class Restaurant
{
[Key]
[Column(Order = 0)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(40)]
[Column(Order = 2)]
public string Name { get; set; }
[MaxLength(1000)]
[Column(Order = 6)]
public string Comments { get; set; }
public virtual ICollection<Specialties> Specialties { get; set; }
}
And this is my specialties table/class:
public enum Specialty
{
Chinees = 0,
Spanish = 1,
Etc.. = 2,
}
[Table("Specialties")]
public class Specialties
{
[Key]
[Column(Order = 0)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[Column(Order = 1)]
public Specialty Specialty { get; set; }
//[Required]
[MaxLength(20)]
[Column(Order = 2)]
public string Name { get; set; }
public virtual ICollection<Restaurant> Restaurant { get; set; }
}
The specialties table is pre-filled with data.
This is my table/class for the many to many relationship:
[Table("RestaurantSpecialties")]
public class RestaurantSpecialties
{
[Key]
public int RestaurantId { get; set; }
[Key]
public int SpecialtyId { get; set; }
[ForeignKey("RestaurantId")]
public virtual Restaurant Restaurant{ get; set; }
[ForeignKey("SpecialtyId")]
public virtual Specialties Specialty { get; set; }
}
With Fluent Api I tried to define the model like this:
// Primary keys
modelBuilder.Entity<Restaurant>().HasKey(q => q.Id);
modelBuilder.Entity<Specialties>().HasKey(q => q.Id);
// Relationship
modelBuilder.Entity<Restaurant>()
.HasMany(q => q.Specialties)
.WithMany(q => q.Restaurant)
.Map(q =>
{
q.ToTable("RestaurantSpecialty");
q.MapLeftKey("RestaurantId");
q.MapRightKey("SpecialtyId");
});
And in my Seed function i'm seeding data like this:
context.Specialties.AddOrUpdate(
p => p.Specialty,
new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
);
ICollection<Specialties> lstSpecialties = new List<Specialties>();
lstSpecialties.Add(context.Specialties.FirstOrDefault(x => x.Name == "Chinees"));
context.Restaurants.AddOrUpdate(
p => p.Name,
new Restaurant{ Name = "Ctr test1", Specialties = lstSpecialties, Comments = "sfasd" },
new Restaurant{ Name = "Ctr test2", Specialties = lstSpecialties, Comments = "asd" },
);
But my table RestaurantSpecialties doesn't contain anything. I was expecting to see Id's of both tables.
What am I doing wrong?
[EDIT]
The enum has been renamed from 'Specialty' to 'RestaurantSpecialty' like Gert suggested to do (just the rename part, not the actual name).
The plural class 'Specialties' has also been renamed to 'Specialty'. Also because Gert suggested it.
The join table has also been removed from the model. The Fluent Api part in my original post has been updated accordingly.
Catering(s) has been renamed to Restaurant(s) which i forgot to do so in the beginning. So to avoind confusions i've done this too in my original code post.
context.Specialties.AddOrUpdate(
new Specialty { CateringSpecialty = RestaurantSpecialty.Chinese, Name = "Chinese" },
new Specialty { CateringSpecialty = CateringSpecialty.Greeks, Name = "Greeks" },
);
var aSpecialty = context.Specialties.FirstOrDefault(x => x.Name == "Chinese");
var restaurant1 = context.Restaurants.Include(c => c.Specialties).FirstOrDefault(x => x.Name == "Ctr test1");
if (restaurant1 == null)
{
restaurant1 = new Restaurant
{
Name = "Ctr test4",
Specialties = new List<Specialty> { aSpecialty },
Comments = "testtttttttt"
};
context.Restaurants.Add(restaurant1);
}
else
{
if (!restaurant1.Specialties.Any(s => s.Id == aSpecialty.Id))
restaurant1.Specialties.Add(aSpecialty);
restaurant1.Name = "Ctr test4";
restaurant1.Comments = "testtttt";
}
Try to add/update manually. I suspect that the AddOrUpdate method does not support updating a full object graph of related entities. Probably it supports adding related entities together with its parent entity if the parent entity doesn't exist yet. But if the "Chinees" specialty was already in the database without related entities the relationships possibly don't get updated.
A "manual update" would look like this:
context.Specialties.AddOrUpdate(
p => p.Specialty,
new Specialties { Specialty = Specialty.Chinees, Name = "Chinees" },
new Specialties { Specialty = Specialty.Spanish, Name = "Spanish" },
);
var chineesSpecialty = context.Specialties
.FirstOrDefault(x => x.Name == "Chinees");
var catering1 = context.Caterings.Include(c => c.Specialties)
.FirstOrDefault(x => x.Name == "Ctr test1");
if (catering1 == null)
{
catering1 = new Catering
{
Name = "Ctr test1",
Specialties = new List<Specialties> { chineesSpecialty },
Comments = "sfasd"
};
context.Caterings.Add(catering1);
}
else
{
if (!catering1.Specialties.Any(s => s.Id == chineesSpecialty.Id))
catering1.Specialties.Add(chineesSpecialty);
catering1.Comments = "sfasd";
}
// and the same for "Ctr test2"
This is also mentioned in this answer but it was during the early preview releases of Code-First Migrations, so I am not sure if this limitation still exists. But it would explain why you don't get entries in the relationship table.
Your mapping itself looks correct to me. (But as already recommended by Gert Arnold in the comments I would really remove the RestaurantSpecialties entity which looks redundant and only complicates your model.)

Linq to Entities - OrderBy Tags (many to many relationship)

asp.net mvc 4, Entity Framework 5, SQL Server 2012 Express, Code First
I have a Place model:
public virtual int PlaceID { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public virtual string Name { get; set; }
and a related Tag model:
public virtual int TagID { get; set; }
public virtual string Name { get; set; }
public virtual string NamePlural { get; set; }
public virtual ICollection<Place> Places { get; set; }
they have a many to many relationship.
I am selecting all places with the tag 'Restaurant':
List<Place> Restaurants = allPlaces.Where(
p => p.Tags.Any(
t => t.Name == "Restaurant")).OrderBy(p => p.Name).ToList();
I would like to display this by ordered tag - then sorted by name.
Lets say there are 5 Places:
My Cafe Bar, Tags="Restaurant", "Cafe", "Bar"
Another Cafe, Tags="Restaurant", "Cafe"
Marios Italian, Tags="Restaurant", "Italian", "Bar"
Donnies Pizzaria, Tags="Restaurant", "Italian"
A1 Chinese, Tags="Restaurant", "Chinese"
Fusion One, Tags="Restaurant", "Chinese", "Italian"
China Garden, Tags="Restaurant", "Chinese"
I would like to output in the below order (as they're all Restaurants there's no need to output a Restaurants group):
>>Bars
Marios Italian
My Cafe Bar
>>Cafes
Another Cafe
My Cafe Bar
>>Chinese
A1 Chinese
Fusion One
China Garden
>>Italian
Donnies Pizzaria
Fusion One
Marios Pizzaria
In my view I would like to display them as above - with headings.
Is this possible with Linq?
Thanks.
Looks like you're looking for a GroupBy, not OrderBy. Check this query:
var restaurants = allPlaces.Where(p => p.Tags.Any(t => t.Name == "Restaurant"))
.SelectMany(p => p.Tags, (p, t) => new { Tag = t.Name, Name = p.Name })
.GroupBy(i => i.Tag)
.Where(g => g.Key != "Restaurant")
.Select(g => new { Tag = g.Key, Places = g })
.ToList();
It will result a list of anonymous objects with two properties:
{
public string Tag { get; set; }
public List<string> Places { get; set;}
}
I'm pretty sure about the logic here, but not about LINQ to Entities support for all things used there.
I ended up looking at this a different way.
I simply pass all tags to the view (alphabetically sorted) - then loop through those. Whilst looping through each tag, I loop through each place within it (navigation property).
Very simple solution to the problem:
#foreach (var t in Model)
{
<h2>#t.Name</h2>
foreach (var p in t.Places)
{
<li>#Html.ActionLink(p.Name, "Details", new { URL = p.URL })</li>
}
}

Resources