issue with select clause in linq - to - entities query - c#

Hello I'm using code first approach and I defined the following model:
public partial class tmmodel
{
public tmmodel()
{
this.tmmodel_L10n = new HashSet<tmmodel_L10n>();
}
public int id { get; set; }
public int Order { get; set; }
public bool Active { get; set; }
public virtual ICollection<tmmodel_L10n> tmmodel_L10n { get; set; }
}
public partial class tmmodel_L10n
{
public int modelid { get; set; }
public int CultureId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public virtual tmmodel tmmodel { get; set; }
}
So I want to get in an anonymous class - the id, Active and order property of my tmodel along with teh title in tmodel_L10:
With Linq i MANAGED TO GET the result - but when I try to use Linq - to - sql - I have some problems :
var items = from i in dc.tmmodel
join l10n in dc.tmmodel_L10n on new { i.id, cid = 1 } equals new { l10n.modelid, cid = l10n.CultureId }
select new
{
id = i.id,
i.Order,
i.Active,
l10n.Title,
};
Here is my - linq to entites query and you can see that I don't have any access to the Title property:
var linqtosqlitems = dc.tmmodel
.Include(x => x.tmmodel_L10n)
.Select(l => new {id = l.id,l.Active,**l.tmmodel_L10n.??**}).ToList();

By using include Linq Creates an Enumarable of your "child" table because it can be one to many relationship. If You are sure there is only one record in "child table" You can go like:
var linqtosqlitems = dc.tmmodel
.Include(x => x.tmmodel_L10n)
.Select(l => new {id = l.id,l.Active,l.tmmodel_L10n.FirstOrDefault().Title}).ToList();

Related

Convert DataTable to a nested object using LINQ

I have a stored proc returning a datatable using a stored procedure. I am able to convert the it to an object using the following code
outgoingPaymentData.AsEnumerable().Select(x => new OutgoingPaymentApprovalDetails() { });
Here is my OutgoingPaymentApprovalDetails class
public class OutgoingPaymentApprovalDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public string DocumentId { get; set; }
public string DebtorName { get; set; }
public string Currency { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Now, instead of a flat list, I need to add heirarchy, to select this one object to 3 objects.
Classes as under:
public class OriginatorDetails
{
public int OriginatorId { get; set; }
public string Name { get; set; }
public List<DocumentDetails> DocumentDetails { get; set; }
}
public class DocumentDetails
{
public string DocumentId { get; set; }
public List<TransactionDetails> TransactionDetails { get; set; }
}
public class TransactionDetails
{
public string Amount { get; set; }
public string DebitAccountNo { get; set; }
public string CreditAccountNo { get; set; }
}
Basically, All Documents of a particular Originator have to be in the list of DocumentDetails and all TransactionDetails of a particular document have to be in that list.
One way is to create a dictionary and add stuff in it and finally create an object. I was wondering if there was a more abbreviated and efficient way to do something like this.
TIA
You can do the grouping of retrieved records of OutgoingPaymentApprovalDetails using Linq to create the nested object of OriginatorDetails collection.
see below code
var originalDetails = inputs.GroupBy(g => g.OriginatorId)
.Select(g => new OriginatorDetails()
{
OriginatorId = g.Key,
Name = g.First().Name,
DocumentDetails = g.GroupBy(d => d.DocumentId)
.Select(d => new DocumentDetails()
{
DocumentId = d.Key,
TransactionDetails = d.Select(t => new TransactionDetails()
{
DebitAccountNo = t.DebitAccountNo,
CreditAccountNo = t.CreditAccountNo,
Amount = t.Amount.ToString()
}).ToList()
})
.ToList()
});
Check the created https://dotnetfiddle.net/FCA7Qc to demostrate your scenario.
Try this code:
Basically you need to group 2 times, first time by OriginatorId and Name and then by DocumentId like this:
var result = list.GroupBy(c => new {c.OriginatorId, c.Name})
.Select(g => new OriginatorDetails()
{
Name = g.Key.Name,
OriginatorId = g.Key.OriginatorId,
DocumentDetails = g
.GroupBy(dd => dd.DocumentId)
.Select(dd => new DocumentDetails()
{
DocumentId = dd.Key,
TransactionDetails = dd.ToList()
.Select(td => new TransactionDetails()
{
Amount = td.Amount.ToString(),
CreditAccountNo = td.CreditAccountNo,
DebitAccountNo = td.DebitAccountNo
}).ToList()
}).ToList()
}).ToList();

How to display two tables separately in a single view with a join query in one table?

I am creating a dashboard in ASP.NET MVC using C# and Entity Framework.
I have two tables
Tbl_Channel (Id, Channel_Name)
Tbl_News (Id, Channel_Id, News_Title, News_Description)
When I tried this query:
public ActionResult Dashboard()
{
ShowData model = new ShowData();
var rs1 = (from c in db.Tbl_Channel select c).ToList();
var rs2 = (from c in db.Tbl_News
join d in db.Tbl_Channel on c.Channel_Id equals d.Id
select new
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
})
.OrderByDescending(x => x.Id)
.ToList();
model.tbl_ChannelData = rs1;
model.tbl_NewsData = rs2;
return View(model);
}
I get the following error in line model.tbl_NewsData = rs2:
Model class :
public class ShowData
{
public List<Tbl_Channel> tbl_ChannelData { get; set; }
public List<Tbl_News> tbl_NewsData { get; set; }
}
Error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.List
The output I want to display be like:
(Id, Channel_Name, News_Title, News_Description)
The root cause of the problem is this: you have defined your properties in the model class like this:
public class ShowData
{
public List<Tbl_Channel> tbl_ChannelData { get; set; }
public List<Tbl_News> tbl_NewsData { get; set; }
}
Yet, your query selects an anonymous type:
select new
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
}
This causes the error. What you need to do is instantiate objects of the type defined on your model class, e.g.
select new Tbl_News
{
c.Id,
c.News_Title,
c.News_Description,
d.Channel_Name
}
or if this anonymous type doesn't match any existing type yet, you need to define a suitable type
public class ChannelAndNews
{
public int ChannelId { get; set; }
public string NewsTitle { get; set; }
public string NewsDescription { get; set; }
public string ChannelName { get; set; }
}
and create instances of that type, which you can then assign to the model class properties:
select new ChannelAndNews
{
ChannelId = c.Id,
NewsTitle = c.News_Title,
NewsDescription = c.News_Description,
ChannelName = d.Channel_Name
}
Changing query to this one maybe it work:
var query = (from c in db.tblnews
join d in db.tblchannel on c.Channel_Id equals d.Id
Select new Data()
{
Id = c.Id,
News_Title= c.News_Title,
News_Description= c.News_Description,
Channel_Name= d.Channel_Name
})
.OrderByDescending(x => x.Id)
.ToList();
return View(query)
create this class:
public class Data
{
public int Id{ get; set; }
public string News_Title{ get; set; }
public string News_Description{ get; set; }
public string Channel_Name{ get; set; }
}
Your Expected Result:[Just Assumption]
[]1
The Classes Needed:[In Your Case]
public class Tbl_Channel
{
public int Id { get; set; }
public string Channel_Name { get; set; }
}
public class Tbl_News
{
public int Id { get; set; }
public int Channel_Id { get; set; }
public string News_Title { get; set; }
public string News_Description { get; set; }
}
public class ChannelsAndNews
{
public int ChannelId { get; set; }
public string Channel_Name { get; set; }
public string News_Title { get; set; }
public string News_Description { get; set; }
}
public class ShowData
{
public List<Tbl_Channel> Channels { get; set; }
public List<Tbl_News> News { get; set; }
public List<ChannelsAndNews> ChannelsAndNews { get; set; }
}
The Controller Method: [Changes]
public ActionResult ShowData()
{
// GetChannels() & GetNews() -- Seeded Locally in your case from DB.
var showDataModel = new ShowData();
showDataModel.Channels = (from channel in GetChannels() select channel).ToList();
showDataModel.News = (from news in GetNews() select news).ToList();
showDataModel.ChannelsAndNews = (from channel in GetChannels()
join news in GetNews() on channel.Id equals news.Channel_Id
select new ChannelsAndNews
{
ChannelId = channel.Id,
Channel_Name = channel.Channel_Name,
News_Title = news.News_Title,
News_Description = news.News_Description
})
.OrderByDescending(channelnews => channelnews.ChannelId)
.ToList();
return View(showDataModel);
}
This just seems to be working fine.

Join 3 One to Many Tables in Entity Framework

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();
}
}

Get List ordered by number of records from another table using Linq

I'm using EF6 and I created this two models:
public class Publication
{
public int Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
public class ViewLog
{
public int Id { get; set; }
public int? UserId { get; set; }
[ForeignKey("UserId")]
public User.User User { get; set; }
public int? SessionId { get; set; }
[ForeignKey("SessionId")]
public User.Session Session { get; set; }
public int PublicationId { get; set; }
[ForeignKey("PublicationId")]
public Publication.Publication Publication { get; set; }
public DateTime Created { get; set; }
}
Every time I visit a Publication I create a new record into ViewLog table.
Now, using Linq, I need to get all Publications ordered by the number of ViewLogs (visits) per publication in the last 24hs. (If Publication has no ViewLogs they need to appear too, but obviously after the publications that has viewlogs)
You can use GroupJoin when you don't have navigation properties and need a left outer join
The lambda syntax goes like this:
var publicationQuery = new List<Publication>().AsQueryable();
var viewLogQuery = new List<ViewLog>().AsQueryable();
var leftJoinQuery = publicationQuery
.GroupJoin(viewLogQuery, x => x.Id, x => x.PublicationId, (pub, logs) => new
{
PublicationId = pub.Id,
LogCount = logs.Count()
})
.OrderByDescending(x => x.LogCount);
I also found this Query Expression Translation Cheat Sheet very useful to go from query expressions (which are closer to SQL) to lambda method syntax (which I prefer)
If you do have a navigation property Publication.ViewLogs (which is a List<ViewLog>), then you can just use Select() with a projection
var publicationQuery = new List<Publication>().AsQueryable(); // From a DbSet...
var query = publicationQuery
.Select(x => new
{
PublicationId = x.Id,
LogCount = x.ViewLogs.Count
})
.OrderByDescending(x => x.LogCount);

Multiple collections in an object using Linq

I have following object
public class bizObj
{
public string name { get; set; }
public int p_id { get; set; }
public string acc_number { get; set; }
public string a_name { get; set; }
public string a_phone { get; set; }
public virtual product product { get; set; }
public virtual account account { get; set; }
}
Linq statment to get data from db is
public IEnumerable<bizObj> GetbizObj(int id)
{
var acs = (from c in db.p_account
where c.p_id==id
select new bizObj
{
name = c.p_name,
p_id = c.product.id,
acc_number=c.account.acc_number,
a_name = c.a_name,
a_phone = c.a_phone
});
return acs;
}
The above code is working fine but it is returning one collection. What I am trying to
get is that it has a collection of
{
name,
p_id
//than a second collection which has all the records that have same name ane p_id
{
acc_number,
a_name
a_phone
}
}
Please let me know how I can accomplish this using linq/lambda expression. Thanks
Question is unclear, but it looks like you're saying you want to group rows by name and p_id.
var query = acs.GroupBy(x => new { x.name, x.p_id })
.Select(g => new { g.Key.name, g.Key.p_id, Items = g.ToList() });

Categories

Resources