I have a Linq query like this:
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
s.Stud_Id,
s.FirstName,
c.Cls_Id,
c.Room,
c.Notification
};
repeater.DataSource = result.ToList();
repeater.DataBind();
But in Notification field has content like this: This Room of C Programming Class/NTFF. If binding in Lable Text='<%#DataBinder.Eval(Container.DataItem, "Notification")%>' it will display: This Room of C Programming Class/NTFF.
I want to split this string into 2 string like this:
str1 = This Room of C Programming Class;
str2 = NTFF;
before binding and binding str1 into Lable1 and str2 into Lable2. How can I do this?
You could use something like this: First create a DTO to store the result entities with all the fields plus one extra field to store the the list of notifications.
public class Result
{
public int Stud_Id { get; set; }
...
...
public string Notification { get; set; }
public string[] Notifications { get; set; }
}
List<Result> result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new Result
{
Stud_Id = s.Stud_Id,
...
...
Notification = c.Notification
}).ToList();
result.ForEach(r =>
{
r.Notifications = r.Notification.Split('/');
});
Now you have two strings in Notifications:
Notification[0] = "This Room of C Programming Class";
Notification[1] = "NTFF"
You can now use whichever you want to bind in the Lable.
You can use Split function to get str1 like this:-
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
s.Stud_Id,
s.FirstName,
c.Cls_Id,
c.Room,
str1 = c.Notification.Split('/').FirstOrDefault()
};
Then, you can bind it to your Label like this:-
<asp:Lable Text='<%#DataBinder.Eval(Container.DataItem, "str1")%>'><asp:Label/>
Update:
Since you are using Entity Framework, you can't use Split function directly. You need to bring the results in memory. One way is to create a custom class and fill it like this:-
public class Students
{
public int Stud_Id { get; set; }
public string FirstName{ get; set; }
public int Cls_Id{ get; set; }
public string Room{ get; set; }
public string Notification{ get; set; }
public string str1{ get; set; }
public string str2{ get; set; }
}
Then, first fill your custom class with query like this:-
List<Students> students = (from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new Students
{
Stud_Id = s.Stud_Id,
FirstName = s.FirstName,
Cls_Id = c.Cls_Id,
Room = c.Room,
Notification= c.Notification
}).ToList();
Finally, iterate through the result and fill up the str1 & str2 variables like this:-
foreach (Student student in students)
{
string[] Notifications = student.Notification.Split('/');
student.str1 = Notifications.FirstOrDefault();
student.str2 = Notifications.ElementAtOrDefault(1);
}
After this, simply bind your labels with parameters str1 & str2.
Use string.Replace() like below:
<%# ((string)DataBinder.Eval(Container.DataItem, "Notification")).Replace("/NTFF", string.Empty) %>
Please check the syntax first. But should work in this case. Let me know if its not working.
Edit:
Code Behind:
var result = from c in db.Class
join s in db.Students on c.Cls_Id equals s.Cls_Id
select new
{
Id = s.Stud_Id,
FirstName = s.FirstName,
ClassId = c.Cls_Id,
Room = c.Room,
FirstNotification = c.Notification.Split('/')[0],
SecondNotification = c.Notification.Split('/')[1]
};
Then on front end use FirstNotification and SecondNotification properties.
Note: Above code will throw Index out of bound exception when there is no '/' character.
Related
Hello i have a console app with which I have to fetch data from few tables and create a excel file out of it, here is my code
var Result = (from a in Db.tbl_ApplicantMaster
join b in Db.tbl_App_Process on a.APP_ID equals b.APID
join c in Db.tbl_Process on b.ProcessID equals c.ID
join d in Db.tbl_Nationality on a.Nationality equals d.country_code
join e in Db.tbl_AgencyMaster on a.Agn_ID equals e.AgID
select new ExcelData
{
Name = a.Name,
AppId = a.APP_ID,
ProcessName = c.Process,
StartDate = b.StartTime.ToString(),
EndDate = b.EndTime.ToString(),
Nationality = d.country_enName,
Agency = e.AgencyName,
}).ToList();
var File = new FileInfo(#"C:\path\ExcelSheet.xlsx");
await GetExcelFile.ExportDataToExcel(Result, File);
And This is the Code I use to create an Excel file
public static async Task ExportDataToExcel(List <ExcelData> data,FileInfo File)
{
try
{
DeleteIfExists(File);
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var Package =new ExcelPackage(File))
{
var Ws = Package.Workbook.Worksheets.Add(Name: "MainReport");
var range = Ws.Cells[Address: "A1"].LoadFromCollection(data, true);
range.AutoFitColumns();
await Package.SaveAsync();
}
}
catch (Exception)
{
throw;
}
}
This is my Model
public class ExcelData{
public int AppId { get; set; }
public string Name { get; set; }
public string ProcessName { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
public string Nationality { get; set; }
public string Agency { get; set; }
public double No_of_Days { get; set; }
}
with this I am able to get the required data and even create a excel sheet from it but the excel sheet requires to be in a specific pattern where the applicant name should be shown only and then the date and name of each process, every applicant has 16 process so its not good to repeat the same name in each column with the process, the current data comes as following
ApplicantID:111
Name: X
ProcessName:Licence
startDate:10-10-2021
EndDate: 20-10-2021
ApplicantID:111
Name:x
ProcessName:TOIEC Test
start date:10-10-2021
End Date: 20-10-2021
ApplicantID:111
Name:x
ProcessName:Physical Test
start date:10-10-2021
End Date: 20-10-2021
but I want it to be like
ApplicantID:111
Name: X
ProcessName:Licence
startDate:10-10-2021
EndDate: 20-10-2021
ProcessName:TOIEC Test
start date:10-10-2021
End Date: 20-10-2021
ProcessName:Physical Test
start date:10-10-2021
End Date: 20-10-2021
How can I achieve this in C# ? do I have to use any thirdparty libs for this? Please Help Me Out,ThankYou
Change the ExcelData class and make AppId and Name nullable (since Name is a string you have to this only if you are working with nullable reference types).
Then add an orderby to your query and order the items by a.APP_ID:
var Result = (from a in Db.tbl_ApplicantMaster
join b in Db.tbl_App_Process on a.APP_ID equals b.APID
join c in Db.tbl_Process on b.ProcessID equals c.ID
join d in Db.tbl_Nationality on a.Nationality equals d.country_code
join e in Db.tbl_AgencyMaster on a.Agn_ID equals e.AgID
orderby a.APP_ID
select new ExcelData {
Name = a.Name,
AppId = a.APP_ID,
ProcessName = c.Process,
StartDate = b.StartTime.ToString(),
EndDate = b.EndTime.ToString(),
Nationality = d.country_enName,
Agency = e.AgencyName,
}).ToList();
Now you can process the excel data list and set duplicate AppId and Name to null:
int? lastAppId = null;
foreach (var item in Result) {
if (item.AppId == lastAppId) {
item.AppId = null;
item.Name = null;
} else {
lastAppId = item.AppId;
}
}
This probably not perfect, but since we must pass elements of a single type to LoadFromCollection, we are somewhat limited. I don't know whether it works to insert null items into the list. If Excel inserts empty lines, this would help doing some grouping. something like this:
for (int i = Result.Count - 1; i >= 0; i--) {
var item = Result[i];
if (item.AppId == lastAppId) {
item.AppId = null;
item.Name = null;
} else {
lastAppId = item.AppId;
Result.Insert(i, null);
}
}
Note that this loops the list in the reverse order, so that inserting the null entry does not change the index of the not yet processed items.
I'm using Dapper and I have classes like this:
public class Article{
public int Id { get; set; }
public string Description{get;set;}
public Group Group { get; set; }
public List<Barcode> Barcode {get;set;}
...
}
public class Group{
public int Id { get; set; }
public string Description {get;set;}
}
public class Barcode{
public int Id { get; set; }
public string Code{get;set;}
public int IdArticle { get; set; }
...
}
I can get all information about Article but I would like to know if is possible with one query get also the list of barcodes for each article. Actually what I do is this:
string query = "SELECT * FROM Article a " +
"LEFT JOIN Groups g ON a.IdGroup = g.Id ";
arts = connection.Query<Article, Group, Article>(query,
(art, gr) =>
{ art.Group = gr; return art; }
, null, transaction).AsList();
I also found a good explanation here but I don't understand how to use it in my case, because I have also the Group class.
How should I do this with Dapper, is it possible or the only way is to do different steps?
Thanks
QueryMultiple is your friend
var query = #"
select a.*, g.* from Article a left join Groups g on g.Id = a.IdGroup
select * from Barcode";
//NOTE: IdGroup should exists in your Article class.
IEnumerable<Article> articles = null;
using (var multi = connection.QueryMultiple(query)){
articles = multi.Read<Article, Group, Article>((a, g)=>
{ a.Group = g; return a; });
if (articles != null) {
var barcodes = multi.Read<Barcode>().ToList();
foreach(var article in articles){
article.Barcode = barcodes.Where(x=>x.IdArticle == article.Id).ToList();
}
}
}
That may not be fun especially if you don't have any filters in your query. But I doubt that you will return all Articles. In that case you can filter the Barcode like this (edited sql) > select * from Barcode where Id in #ids. Then include the parameter ids (a list of Article Ids) in the QueryMultiple.
Option2
Or you could just do separate queries:
var query = "select a.*, g.* from Article a left join Groups g on g.Id = a.IdGroup";
var articles = connection.Query<Article, Group, Article>(query,
(a,g)=> { a.Group = g; return g; }).ToList();
query = "select * from Barcode where IdArticle IN #articleIds";
var articleIds = articles.Select(x=>x.Id);
var barcodes = connection.Query<Barcode>(query, new { articleIds });
foreach(var article in articles){
article.Barcode = barcodes.Where(x=>x.IdArticle == article.Id);
}
I prefer the first option.
i have basically a post repository that should return all the gallery items belong to it. If there's no gallery belonging to post it should still return post distinct by post id
public List<PostLocalizedOutput> GetAllPostsWithCategories(string culture, bool? isPublished)
{
var query =
from p in Context.Posts
join pl in Context.PostsLocalized on p.Id equals pl.PostId
from c in p.Categories
join cl in Context.CategoriesLocalized on c.Id equals cl.CategoryId
from g in p.Galleries.DefaultIfEmpty()
join gi in Context.GalleryItems on g.Id equals gi.GalleryId
where
pl.Culture == culture &&
cl.Culture == culture
select new PostLocalizedOutput
{
PostId = pl.PostId,
CategoryId = cl.CategoryId,
Title = pl.Title,
FormattedCategoryName = cl.FormattedCategoryName,
PostContent = pl.PostContent,
PostType = pl.Post.PostType,
IOrder = pl.Post.IOrder,
Tags = pl.Tags,
PublishDate = pl.Post.PublishDate,
ViewCount = pl.Post.ViewCount,
ShowInHomePageSlider = pl.Post.ShowInHomePageSlider,
AllowComments = pl.Post.AllowComments,
Image = pl.Post.Image,
IsArchived = pl.Post.IsArchived,
IsDraft = pl.Post.IsDraft,
IsPublished = pl.Post.IsPublished,
GalleryItems = new GalleryItemOutput
{
FileName = gi.FileName,
GalleryId = gi.GalleryId,
Id = gi.Id,
Notes = gi.Notes,
Title = gi.Title
} (around here i feel like i should foreach something or what?)
};
return query.OrderBy(x => x.IOrder).ThenBy(x => x.PublishDate).DistinctBy(x => x.PostId).ToList();
}
here is my postlocalizedoutput
public class PostLocalizedOutput : IOutputDto
{
public int PostId { get; set; }
public int CategoryId { get; set; }
public bool IsPublished { get; set; }
...
public List<GalleryItemOutput> GalleryItems { get; set; }
}
GalleryItemOutput should be list because i want all the galleryitems of a post. But when i define it as a list in repository i cannot set each field of galleryitem of a post. This code now returns me four rows because i have four gallery items of that post and each one has the same postId. I do not want that. DefaultIfEmpty also does not work even if a post does not have any gallery items i should still be able to get that post without gallery items.
Any approach ?
Thanks for all suggestions.
This should help you get started.
Don’t know if this compiles, please just interpret as pseudo-code. Looks like your top-level range variable is PostsLocalized not Posts. This code assume/uses navigation properties that are probably setup in your EDM classes. I have overused let keyword here just to make it clearer for you. In the select new clause you can change the p1.Post.... to just p. I left them to make as few edits as possible.
var query =
from pl in Context.PostsLocalized
where pl.Culture == culture
let p = p1.Post
let categories = p.Categories
let localizedCategories = categories.SelectMany(cat => cat.CategoriesLocalized).Where(cl => cl.Culture == culture)
let galleries = p.Galleries
let galleryItems = galleries.SelectMany(gal => gal.GalleryItems)
let cl = localizedCategories.FirstOrDefault() // only one or zero of these i assume?
select new PostLocalizedOutput
{
PostId = p1.PostId,
CategoryId = cl.CategoryId,
Title = pl.Title,
FormattedCategoryName = cl.FormattedCategoryName,
PostContent = pl.PostContent,
PostType = pl.Post.PostType,
IOrder = pl.Post.IOrder,
Tags = pl.Tags,
PublishDate = pl.Post.PublishDate,
ViewCount = pl.Post.ViewCount,
ShowInHomePageSlider = pl.Post.ShowInHomePageSlider,
AllowComments = pl.Post.AllowComments,
Image = pl.Post.Image,
IsArchived = pl.Post.IsArchived,
IsDraft = pl.Post.IsDraft,
IsPublished = pl.Post.IsPublished,
GalleryItems = galleryItems.Select(gi => new GalleryItemOutput
{
FileName = gi.FileName,
GalleryId = gi.GalleryId,
Id = gi.Id,
Notes = gi.Notes,
Title = gi.Title
})
// might need a .ToList() here on those GalleryItems
(around here i feel like i should foreach something or what?)
};
I am trying to get a list filtered based on the matches of one of the properties with a property of another list.
In below example, only the items which have common 'name' between both lists should be filtered in 1st list. Can some one tell me the most concise way of doing it?
class TCapability
{
public string Name { get; set; }
public int Id { get; set; }
}
class PCapability
{
public string Name { get; set; }
public int Code { get; set; }
}
Input:
var capability = new List<TCapability>()
{
new TCapability() {Name="a", Id=1},
new TCapability() {Name="b", Id=2},
new TCapability() {Name="c", Id=3}
};
var type2Capability = new List<PCapability>()
{
new PCapability() {Name="a", Code=100},
new PCapability() {Name="b", Code=200},
new PCapability() {Name="d", Code=300}
};
Expected Output:
capability =
{
{ Name="a", Id=1 },
{ Name="b", Id=2 }
}
var result = capability.Where(c => type2Capability.Any(c2 => c.Name == c2.Name));
you can try use join clause like this
capability = (from a in capability
join b in type2Capability on a.Name equals b.Name
select a).ToList();
UPDATE on comment if type2Capability can have duplicate names
capability = (from a in capability
join b in type2Capability on a.Name equals b.Name into f
where f.Any()
select a).ToList();
If the lists can get long then a HashSet can speed things up.
var set = new HashSet<string>(type2Capability.Select(t => t.Name));
var res = capability.Where(c => set.Contains(c.Name));
I have my class structure as follows
public class Email
{
public string Subject {get;set;}
public string Message {get;set;}
public Contact Sender {get;set;}
public string SenderEmail {get;set;}
}
public class Contact
{
public string Email {get;set;}
public string Name {get;set;}
}
and I run my Linq query in two parts
First i select all the emails.
var query = from msg in context.Email
select msg;
Then i assign the Contact details to the Email Class
List<Email> outputList = new List<Email>();
foreach (var item in query.ToList())
{
var q = from contact in context.Contact
where contact.Email = item.SenderEmail
select contact;
item.Sender = q.SingleOrDefault();
outputList.Add(item);
}
return outputList;
Is there anyway i can run a join query and simply output the List without having to run multiple queries
I think this would do the trick (warning: untested code):
var qry = from email in context.Email
join contact in context.Contact
on email.SenderEmail equals contact.Email
into contacts
select new { eml = email, sender = contacts.FirstOrDefault() };
var items = qry.ToList();
foreach (var item in items)
{
item.eml.Sender = item.sender;
outputList.Add(item.eml);
}
return outputList;
I suspect this should work:
var query = from msg in context.Email
join contact in context.Contact
on msg.SenderEmail equals contact.Email
into contacts
select new { msg, contacts };
var list = query.ToList();
foreach (var pair in list)
{
pair.msg.Sender = pair.contacts.FirstOrDefault();
}
var messages = list.Select(pair => pair.msg);
This uses a group join. You haven't said which LINQ provider you're using, but I expect it should work for most providers...