JOIN with LinqtoSql, only select TOP(x) on joined table? - c#

I've had a look around on StackOverlow but haven't been able to find a definitive answer on this.
Below I have a code snippet of what I currently have, and I will explain what I am trying to achieve.
Table<Gallery> galleries = pdc.GetTable<Gallery>();
Table<GalleryImage> images = pdc.GetTable<GalleryImage>();
Table<Comment> comments = pdc.GetTable<Comment>();
var query = from gallery in galleries
join image in images on gallery.id equals image.galleryid into joinedimages
join comment in comments on gallery.id equals comment.galleryid into joinedcomments
select gallery;
gallst.DataSource = query;
gallst.DataBind();
From the above I then have the following repeater:
<asp:Repeater ID="gallst" runat="server" EnableViewState="false">
<HeaderTemplate>
<div id="gallery">
</HeaderTemplate>
<ItemTemplate>
<div class="item">
<h2><%# DataBinder.Eval(Container.DataItem, "name") %> # <%# DataBinder.Eval(Container.DataItem, "wheretaken") %></h2>
<ul class="images">
<asp:Repeater ID="galimgs" runat="server" EnableViewState="false" DataSource='<%# Eval("GalleryImages") %>'>
<ItemTemplate>
<li><img src="<%# DataBinder.Eval(Container.DataItem, "image") %>_thumb.jpg" /></li>
</ItemTemplate>
</asp:Repeater>
</ul>
<div class="comments">
<asp:Repeater ID="galcomments" runat="server" EnableViewState="false" DataSource='<%# Eval("Comments") %>'>
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><%# GetUserName(new Guid(Eval("userid").ToString())) %> said: <%#DataBinder.Eval(Container.DataItem, "comment1") %> (<%# DataBinder.Eval(Container.DataItem, "date", "{0:dddd MM, yyyy hh:mm tt}") %>)</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
<uc:makecomment ID="mcomment" runat="server" PhotoID='<%# DataBinder.Eval(Container.DataItem, "id") %>'></uc:makecomment>
</div>
</div>
</ItemTemplate>
<FooterTemplate>
</div>
</FooterTemplate>
</asp:Repeater>
What I want to do (ideally) is to only take the first 3 comments for each gallery.
I've tried the following LINQ Query with no luck:
var query = from gallery in galleries
join image in images on gallery.id equals image.galleryid into joinedimages
join comment in comments.Take(3) on gallery.id equals comment.galleryid into joinedcomments
select gallery;
Does anyone have any suggestions on how I can achieve this?

This looks like it might be the tweak you need. It's from a very helpful LINQ sample site.
This sample prints the customer ID, order ID, and order date for the first three orders from customers in Washington. The sample uses Take to limit the sequence generated by the query expression to the first three of the orders.
public void Linq21() {
List<Customer> customers = GetCustomerList();
var first3WAOrders = (
from c in customers
from o in c.Orders
where c.Region == "WA"
select new {c.CustomerID, o.OrderID, o.OrderDate} )
.Take(3);
Console.WriteLine("First 3 orders in WA:");
foreach (var order in first3WAOrders) {
ObjectDumper.Write(order);
}
}
Result
First 3 orders in WA:
CustomerID=LAZYK OrderID=10482 OrderDate=3/21/1997
CustomerID=LAZYK OrderID=10545 OrderDate=5/22/1997
CustomerID=TRAIH OrderID=10574 OrderDate=6/19/1997

I managed to get it to work with:
Table<Gallery> galleries = pdc.GetTable<Gallery>();
Table<GalleryImage> images = pdc.GetTable<GalleryImage>();
Table<Comment> comments = pdc.GetTable<Comment>();
var query = from gallery in galleries
join image in images on gallery.id equals image.galleryid into joinedimages
join comment in comments on gallery.id equals comment.galleryid into joinedcomments
select new
{
name = gallery.name,
wheretaken = gallery.wheretaken,
id = gallery.id,
GalleryImages = joinedimages,
Comments = joinedcomments.Take(3)
};
gallst.DataSource = query;
gallst.DataBind();
With the take taken place on the select. Thanks for your help everyone. Any suggestions on how to write this "better" would be appreciated.

Related

How to execute Linq query and Binding data in Lable of asp.net pages

I have a Linq query:
var result = from p in db.Messages
join c in db.Students on p.Stud_Id equals c.Stud_Id
where (p.Staff_Id == Int32.Parse(userID.Text))
select new
{
c.Stud_Id,
c.FirstName,
p.BySupervisor
};
How can I add result to list and binding each value in Lable using DataBinder. Of course, i have already repeater control to databound
Try like this
Create a class
public class stud
{
public int Stud_Id {set;get;}
public string FirstName{set;get;}
public string BySupervisor {set;get;}
}
Then convert dataset into that class's list
var result = (from p in db.Messages
join c in db.Students on p.Stud_Id equals c.Stud_Id
where (p.Staff_Id == Int32.Parse(userID.Text))
select stud
{
Stud_Id=c.Stud_Id,
FirstName=c.FirstName,
BySupervisor=p.BySupervisor
}).ToList<stud>();
Then bind data with repeater
repeaterControl.DataSource = result ;
repeaterControl.DataBind();
If you already have included the Repeater control(as per your comment) then simply assign the datasource like this:-
YourrepeaterControlId.DataSource = result;
YourrepeaterControlId.DataBind();
Please note there is No Need to materialize your result into a List<T>, it will work just fine if you pass IEnumerable<T>.
Thus in your case suppose your control looks like this:-
<asp:Repeater ID="YourrepeaterControlId" runat="server">
<ItemTemplate>
<asp:Label ID="StudIdLabel" runat="server" Text='<%# Eval("Stud_Id") %>'>
</asp:Label>
<asp:Label ID="FirstNameLabel" runat="server" Text='<%# Eval("FirstName") %>'>
</asp:Label>
<asp:Label ID="lSupervisor" runat="server" Text='<%# Eval("BySupervisor") %>'>
</asp:Label>
</ItemTemplate>
</asp:Repeater>
Binding your control as I mentioned earlier will work just fine, there is no need to create an extra class.

how to use ul and li in code behind c#

I am trying to fill li and ul of an HTML file using my database.
single category representing multiple items in database.
I have taken the li items in a string, I am replacing [food] with CATEGORY name and [itemTemplate] with ITEMS. The issue in my code is the category name is repeating
Every time as new item display. I have to show category name once and add all related items within that category.
String liTemplate = "<li><h4 class='menu-title-section'> <h4 class='menu-title-section'><a href='#appetizers'>[food]</a></h4>[itemTemplate]</li>";
String itemTemplate = "SOME ITEM TEMPLETE";
DataTable dt = dm.GetData("spTestMenu")
StringBuilder sb = new StringBuilder();
sb.Append("<ul class='our-menu'>");
String liTemplateWorkingCopy = String.Empty, itemTemplateWorkingCopy = String.Empty
foreach (DataRow level1DataRow in dt.Rows)
{
liTemplateWorkingCopy = liTemplate;
itemTemplateWorkingCopy = itemTemplate;
SubCategoryName = level1DataRow["MealSubCatName"].ToString();
if (!previusSubCat.Equals(SubCategoryName))
{
liTemplateWorkingCopy = liTemplateWorkingCopy.Replace("[food]", level1DataRow["MealSubCatName"].ToString());
previusSubCat = SubCategoryName;
}
itemTemplateWorkingCopy = itemTemplateWorkingCopy.Replace("[foodtitle]", level1DataRow["itemName"].ToString());
itemTemplateWorkingCopy = itemTemplateWorkingCopy.Replace("[imgsrc]", level1DataRow["imageURL"].ToString());
itemTemplateWorkingCopy = itemTemplateWorkingCopy.Replace("[price]", level1DataRow["Price"].ToString());
liTemplateWorkingCopy = liTemplateWorkingCopy.Replace("[itemTemplate]", itemTemplateWorkingCopy);
sb.Append(liTemplateWorkingCopy);
foodMenu.Text = sb.ToString();
}
You can set runat="server" for <li> or <ul>..
<li class="abc" runat="server" id="first"></li>
I would suggest using a ListView control for this. This way you can maintain the HTML outside of your code; it's cleaner that way and much more elegant.
Group your rows by the 'MealSubCatName' column, and use LINQ to create an anonymous object:
C#
var grouped = dt.AsEnumerable().GroupBy(c => c["MealSubCatName"].ToString())
.Select(g => new
{
category = g.FirstOrDefault()["MealSubCatName"].ToString(),
items = g.Select(r => new {
title = r["itemName"].ToString(),
image = r["imageURL"].ToString()
})
});
lvFood.DataSource = grouped;
lvFood.DataBind();
ASPX
<asp:ListView ID="lvFood" runat="server">
<LayoutTemplate>
<ul>
<asp:PlaceHolder runat="server" ID="groupPlaceholder" />
</ul>
</LayoutTemplate>
<GroupTemplate>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</GroupTemplate>
<ItemTemplate>
<li>
<h4 class="menu-title-section">
<%# Eval("category") %>
</h4>
</li>
<asp:Repeater ID="rpt" runat="server" DataSource='<%# Eval("items") %>'>
<ItemTemplate>
<img src="<%# Eval("image")%>" alt="<%# Eval("title")%>" />
<strong><%# Eval("title")%></strong><br />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:ListView>
Add a div with runat server and assign a Id to that div and do something like this I did this trick here www.journeycook.com Check menu of this website.
Suppose you have a div with id link and runat server in ul tag
<ul>
<div runat="server" id="link">
</div>
</ul>
Now add c# code for fetch data using SqlDataReader class and using while loop do something like this
link.innerhtml+="<li>dr["yourcolumn"].ToString()</li>";
I hope it will give you some help

Sort Inner Repeater with LINQ query

I am attempting to list a group of Associations, within each association is a 'widget' that is assigned to the association. The list will include the Association name and any widget assigned to it. The catch is that the inner widget list needs to be sorted by DisplaySequence.
EDMX Model Below:
Simplified Repeater Mark-Up
<asp:Repeater ID="rptAssociations" runat="server">
<ItemTemplate>
<div data-type="assn" id="<%# ((Association)Container.DataItem).AssociationID %>">
<h3 style="margin-top:15px;"><%# ((Association)Container.DataItem).Acronym %> - <%# ((Association)Container.DataItem).Name %></h3>
<asp:Repeater runat="server" ID="rptWidgets" DataSource="<%# ((Association)Container.DataItem).AssociationWidgets %>" >
<HeaderTemplate>
<ul class="WidgetList">
</HeaderTemplate>
<ItemTemplate>
<li id="<%# ((AssociationWidget)Container.DataItem).DisplaySequence %>"><%# ((AssociationWidget)Container.DataItem).Widget.Name %></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</div>
</ItemTemplate>
</asp:Repeater>
Current Query
var associations = (from a in
context.Associations.Include("AssociationWidgets")
.Include("AssociationWidgets.Widget")
orderby a.Acronym
select a).ToList();
rptAssociations.DataSource = associations;
rptAssociations.DataBind();
I am currently able to get the data that I'm looking for with the setup that I have now. I am looking for the most efficient approach to getting this same data, however, having the Widgets listed in the correct display order.
Is there a different approach to the linq query I should take?
I would approach this like this (untested):
var associations =
context.Associations.Select( a =>
new {
//... specific properties you need
AssociationId = a.AssociationId,
Name = a.Name,
... etc
Widgets = a.AssociateWidgets.OrderBy(aw => aw.DisplaySequence)
.Select(aw => aw.Widget)
}
);
Here you'll get a collection of anonymous types. You can use a concrete type such as
public class AssociationInfo
{
public string Name {get;set;}
...
public IEnumerable<Widget> Widgets{ get;set; }
}
if necessary by replacing 'new {' with 'new AssociationInfo {'

Populating a listbox control in asp.net

I have a code block that returns a list of employee object.
The resultset contains more than one employee record. One of the elements, is EmployeeID.
I need to populate listview (lstDepartment) with only the EmployeeID. How can I do that?
lstDepartment.DataSource = oCorp.GetEmployeeList(emp);
lstDepartment.DataBind()
You have to also specify this:
lstDepartment.DataSource = oCorp.GetEmployeeList(emp);
lstDepartment.DataTextField = "EmployeeID";
lstDepartment.DataValueField = "EmployeeID";
lstDepartment.DataBind()
One way would be to use an anonymous type:
lstDepartment.DataSource = oCorp.GetEmployeeList(emp)
.Select(emp => new { emp.EmployeeID });
lstDepartment.DataBind();
Edit: But you also could select all columns but diplay only one. A ListView is not a ListBox or DropDownList. Only what you use will be displayed. So if you're ItemTemplate looks like:
<ItemTemplate>
<tr runat="server">
<td>
<asp:Label ID="LblEmployeeID" runat="server" Text='<%# Eval("EmployeeID") %>' />
</td>
</tr>
... only the EmployeeID is displayed, no matter whatelse is in your DataSource.
You may mention what to display in your ItemTemplate of ListView
<asp:ListView ID="lstDepartment" runat="server">
<ItemTemplate>
<p> <%#Eval("EmployeeID") %> </p>
</ItemTemplate>
</asp:ListView>

Datalist display items based on file names

This question to related to a similar Data list question I posted some days ago. I have a datalist which displays Categories, and then documents within categories. What needs to happen is that documents under each category gets displayed in the order based on some numbers in file names. Documents are in format like '001-filename.pdf', '002-filename.pdf' ... '00x-filename.pdf'. I can use the first dash as some kind of 'split' function then grab the numbers like '001' etc to make the sorting to work. I think this could be done on either itemdatabound or in the sql syntax. I am posting the relevant code here. Any idea as to how I can make this to work? It is possible that there could be more than one document with shared number prefix: '001-filename.pdf', '001-filenameversion2.pdf' etc. Thanks!
ASPX:
<asp:DataList ID="DataList1" runat="server" RepeatDirection="Vertical" DataKeyField="docid"
EnableViewState="True" OnItemDataBound="DataList1_ItemDataBound">
<ItemTemplate>
<table cellpadding="0" cellspacing="0" id="tbl_data">
<tr runat="server" id="tr_category">
<td>
<asp:Label ID="lblHeader" runat="server" Font-Bold="True" Text='<%# Eval("categoryname") %>'
Font-Underline="True"></asp:Label>
<asp:Label runat="server" ID="lbl_cb_all">Select All
<asp:CheckBox runat="server" OnCheckedChanged="CheckAllChanged" AutoPostBack="true"
ID="cb_selectall" />
</asp:Label>
<asp:HiddenField ID="HiddenCatID" runat="server" Value='<%# Eval("CatID") %>' />
<asp:HiddenField ID="HiddenDocID" runat="server" Value='<%# Eval("docid") %>' />
</td>
</tr>
<tr runat="server" id="tr_data">
<td>
<asp:CheckBox runat="server" ID="cb_docid" Value='<%# Eval("docid") %>' OnCheckedChanged="displayselectedinit"
AutoPostBack="true" />
<asp:HyperLink ID="hpl_docfileencr" Text='<%# Eval("docfileencr") %>' NavigateUrl='<%# "~/PDFEncr/" + DataBinder.Eval(Container.DataItem, "docfileencr") %>'
Target="_blank" runat="server" />
<br />
</td>
</tr>
</table>
</ItemTemplate>
</asp:DataList>
C# CodeBehind:
sqlsyntax = #"SELECT dbo.projectsdocuments.docfileencr,dbo.categories.catid, dbo.categories.categoryname, dbo.projectsdocuments.docid
FROM dbo.Projects INNER JOIN dbo.projectsdocuments ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = " + projectid + " ORDER BY dbo.categories.sortorder ASC";
protected void DataList1_ItemDataBound(Object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var row = (DataRowView)e.Item.DataItem;
var view = row.DataView;
var lastRow = e.Item.ItemIndex == 0 ? null : view[e.Item.ItemIndex - 1];
var tr_category = (System.Web.UI.HtmlControls.HtmlTableRow)e.Item.FindControl("tr_category");
var sameCategory = lastRow != null && (int)row["catid"] == (int)lastRow["catid"];
tr_category.Visible = !sameCategory;
}
}
Change your ORDER BY to include both columns.
SELECT
dbo.projectsdocuments.docfileencr,
dbo.categories.catid,
dbo.categories.categoryname,
dbo.projectsdocuments.docid
FROM dbo.Projects
INNER JOIN dbo.projectsdocuments
ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories
ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = " + projectid + "
ORDER BY dbo.categories.sortorder, dbo.projectsdocuments.docfileencr
The order by precedence is left to right, the default is "ASCENDING" but you can change a specific column sort by adding "ASC or DESC" directly after it.
** IMPORTANT NOTE **
Your code is susceptible to SQL Injection because your doing string concatenation. If this is a concern to you, change the SQL statement to use a named parameter that you assign using a command parameter (see example below).
Step 1: Change the inline string parameter, to a named parameter "#projectid".
SELECT
dbo.projectsdocuments.docfileencr,
dbo.categories.catid,
dbo.categories.categoryname,
dbo.projectsdocuments.docid
FROM dbo.Projects
INNER JOIN dbo.projectsdocuments
ON (dbo.Projects.projectid = dbo.projectsdocuments.projectid)
INNER JOIN dbo.categories
ON (dbo.projectsdocuments.categoryid = dbo.categories.catid)
WHERE Projects.projectid = #projectid
ORDER BY dbo.categories.sortorder, dbo.projectsdocuments.docfileencr
Step 2: Assign the parameter inline (example code)
using(SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
SqlCommand command = new SqlCommand(sql, conn);
command.CommandType = CommandType.Text;
// Assign the value projectid to the parameter #projectid
command.Parameters.Add(new SqlParameter("#projectid", projectid));
// Execute The Command (fill dataset, create datareader, etc...)
SqlDataReader reader = command.ExecuteReader();
}

Categories