Entity Framework Selecting columns from multiple tables (for ThenInclude)? - entity-framework-6

I have question about select...
var applicationUser = unitOfWork.ApplicationUsers.GetAll().Include(i => i.ApplicationUserRoles).ThenInclude(i => i.ApplicationRole)
.Where(i => i.UserName.ToUpper() == userName.ToUpper())
.Select(i => new
{
i.Email,
i.FirstName,
i.LastName,
i.PhoneNumber,
i.ImageUrl,
i.JoinedDate,
i.DateOfBirth,
i.ApplicationUserRoles
})
.FirstOrDefault();
I cant get ApplicationRole how to use it in select ?

If you are using projection with Select you don't need to use Include. Just select the values. To get the Roles via the UserRoles you will need to Select to retrieve those:
var applicationUser = unitOfWork.ApplicationUsers.GetAll()
.Where(i => i.UserName.ToUpper() == userName.ToUpper())
.Select(i => new
{
i.Email,
i.FirstName,
i.LastName,
i.PhoneNumber,
i.ImageUrl,
i.JoinedDate,
i.DateOfBirth,
ApplicationRoles = i.ApplicationUserRoles.Select(x => x.ApplicationRole).ToList()
})
.FirstOrDefault();
You can further refine this by sub-selecting just the role details you need from the application role...
// ...
ApplicationRoles = i.ApplicationUserRoles.Select(x =>
x.ApplicationRole.Select(ar => new { ar.RoleId, ar.RoleName })).ToList()
I recommend using SingleOrDefault rather than FirstOrDefault if you are expecting at most 1 result. First/FirstOrDefault should always be used with an OrderBy/OrderByDescending condition to ensure predictable results.

If I can use for ApplicationRoles, it is working
.Select(i => new
{i.Email,i.FirstName,i.LastName,i.PhoneNumber,i.ImageUrl,i.JoinedDate,i.DateOfBirth,
ApplicationRoles = i.ApplicationUserRoles.Select(x => x.ApplicationRole).ToList(),
})
I cant use for ApplicationUserRoles how is it work for it ?
public class ApplicationUserSummary
{
public DateTime JoinedDate { get; set; }
public string ImageUrl { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfBirth { get; set; }
public ICollection<ApplicationRole> ApplicationRoles { get; set; }
public ICollection<ApplicationUserRole> ApplicationUserRoles { get; set; }
}

Related

Return data from multiple tables using LINQ Method Syntax

I have got problem with return data from my database. In my database are three tables: Customer, Activity and CustomerActivity. I want to return activities data for specific customer and with specific type activity. I wrote query but it doesn't work exactly as I wish. Here is my code and tables. Thank you for advices.
var activities = db.Join(db.CustomerActivities, a => a.ActivityID, ca =>ca.CustomerActivityID, (a, ca) => new { Activity = a, CustomerActivity = ca })
.Where(a => a.CustomerActivity.customer.CustomerID == id && a.Activity.TypeActivityID == typeActivity)
.Select(m => new ActivityMV()
{
ActivityID = m.Activity.ActivityID,
Name = m.Activity.Name,
DateOfActivity = m.Activity.DateOfActivity,
Desc = m.Activity.Desc
})
.ToList();
public class CustomerActivity
{
public int CustomerActivityID { get; set; }
public int CustomerID { get; set; }
public int ActivityID { get; set; }
public virtual Customer customer { get; set; }
public virtual Activity activity { get; set; }
}
public class Activity
{
public int ActivityID { get; set; }
public string Name { get; set; }
public string Desc { get; set; }
public string DateOfActivity { get; set; }
public string TypeActivityID { get; set; }
public virtual ICollection <CustomerActivity> customerActivities { get; set; }
}
Try
var activities = db.CustomerActivities
.Where(ca => ca.CustomerID == id && ca.Activity.TypeActivityID == typeActivity)
.Select(ca => new ActivityMV()
{
ActivityID = ca.ActivityID,
Name = ca.Activity.Name,
DateOfActivity = ca.Activity.DateOfActivity,
Desc = ca.Activity.Desc
})
.ToList();

How to map redundant relationship in EF6

I have a Quote object, with a collection of QuoteAnswer objects. I also want a shortcut to the latest QuoteAnswer. So I modeled (irrelevant code ommitted for brevity):
public class Quote
{
public int Id { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
public QuoteAnswer LatestAnswer { get; set; }
public int LatestAnswerId { get; set; }
}
public class QuoteAnswer
{
public int Id { get; set; }
public Quote Quote { get; set; }
public int QuoteId { get; set; }
/* Did not map this, not interested/not needed
* public Quote LastAnswerFor { get; set; }
* public int LastAnswerForId { get; set; }
*/
}
That's beacuse I want to be able to do this:
var quotes = context.Quotes
.Include(x => x.LatestAnswer)
.ToList();
Instead of this:
var quotes = context.Quotes
.Include(x => x.Answers)
.ToList();
foreach (var q in quotes)
{
var latestAnswer = q.Answers.OrderByDescending(x => x.Date).FirstOrDefault();
}
Which would obviously force me to load unecessary data.
The Problem
When I try to map this do database code (add a migration), I get a new property I don't know where it's comming from.
Generated migration code (parts ommitted for brevity):
CreateTable(
"dbo.QuoteAnswer",
c => new
{
Id = c.Int(nullable: false),
QuoteId = c.Int(nullable: false),
QuoteId1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Quote", t => t.QuoteId)
.ForeignKey("dbo.Quote", t => t.QuoteId1)
.Index(t => t.QuoteId)
.Index(t => t.QuoteId1);
AddColumn("dbo.Quote", "LatestAnswerId", c => c.Int());
CreateIndex("dbo.Quote", "LatestAnswerId");
AddForeignKey("dbo.Quote", "LatestAnswerId", "dbo.QuoteAnswer", "Id");
What's that QuoteId1 thing? I get the QuoteId, but I don't recognize QuoteId1.
How can I achive this mapping? Is this even supported in EF6?
First, it's possible. The explicit FK property should be removed:
public class Quote
{
public int Id { get; set; }
public string Data { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
public QuoteAnswer LatestAnswer { get; set; }
}
and the new relationship should be mapped with fluent API:
modelBuilder.Entity<Quote>()
.HasOptional(e => e.LatestAnswer)
.WithOptionalDependent()
.Map(m => m.MapKey("LatestAnswerId"));
But I won't recommend you doing it because it would introduce a lot of maintenance problems - aside of the obvious need to keep it up-to-date, it would create circular FK dependency, so basically all CUD operations would be problematic (if working at all).
I think you are trying to solve the "loading unnecessary data" problem is a wrong way. You can achieve the same goal by using simple projection:
var quotesWithLatestAnswer = context.Quotes
.Select(q => new { Quote = q, LatestAnswer = q.Answers.OrderByDescending(a => a.Date).FirstOrDefault() })
.ToList();
Note that the code inside Select will be translated to SQL and executed in the database, returning only the data needed.
To return the latest answer as part of your entity, you can make mark it as unmapped:
public class Quote
{
public int Id { get; set; }
public string Data { get; set; }
public ICollection<QuoteAnswer> Answers { get; set; }
[NotMapped]
public QuoteAnswer LatestAnswer { get; set; }
}
and use a combination of LiNQ to Entities (SQL) and LINQ to Objects query:
var quotes = context.Quotes
.Select(q => new { Quote = q, LatestAnswer = q.Answers.OrderByDescending(a => a.Date).FirstOrDefault() })
.AsEnumerable() // end of db query
.Select(qi => { qi.Quote.LatestAnswer = qi.LatestAnswer; return qi.Quote; })
.ToList();
This way you'll have clean and easy to maintain relational database model as well as efficient retrieval of the data needed.

Entity Framework Access collection of collection

I'm trying to access a collection of a collection of my main model in my Details action of my controller. But I keep getting the following error
System.InvalidOperationException: 'The property expression 'e => {from Color color in [e].Colors select [color].Images}' is not valid. The expression should represent a property access: 't => t.MyProperty'. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.'
this pops up on the line:
var model = _context.Towers
.Include(e => e.Colors.Select(color => color.Images))
.FirstOrDefault(e => e.ID == id);
Here's some of the other code:
Tower.cs
public class Tower
{
[Key]
public Nullable<int> ID { get; set; }
public List<Color> Colors { get; set; } = new List<Color>();
}
Color.cs
public class Color
{
[Key]
public Nullable<int> ID { get; set; }
public string ColorHash { get; set; }
public List<Image> Images { get; set; } = new List<Image>();
}
Image.cs
public class Image
{
[Key]
public Nullable<int> ID { get; set; }
public string ImagePath { get; set; }
}
I need to be able to access the Images associated with each Color associated with the Tower I'm showing details for.
I think it should be something like this:
var model = _context.Towers
.Include(e => e.Colors)
.ThenInclude(color => color.Images))
.FirstOrDefault(e => e.ID == id);

EF7 Getting null values for entity's collection of entities which are many to many

I am getting null values for the collection of entities nested in my top entity. How do I properly write my LINQ query so that these values aren't null??
I am using Entity Framework 7 and MVC 6 Here are my classes:
My models:
public class WorkStation
{
public Id { get; set; }
public string Name{ get; set; }
public ICollection<PersonWorkStation> PersonWorkStations{ get; set; }
}
public class Person
{
public Id { get; set; }
public string FirstName { get; set; }
public ICollection<PersonWorkStation> PersonWorkStations{ get; set; }
}
public class PersonWorkStation
{
public int Id { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
public int WorkStationId { get; set; }
public WorkStation WorkStation { get; set; }
}
My DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PersonWorkStation>()
.HasKey(op => new { op.Id });
modelBuilder.Entity<PersonWorkStation>()
.HasOne(pt => pt.Person)
.WithMany(p => p.PersonWorkStation)
.HasForeignKey(pt => pt.PersonId);
modelBuilder.Entity<PersonWorkStation>()
.HasOne(pt => pt.WorkStation)
.WithMany(t => t.PersonWorkStation)
.HasForeignKey(pt => pt.WorkStationId);
base.OnModelCreating(modelBuilder);
}
So with that being said, when I bring back a person, and look at the "PersonWorkStation"s collection, the WorkStation property is null. How can I bring back that entity?
Here is how I am retrieving the data:
var person = _context.Persons
.Include(p => p.PersonWorkStation)
.FirstOrDefault(p => p.Id == 1);
return person;
Again, the person.PersonWorkStations.Workstation entity is null for all items in the person.PersonWorkStations collection. How do I return this entity?
Thanks!
I have found the answer, I needed to add this line:
var person = _context.Persons
.Include(p => p.PersonWorkStation)
.ThenInclude(p => p.WorkStation)
.FirstOrDefault(p => p.Id == 1);
return person;

Asp.Net Mvc Linq issue

I know my problem is really basic. If I write /api/category/1, I wanna list all Tales with the categoryId==1. I wrote this code, but it gives an error.
[HttpGet]
public IEnumerable<Tale> GetAllTalesByCategory(int id)
{
var tales = TaleService.FindAllTale().Select(x => new Tale
{
TaleId = x.TaleId,
TaleName = x.TaleName,
Content = x.Content,
VoicePath = x.VoicePath
}).Where(x => new Tale
{
x.Categories.Select(c => c.CategoryId).First() == id
});
}
Error:
Error 1 Cannot initialize type 'MasalYuvasi.Entities.Tale' with a collection initializer because it does not implement 'System.Collections.IEnumerable' D:\MvcProject\MasalYuvasi\MasalYuvasi\Controllers\DenemeController.cs 33 13 MasalYuvasi
Models:
public class Tale
{
public int TaleId { get; set; }
public string TaleName { get; set; }
public string Content { get; set; }
public string VoicePath { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public Tale()
{
this.Categories = new List<Category>();
}
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Tale> Tales { get; set; }
public Category()
{
this.Tales = new List<Tale>();
}
}
Try this:
[HttpGet]
public IEnumerable<Tale> GetAllTalesByCategory(int id)
{
var tales = TaleService.FindAllTale().Select(x => new Tale
{
TaleId = x.TaleId,
TaleName = x.TaleName,
Content = x.Content,
VoicePath = x.VoicePath
}).Where(x => x.Categories.Select(c => c.CategoryId).First() == id).ToList();
}
Fixed the where condition, and added .ToList().
The problem is that your code is using a collection initializer here:
new Tale
{
x.Categories.Select(c => c.CategoryId).First() == id
}
I'm not sure what this code is supposed to be doing, but as x.Categories.Select(c => c.CategoryId).First() == id will return a bool, I don't think this is doing what you want it to.
Based on your comment:
I want to list in the category of tales. Forexample I have 2 tales in CategoryId is 1. If I write "/api/category/1" ı want to list this 2 tales.
I think you are looking for something simpler than what you've got. You want to select Tales (represented by x) where Any of the categories have a CategoryId of id:
.Where(x => x.Categories.Any(c => c.CategoryId == id ));
Note that you can append .ToList() to the end of the where clause, as suggested by pjobs, but this may have a subtle effect on the behavior of your application. For more detail, see LINQ and Deferred Execution.

Resources