How to remove duplicate property from a list in C# - c#

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.

Related

How to do Linq query update

I have the following method to return the data of two entities.
public List<Object> GetDados()
{
var sendFilter = new Filter<MessageSent>();
//employeeFilter.Add(x => x.Name, name);
sendFilter.Add(x => x.MessageSentSeq, ID_GROUP_SEND);
// You can add more filters
MessageSentService svc = new MessageSentService();
var messages = svc.Find(sendFilter).ToList();
var employees = new EmployeeService().GetAll();
var query =
from employee in employees
join message in messages
on employee.EmployeeId equals message.EmployeeId
select new
{
MessageSentId = message.MessageSentId,
//EmployeeId = message.EmployeeId,
//MessageSentSeq = message.MessageSentSeq,
Name = employee.Name,
Surname = employee.Surname,
Mobile = employee.Mobile,
Email = employee.Email,
Status = "N"
};
return query.ToList<Object>();
}
Call
ILog log = LogManager.GetLogger(typeof(Form));
List<Object> Send;
Send = GetDados();
gvSent.DataSource = Send;
When doing send I would like to update the record that is inside object var query that in turn populates my grid
I need to set the status field with S at the end of everything I updated my database with the items that were sent correctly.
Because your function returns a List<Object>, you'll be unable to modify those properties without using Reflection or some other drastic measure. If you plan on this data being modifiable, you'll need to make sure the data you're returning is typed. First define a simple class:
class Dado
{
public string MessageSentId { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public bool Mobile { get; set; }
public string Email { get; set; }
public string Status { get; set; }
}
...then modify your function to return instances of this type:
public List<Dado> GetDados()
{
var sendFilter = new Filter<MessageSent>();
//employeeFilter.Add(x => x.Name, name);
sendFilter.Add(x => x.MessageSentSeq, ID_GROUP_SEND);
// You can add more filters
MessageSentService svc = new MessageSentService();
var messages = svc.Find(sendFilter).ToList();
var employees = new EmployeeService().GetAll();
return (
from employee in employees
join message in messages
on employee.EmployeeId equals message.EmployeeId
select new Dado
{
MessageSentId = message.MessageSentId,
//EmployeeId = message.EmployeeId,
//MessageSentSeq = message.MessageSentSeq,
Name = employee.Name,
Surname = employee.Surname,
Mobile = employee.Mobile,
Email = employee.Email,
Status = "N"
}
).ToList();
}
Now, in your code after populating the grid can do something like:
foreach (var dado in Send) {
dado.Status = "S";
}

Left join multiple IList<> using Linq

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

How to split string before Binding in repeater

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.

linq query join with multiple tables filtering not working

I have a form with four text boxes and two comboboxes ...
i am filtering the data and displaying the data in datagrid view depends upon the selection in combobox and text typed in textboxes ..
for that i have written the below code
private void btnRunreports_Click(object sender, EventArgs e)
{
int agefrom = Convert.ToInt32(cbGEFrom.Text);
int ageto = Convert.ToInt32(cbGETo.Text);
DateTime today = DateTime.Today;
DateTime max = today.AddYears(-(agefrom + 1));
DateTime min = today.AddYears(-(ageto));
string maximum = Convert.ToString(max);
string minimum = Convert.ToString(min);
string gender = "";
gender = Classes.reportmembers.ConvertGender(cbGEGendertype.Text);
var mems = Classes.reportmembers
.getallreportmembers(gender,
cbGEMembershiptype.SelectedText,
txtlastname.Text,
txtpostcode.Text,
txtcardnum.Text,
txtreference.Text,
cbGEStatustype.SelectedText,
maximum, minimum);
BindingSource bs = new BindingSource();
bs.DataSource = mems;
dgvReportMembers.DataSource = bs;
}
and this is my class reportmembers:
class ReportMebers
{
public int MemberID { get; set; }
public string Lastname { get; set; }
public string Firstname { get; set; }
public string Postcode { get; set; }
public string Reference { get; set; }
public string CardNum { get; set; }
public string IsBiometric { get; set; }
public string DOB { get; set; }
public string MShipType { get; set; }
public string StatusType { get; set; }
public string EndDate { get; set; }
}
class reportmembers
{
public static List<ReportMebers> getallreportmembers(string gender, string membershiptype, string lastname,
string postcode,string cardnum,string refernce,
string membershipstatustypesa, string maxage, string minage)
{
//CultureInfo provider = CultureInfo.InvariantCulture;
EclipseEntities eclipse = new EclipseEntities();
List<ReportMebers> reporall = new List<ReportMebers>();
var memberreport = from report in eclipse.members
join memtomship in eclipse.membertomships on report.member_Id equals memtomship.member_Id
join mshoption in eclipse.mshipoptions on memtomship.mshipOption_Id equals mshoption.mshipOption_Id
join membershiptypes in eclipse.mshiptypes on mshoption.mshipType_Id equals membershiptypes.mshipType_Id
join membershipstatustypes in eclipse.mshipstatustypes on memtomship.mshipStatusType_Id equals membershipstatustypes.mshipStatusType_Id
where report.member_Lastname.Equals(lastname)
&& report.member_CardNum.Equals(cardnum)
&& report.member_Postcode.Equals(postcode)
&& report.member_Reference.Equals(refernce)
&& report.member_Gender.Equals(gender)
&& membershiptypes.mshipType_Name.Equals(membershiptype)
&& membershipstatustypes.mshipStatusType_Name.Equals(membershipstatustypesa)
&& string.Compare(report.member_Dob,maxage) >= 0
&& string.Compare(report.member_Dob, minage)< 0
select new
{
report.member_Id,
report.member_Lastname,
report.member_Firstname,
report.member_Postcode,
report.member_Reference,
report.member_CardNum,
report.member_IsBiometric,
report.member_Dob,
membershiptypes.mshipType_Name,
membershipstatustypes.mshipStatusType_Name,
memtomship.memberToMship_EndDate
};
try
{
foreach (var membe in memberreport)
{
ReportMebers allmembersrepor = new ReportMebers();
allmembersrepor.MemberID = membe.member_Id;
allmembersrepor.Lastname = membe.member_Lastname;
allmembersrepor.Firstname = membe.member_Firstname;
allmembersrepor.Postcode = membe.member_Postcode;
allmembersrepor.Reference = membe.member_Reference;
allmembersrepor.CardNum = membe.member_CardNum;
allmembersrepor.IsBiometric = membe.member_IsBiometric;
allmembersrepor.DOB = membe.member_Dob;
allmembersrepor.MShipType = membe.mshipType_Name;
allmembersrepor.StatusType = membe.mshipStatusType_Name;
allmembersrepor.EndDate = membe.memberToMship_EndDate;
reporall.Add(allmembersrepor);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return reporall;
}
if i type robin in txtlastname the details will be displayed whoose last name is robin...
i have checked in database there is person with last name robin..
but it does not displayed in datagrid view...
would any guys pls help on this...
Many thanks In advance....
Your problem is, that you are doing an AND comparison over all fields. That means, only entries from the database are returned, that match ALL entered data! If you only enter robin as last name and nothing else, you will get no results, because all the other fields aren't matching. Change your query to include only those fields that are not empty. Something like this:
var query = from report in eclipse.members
join memtomship in eclipse.membertomships on report.member_Id equals memtomship.member_Id
join mshoption in eclipse.mshipoptions on memtomship.mshipOption_Id equals mshoption.mshipOption_Id
join membershiptypes in eclipse.mshiptypes on mshoption.mshipType_Id equals membershiptypes.mshipType_Id
join membershipstatustypes in eclipse.mshipstatustypes on memtomship.mshipStatusType_Id equals membershipstatustypes.mshipStatusType_Id;
if(!string.IsNullOrEmpty(lastname))
query = query.Where(r => r.member_Lastname == lastname);
if(!string.IsNullOrEmptry(cardnum)
query = query.Where(r => r.member_CardNum == cardnum);
// and so on for all parameters

How do I match two identical database tables with LINQ?

I want to match 2 identical tables:
sourceProducts (productName, ProductionDate, ManID, shipper, distributer)
CommProducts (productName, ProductionDate, ManID, shipper, distributer)
but the number of rows and the record contents may differ. How do I select a certain record = raw from one table and get its clone record from the other table (e.g., check if the same record exists)? How do I do this using LinQ?
UPDATE: Here's the LINQ code:
protected void checkBtn_Click(object sender, EventArgs e)
{
MyProductsDataContext mySdb = new MyProductsDataContext();
Product mypro = new Product { ManId = int.Parse(TxtManI.Text), ProductName = TxtProN.Text, ProductionDate =DateTime .Parse ( TxtProDat.Text), Shipper = TxtShipI.Text, Distributer = TxtDistI.Text };
var spro = (from p in mySdb.Products
select new { p.ManId, p.ProductName, p.ProductionDate, p.Shipper, p.Distributer }).
Intersect(from s in mySdb.SourceProducts select new { s.ManId, s.ProductName, s.ProductionDate, s.Shipper, s.Distributer });
if (spro != null)
{
LblMessage.Text = "Acceptable product Data Inserted Sucessfully";
InsertData();
}
else
{
LblMessage.Text = "Invalid Product or bad Entry Please retype";
}
}
I would join on ManId and then compare the rest of the values in a where clause:
bool productExists = (
from p in mySdb.Products
join s in mySdb.SourceProducts
on p.ManId equals s.ManId
where p.ProductName == s.ProductName
&& p.ProductionDate == s.ProductionDate
&& p.Shipper == s.Shipper
&& p.Distributer = s.Distributer
select new { p.ManId, p.ProductName, p.ProductionDate, p.Shipper, p.Distributer }
).Any();
if (productExists)
{
LblMessage.Text = "Acceptable product Data Inserted Sucessfully";
InsertData();
}
else
{
LblMessage.Text = "Invalid Product or bad Entry Please retype";
}
I've used Any() to produce an efficient EXISTS SQL query. You could use SingleOrDefault() or FirstOrDefault() instead if you actually need to use the product returned.
I also don't see anywhere that you're using your new Product's ID - you might need to add that filter to the query as well:
Product mypro = new Product { ... };
bool productExists = (
from p in mySdb.Products
where p.ManId equals mypro.ManId
join s in mySdb.SourceProducts
on p.ManId equals s.ManId
...
You can probably do this using a join but I've hobbled together a unit test which shows one way to this
public class TestProduct
{
public int ManId { get; set; }
public string ProductName { get; set; }
public DateTime ProductionDate { get; set; }
public string Shipper { get; set; }
public string Distributor { get; set; }
}
[TestMethod]
public void TestSourceTable()
{
// Set up a test list
var list = new List<TestProduct>();
for (int i=0;i<5;i++)
{
var p = new TestProduct
{
Distributor = "D" + i,
ManId = i,
ProductionDate = DateTime.Now,
ProductName = "P" + i,
Shipper = "S" + i
};
list.Add(p);
}
// Get an existing product
var existingProduct = list[4];
// Get an unknown product
var unknownProduct = new TestProduct()
{
ManId = -1,
Distributor = "",
ProductionDate = DateTime.Now.AddDays(-1),
ProductName = "",
Shipper = ""
};
// product found
Assert.True(list.Any(p => p == existingProduct));
// product not found
Assert.False(list.Any(p => p == unknownProduct));
}

Categories

Resources