I have a simple db structure:
public class Person
{
[Key]
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Transport
{
[Key]
public int TransportID { get; set; }
public string Model { get; set; }
public string Brand { get; set; }
}
public class Accident
{
[Key]
public int AccsidentID { get; set; }
public DateTime AccidentDate { get; set; }
public int TransportID { get; set; }
[ForeignKey("TransportID")]
public virtual Transport Transport { get; set; }
public int PersonID { get; set; }
[ForeignKey("PersonID")]
public virtual Person Person { get; set; }
}
I need to create a list of accidents, wich I could pass to WPF form (using MVVM)
First I created new class witch I would like to see in my GridControl
public class AccsidentObject
{
[Key]
public int AccidentID { get; set; }
public DateTime AccidentDate { get; set; }
public int TransportID { get; set; }
public string Model { get; set; }
public string Brand { get; set; }
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Could you please give me examples:
if I want to get list of all values from Accident table including data from Transport and Person tables
if I want to get Accident list grouped by TransportID (also include data from Person and Transport tables)
I am getting data from Linq query:
var result = from ac in DBContext.Accidents select ac;
List<Accident> accidentList = result.toList();
But I need to add some fields to list from other tables, what would be a code?
What do I do wrong and could not construct a list of AccidentObject, maybe there are some mistake in my DBContext, lists o something... Could you please help me to understand List elements??
Considering to 2 part I wrote:
var result = from ac in DBContext.Accidents select ac;
result = result.GroupBy(g => g.TransportID).toList();
And now I need to add some Transport details and format AccsidentObject list ...
To get an entity (or collection of entities) with associations eagerly populated use the Include extension method, or include in a final projection into your type:
var res = await (from a in ctx.Accidents
select new AccsidentObject {
AccidentID = a.AccidentID,
TransportID = a.Transport.TransportID,
Model = a.Transport.Model,
// …
}).ToListAsync();
You can use groupby in a LINQ comprehension expression to group by something. In the result the Key property is the thing grouped by and each instance is a collection of all things grouped by.
var res = await (from a in ctx.Accidents
group by a.TransportID into g
select new {
TransportID = g.Key,
Accidents = g
}).ToListAsync();
In the resulting anonymous types the Accidents property with be a collection of Accident.
var accidents = DBContext.Accidents.Select( a => new AccidentObject
{
AccidentID = a.AccidentId,
AccidentDate
TransportID
Model
Brand = a.Transport.Brand,
PersonID = a.Person.PersonID,
FirstName
LastName
});
and fill in the blanks in much the same way.
here's a linq example without using lambda expressions, that includes a group by clause if you prefer it: Linq to sql select into a new class
Related
I have two classes:
class Class1
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Amount { get; set; }
public string Category { get; set; }
}
class Class2
{
public id ProductId { get; set; }
public DateTime UpdateTime { get; set; }
public int ItemState { get; set; }
public decimal Price { get; set; }
public string ImageUrl { get; set; }
}
I create two lists for each of this class:
public List<Class1> listClass1 { get; set; } = new List<Class1>();
public List<Class2> listClass2 { get; set; } = new List<Class2>();
Now I want to create a new list by joining both lists on the ProductID containing all properties from Class1 and just UpdateTime and ItemState from Class2. What is the best way to do this?
(The ultimate goal is to let the user save the current session to let him continue with his work later, for this I need the complete listClass1 and the mentioned properties of listClass2. The idea is to use an XmlSerializer on the merged list and save it into an XML file. To open the file, I want to deserialize it. If you have a better approach to let the user save his session, I'm all ears.)
I would suggest to make a third class which represents the joined parameter set.
public class Class1_2
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int Amount { get; set; }
public string Category { get; set; }
public DateTime UpdateTime { get; set; }
public int ItemState { get; set; }
}
How to join 2 Lists is described in LINQ Join 2 Lists. So you would end up with something like this:
List<Class1_2> joinedList = (from item1 in listClass1
join item2 in listClass2
on item1.ProductId equals item2.ProductId
select new Class1_2
{
ProductId = item1.ProductId,
Amount = item1.Amount,
ProductName = item1.ProductName,
Category = item1.Category,
ItemState = item2.ItemState,
UpdateTime = item2.UpdateTime
}).ToList();
Explanation: You join the items on the particular property. If it is equal in both lists you take the item and construct a new one with the selected properties. The join would also work without Class1_2 but then you get a result of dynamic type. The advantage of the new Class_1_2 is that you can use it for serialization with a defined structure.
EDIT
you could also use a second constructor in Class1_2
public Class1_2(Class1 c1, Class2 c2)
{
ProductId = c1.ProductId;
Amount = c1.Amount;
ProductName = c1.ProductName;
Category = c1.Category;
ItemState = c2.ItemState;
UpdateTime = c2.UpdateTime;
}
that would simplify a little the select statement to:
select new Class1_2(item1, item2)).ToList();
i have 2 tables that each one has a one-to-many relation to the table between and the table between has ids of 2 other tables
dbo.Posts dbo.Posts_Categories dbo.Categories
-ID -ID -ID
-Title -PostID -Name
-CategoryID
result i expect is :
Title = post1 Categories = web,mobile,desktop
Title = post2 Categories = app,game
...
i know how to query this in sql using Stuff function and For Xml Path but i have no idea how do i do this in entity framework!
any suggestion or book for how to do works in this way might help!
Edit: EF classes added:
public class Post : ReportingBase {
public Post() { }
[Required, MaxLength(500)]
public string Title { get; set; }
[Required, MaxLength(500)]
public string Address { get; set; }
[Required]
public string Body { get; set; }
[Required, MaxLength(500)]
public string Tags { get; set; }
[Required]
public int Visit { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
public virtual ICollection<Post_AttachedFile> Posts_AttachedFiles { get; set; }
[ForeignKey("Image")]
public virtual int? ImageID { get; set; }
public virtual Image Image { get; set; }
}
public class Post_Category {
public Post_Category() { }
[Key, Column(Order = 0)]
public int PostID { get; set; }
[Key, Column(Order = 1)]
public int CategoryID { get; set; }
public virtual Post Post { get; set; }
public virtual Category Category { get; set; }
}
public class Category : EntityBase {
public Category() { }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, MaxLength(150)]
public string Address { get; set; }
public int? ParentID { get; set; }
public virtual ICollection<Post_Category> Posts_Categories { get; set; }
}
thank you in advance
Edit : According to #IvanStoev answer i did following :
List<P> p = context.Posts.Select(post => new {
Title = post.Title,
Categories = post.Posts_Categories.Select(pc => pc.Category.Name).ToList()
}).ToList();
and created a class called P :
public class P {
public string Title { get; set; }
public List<string> Categories { get; set; }
}
but it doesn't work correctly and the problem is how to return the result.
In EF it's even easier than in SQL thanks to the concept of so called navigation properties. All you need to know is a basic LINQ query syntax and just follow them (navigate) to get the data needed. For instance:
var result = db.Posts
.Select(post => new
{
Title = post.Title,
Categories = post.Posts_Categories
.Select(pc => pc.Category.Name)
.ToList()
})
.ToList();
The result is a list of anonymous type having string Title property and List<string> Categories property containing the related category names.
You can use Linqpad (software) to get familiarize with the Linq query it builds lambda expression for you by connecting to the database and provides output too to cross verify.
The below one is the lambda expression for joining the tables you have mentioned.
p - Post
pc - post_categories
c - categories
Code:
Posts.Join(Post_Categories, p => p.ID, pc => pc.ID, ( p, pc) => new { p = p, pc = pc})
.Join(Categories, pcc => pcc.pc.CategoryID, c => c.ID, ( pcc, c) => new { pcc = pcc, c = c})
.Select(p.Title)
.Select(c.Name)
You should be using .Include() for any join in EF Core.
I've come up with this simple example: one person can have many dogs.
public class Person
{
public Guid Id { get; set; }
public ICollection<Dog> Dogs { get; set; } // One Person can have many Dogs
}
public class Dogs
{
public Guid Id { get; set; }
public Guid PersonId { get; set; }
}
Generate the migrations after creating the models. Not going over how to do that in this answer.
Here's how you use .Include() to join upon the two different tables:
public class PersonRepository : RepositoryBase
{
public IEnumerable<Person> FetchPeopleWithManyDogs()
{
return DatabaseContext.Person
.Include(x => x.Dogs)
.Where(x => x.Dogs.Count() > 1).ToList();
}
}
I have 3 tables , one to many relationship.
I need to get only specific columns with SelectMany method.
I need to get only Categories.CategoryName and Comments.CommentDate of the selected News object.
Here is my code
News news = db.News.Include(w => w.Categories)
.Include(w => w.Comments).SingleOrDefault(n => n.NewsId == Id);
Here are my Entities:
News Entity:
public partial class News
{
public News()
{
this.Categories = new HashSet<Category>();
this.Comments = new HashSet<Comment>();
}
public int NewsId { get; set; }
public string NewsTitle { get; set; }
public string NewsBody { get; set; }
public System.DateTime NewsDate { get; set; }
public string NewsImagePath { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
Category Entity:
public partial class Category
{
public Category()
{
this.News = new HashSet<News>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<News> News { get; set; }
}
Comment Entity:
public partial class Comment
{
public Comment()
{
this.News = new HashSet<News>();
}
public int CommentId { get; set; }
public string CommentBody { get; set; }
public Nullable<System.DateTime> CommentDate { get; set; }
public virtual ICollection<News> News { get; set; }
}
This LINQ query should take care of it:
var query =
from news in db.News
where news.Id == Id
let categoryNames =
from category in news.Categories
select category.Name
let commentDates =
from comment in news.Comments
select comment.CommentDate
select new {
CategoryNames = categoryNames.ToList(),
CommentDates = commentDates.ToList()
};
That query is not using SelectMany, but that wouldn't help you, since then you wouldn't be able to group your categories and comments by news items. Since categories and comments are not directly connected, you'd need two SelectManys and then you'd need to cross join the results. That would obviously not be what you want.
Maybe try using the following?
var categoryNames = news.Categories.Select(c=>c.CategoryName);
var commentDates = news.Comments.Select(c=>c.CommentDate);
Note that SelectMany is used to flatten lists.For example, lets say you have collection of news matching certain search criteria, and then you use SelectMany to collect all the Categories/Comments of these news set, in a flat list.
I'm trying to query for gifts given a term from my categories table. Entity Framework created a bridge table to connect my "Gift" with my "GiftCategroies". But the query I have yielded no results.
From DbContext:
public DbSet<Gift> Gifts { get; set; }
public DbSet<GiftCategory> Categories { get; set; }
The two entities I created:
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<GiftCategory> Categories { get; set; }
public int Rating { get; set; }
}
public class GiftCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Gift> Gifts { get; set; }
}
Here is my query to try and fetch the gifts given a giftcategory term. No results returned. I'm not sure if I even need a join for this type of query.
var model =
from gifts in db.Gifts
join giftCategory in db.Categories on gifts.Id equals giftCategory.Id
where giftCategory.Name.Contains(searchTerm)
select gifts;
You should use navigation properties instead of joins:
var gifts = (from c in db.Categories
from g in c.Gifts
where c.Name.Contains(searchTerm)
select g).Distinct().ToList();
I have made simple model for example.
public class Publisher
{
public int Id { get; set; }
public string Title { get; set; }
public Address Location { get; set; }
public virtual ICollection<Book> Books { get; set; }
}
public class Address
{
public string Country { get; set; }
public string City { get; set; }
public string Street { get; set; }
public string HouseNumber { get; set; }
}
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public int LanguageId { get; set; }
public int? PublisherId { get; set; }
}
I need to get publishers with related books. I know how to do it using linq to entities. Is it possible to solve a problem using entity sql?
public class CatalogContext : DbContext {...}
public List<Publisher> GetByCity(string city)
{
var result = new List<Publisher>();
string queryString;
queryString = String.Format(#"SELECT VALUE row(a,b)
FROM CatalogContext.Publishers AS a
join CatalogContext.Books AS b on a.Id = b.PublisherId
WHERE a.Location.City = '{0}'", city);
var rows = ((IObjectContextAdapter)_context).ObjectContext.CreateQuery<DbDataRecord>(queryString).ToList();
return ???
}
Query returns required data but it's List<DbDataRecord> - list of pairs <publisher, book>. How to translate it to list of publishers with filled navigation property "Books"?
Is it possible to write query which directly returns List<Publisher>?
you can do the following:
var result = ObjectContext.Publishers.Include("Books").Include("Locations")
.Where(c => c.Location.City = "SOME_CITY").Select(c => c);
Include - basically joins the table.
Then you can drill down to books by doing the following:
var test = result[0].Books;
Why are you using direct sql command instead of Entity Framework code style?