Complex LINQ query - multiple table relationships - asp.net

I have a books database, which has an ICollection of authors. I want to return the author object based on the AuthorId using LINQ.
Book db
int BookId
string Name { get; set; }
public ICollection<Author> Authors { get; set; }
Author db
int AuthorId
string Name
ICollection<Quote> Quotes { get; set; }
ICollection<Penname> Pennames { get; set; } - Edit: Added for clarity
I have tried:
var test = _context.Book.Include(x => x.Authors).Include("Authors.Quotes")
.Select(y => y.Authors)
Which gave me:
EntityQueryable<ICollection<Authors>>
[0] {HashSet<Author>} [0]{Author} [1]{Author} [3]{Author}
[1] {HashSet<Author>} [0]{Author} [1]{Author}
[2] {HashSet<Author>} [0]{Author} [1]{Author}
I just can't figure out how to iterate though the Authors in the Authors list. Something like the below:
var id = 2
var test = _context.Book.Include(x => x.Authors).Include("Authors.Quotes")
.Select(y => y.Authors.Select(x => x.Author).Where(x => x.AuthorId == id))
If I ever do a major update I might use elastic...
Update #Marko Papic:
Thanks. Weirdly if I use the below to get a list of books with authors, I get the quotes and pennames lists populated as I expect
var test = _context.Book.Include(x => x.Authors)
.ThenInclude(x => x.Quotes)
.Include(x => x.Authors)
.ThenInclude(x => x.Pennames)
However if I use SelectMany, then the quotes and pennames end up as null
var test = _context.Book.Include(x => x.Authors)
.ThenInclude(x => x.Quotes)
.Include(x => x.Authors)
.ThenInclude(x => x.Pennames)
.SelectMany(x => x.Authors).Where(x => x.AuthorId == id);
Author myauthor
int AuthorId = 2
string Name = "Bob"
ICollection<Quote> Quotes = null
ICollection<Penname> Pennames = null

You can use SelectMany:
var test = _context.Book.Include(x => x.Authors).ThenInclude(x => x.Quotes)
.SelectMany(x => x.Authors).Where(x => x.AuthorId == id);

I think the includes are ignored because the result type of the query is not the same of the type of your dbset with when you start, from the documentation:
If you change the query so that it no longer returns instances of the
entity type that the query began with, then the include operators are
ignored.
I assume the relationship between Books and Authors is many to many, if that is the case then this is how I would do your query:
var query=_context.Authors.Include(a=>a.Books)
.Include(a=>a.Quotes)
.Include(a=>a.Pennames)
.Where(a=> a.AuthorId == id);

Related

Getting null values from Model while there are records

This is where i am doing the Query i am getting null values on using dynamic query
var responsedata = await _elasticClient.SearchAsync<Acquirer>(s => s
.Query(q => q.ConstantScore(cs => cs.Filter(
f =>
f.Term("MERCHANTNO.keyword", MerchantNo)
)))
);
This is my model
public class Acquirer
{
public string MERCHANTNO { get; set; }
}
This is where index mapping is done
client.Map<Acquirer>(m =>
{
var putMappingDescriptor = m.Index(Indices.Index("acquiringdata")).AutoMap();
return putMappingDescriptor;
});
I am getting the exact number of records but i am getting the null values
This Error can be Resolved by using this
[PropertyName("MERCHANTNO")]

Dynamic Group By clause in linq query

The following linq query groups people in a city by their professions.
However the only way I've been able to get it to work is to hardcode the cities.
Each Person has a City. How can I modify the below query so that it groups by all the distinct cities that are returned in my query?
profession = people
.GroupBy(p => p.Profession).
Select(group =>
new
{
name = group.Key,
data =
new List<int>()
{
group.Count(p => p.City == "Adelaide"),
group.Count(p => p.City == "Brisbane"),
group.Count(p => p.City == "Canberra"),
group.Count(p => p.City == "Darwin"),
group.Count(p => p.City == "Melbourne"),
group.Count(p => p.City == "Perth"),
group.Count(p => p.City == "Sydney"),
},
})
Ie, if my data set was:
Person {id:1, city: "Paris" }
Person {id:2, city: "Paris" }
Person {id:3, city: "London" }
Then the resulting query would be:
profession = people
.GroupBy(p => p.Profession).
Select(group =>
new
{
name = group.Key,
data =
new List<int>()
{
group.Count(p => p.City == "Paris"),
group.Count(p => p.City == "London"),
},
})
This yields the same output as your hardcoded list, but I'm not sure if it's what you want (hint: giving us your expected output will help a lot in that regard).
It simply creates a distinct list of the cities, then builds a predicate delegate that counts the occurrence of each city, and adds that predicate to a list. When the actual grouping is performed, each predicate in the list is invoked with the result of the grouping.
The notation Func<IGrouping<string, Person>, int> simply says "this is a delegate that takes an IGrouping<string, Person> object (the result of calling GroupBy) and returns an int (the result of calling Count)".
class Program
{
static void Main(string[] args)
{
var people = new List<Person>()
{
new Person(1, "Paris", "carpenter"),
new Person(2, "Paris", "bricklayer"),
new Person(3, "London", "video game critic"),
};
var distinctCities = people.Select(p => p.City).Distinct();
var groupPredicates = new List<Func<IGrouping<string, Person>, int>>();
foreach (var city in distinctCities)
{
groupPredicates.Add(g => g.Count(p => p.City == city));
}
var professions = people
.GroupBy(p => p.Profession)
.Select(g =>
new
{
name = g.Key,
data = groupPredicates.Select(gp => gp(g)),
});
foreach (var profession in professions)
{
Console.WriteLine(profession.name + " =>");
foreach (var count in profession.data)
{
Console.WriteLine(" " + count);
}
}
Console.ReadKey(true);
}
}
struct Person
{
public int Id { get; set; }
public string City { get; set; }
public string Profession { get; set; }
public Person(int id, string city, string profession)
{
Id = id;
City = city;
Profession = profession;
}
}

ASP.NET - Automapper not working on productive IIS

I'm mapping data using Automapper in my ASP.NET MVC4 Application's from my domain model onto my view model.
On my development machine with the integrated IIS this works perfectly, however when deploying it to the real IIS, suddenly my code only maps the data seemingly on arbitrary occasions.
It seems that the mapping works once after redeploying the application and sometimes after attaching the remote debugger.
My code is:
iPXE dbCon = new iPXE();
Models.Device bootDevice = dbCon.Devices.SingleOrDefault(d => d.serial == serial);
var imagePredicate = PredicateBuilder.False<Models.Image>();
imagePredicate = imagePredicate.Or(d => d.anyboot == true); // Images everyone can boot
if (bootDevice != null) // If the boot device is in the database
{
imagePredicate = imagePredicate.Or(d => d.Devices.Any(e => e.id == bootDevice.id)); // Retrieve all images assigned to the device
}
IEnumerable<Models.Image> allowedImages = dbCon.Images.AsExpandable().Where(imagePredicate);
Mapper.CreateMap<Models.Category, Category>()
.ForMember(x => x.name, opt => opt.MapFrom(src => src.CategoryTranslations.SingleOrDefault(d => d.locale == locale) == null || // Either no translation row found
src.CategoryTranslations.SingleOrDefault(d => d.locale == locale).name != null ? // Or specific value is null
src.name : // Then use name from main table (untranslated)
src.CategoryTranslations.SingleOrDefault(d => d.locale == locale).name)) // Otherwise use translated value
.ForMember(x => x.images, opt => opt.MapFrom(src => allowedImages.Intersect(src.Images)));
Mapper.CreateMap<Models.Image, Image>()
.ForMember(x => x.description, opt => opt.MapFrom(src => src.ImageTranslations.SingleOrDefault(d => d.locale == locale) == null ||
src.ImageTranslations.SingleOrDefault(d => d.locale == locale).description == null ?
src.description :
src.ImageTranslations.SingleOrDefault(d => d.locale == locale).description))
.ForMember(x => x.code, opt => opt.MapFrom(src => src.ImageTranslations.SingleOrDefault(d => d.locale == locale) == null ||
src.ImageTranslations.SingleOrDefault(d => d.locale == locale).code == null ?
src.code :
src.ImageTranslations.SingleOrDefault(d => d.locale == locale).code));
categories = Mapper.Map<IEnumerable<Models.Category>, List<Category>>(allowedImages.Select(c => c.Category1).Distinct());
public class Category
{
public string name { get; set; }
public List<Image> images { get; set; }
}
public class Image
{
public string tag { get; set; }
public string description { get; set; }
public string code { get; set; }
}
What I'm doing is basically getting a device from the database, getting all assigned images for the device (N:M) and then creating Automapper-Maps for categories and images that belong to the device.
The exact problem is that on the productive server, the images-Lists of the category objects stay empty, even though there is data in allowedImages (5 images), bootDevice (the device data) and allowedImages.Select(c => c.Category1).Distinct() (2 categories).
I'm using the same SQL server for both my machine and the server. If you need any more information, please feel free to ask.
You are calling 'CreateMap' each time the method runs. As I understand, you should only do this once within your application domain. Try moving the CreateMap logic to where it gets called from Application_OnStart to see if that gives you more consistent behavior.
Also, the line:
.ForMember(x => x.images, opt => opt.MapFrom(src => allowedImages.Intersect(src.Images)));
may be troubling you. You may need to change that to an AfterMap that calls a static method that performs the Intersect on your source & destination collections.

Showing linked table items in a controller/view

I have a room class, which has 3 linked tables, Property, Sex (as in Male/Female), Stage.
I can get the Index controller to return the linked table items, by using Include:
var rooms = db.Rooms.Include(r => r.Property).Include(r => r.Sex).Include(r => r.Stage);
What I don't know is, how to you include these linked table items in the Details controller:
Room room = db.Rooms.Find(id);
The full controllers are listed below:
//
// GET: /Room/
public ActionResult Index()
{
var rooms = db.Rooms.Include(r => r.Property).Include(r => r.Sex).Include(r => r.Stage);
return View(rooms.ToList());
}
//
// GET: /Room/Details/5
public ActionResult Details(int id = 0)
{
Room room = db.Rooms.Find(id);
if (room == null)
{
return HttpNotFound();
}
return View(room);
}
Thanks for any help,
Mark
After Include statments you could use LINQ's SingleOrDefault method
Like this:
Room room = db.Rooms.Include(r => r.Property)
.Include(r => r.Sex)
.Include(r => r.Stage)
.SingleOrDefault(r => r.Id = id);

get all the values in Sql table using foreach

I need to get all the Ids' from the table "StaffSectionInCharge", it has only two columns StaffId and SectionId, I have values of StaffId and StudentId.....the problem is I can't get the records directly to this table.....am using entity framework and the design of this table is
[EdmRelationshipNavigationPropertyAttribute("Model", "StaffSectionInCharge", "Section")]
public EntityCollection<Section> Sections
{
get
{
return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<Section>("Model.StaffSectionInCharge", "Section");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Section>("Model.StaffSectionInCharge", "Section", value);
}
}
}
I can access this table through staff table like
Staff staff = buDataEntities.Staffs.First(s => s.StaffId == StaffId);
Section section = buDataEntities.Sections.First(s => s.SectionId == SectionId);
staff.Sections.Add(section);
buDataEntities.savechanges();
like this I can add the records to this StaffSectionInCharge table....
here I want to get all the StaffIds for the corresponding SectionId
I tried getting like
DataAccess.Staff staffs = new DataAccess.Staff();
foreach (int staff in staffs.Sections.Select(s=>s.SectionId))
{
}
but its not working, can anyone help me here
var staffIds = buDataEntities.Staffs
.Where(st => st.Sections.Any(se => se.SectionId == SectionId))
.Select(st => st.StaffId)
.ToList();
or
var staffIds = buDataEntities.Sections
.Where(se => se.SectionId == SectionId)
.SelectMany(se => se.Staffs.Select(st => st.StaffId))
.Distinct()
.ToList();
Both options should work. If SectionId is the primary key of Section you can smplify the second code to:
var staffIds = buDataEntities.Sections
.Where(se => se.SectionId == SectionId)
.Select(se => se.Staffs.Select(st => st.StaffId))
.SingleOrDefault();

Resources