I have 3 models
public class Payroll
{
public int ID { get; set; }
public DateTime Date { get; set; }
public int PayCategoryID { get; set; }
public virtual PayCategory PayCategory { get; set; }
}
this one:
public class PayCategory
{
public int ID { get; set; }
public string PayScenario { get; set; }
public int PayGroupID { get; set; }
public virtual PayGroup PayGroup { get; set; }
}
and this one:
public class PayGroup
{
public int ID { get; set; }
public string Label { get; set; }
public string Description { get; set; }
public string EntryType { get; set; }
}
If I create a List of Payroll I will only get a list of Payroll objects with 4 properties each.
I want a list of , where each Payroll object will have the fields
Date, PayScenario, Label, Description, and EntryType. I know these can be easily obtained by
Payroll.PayCategory.PayScenario
Payroll.PayCategory.PayGroup.Label
etc.
But I am exporting these to an excel document using this generic method:
public static void Export(List<T> data, string name, Controller controller)
{
XLWorkbook workbook = new XLWorkbook();
var worksheet = workbook.Worksheets.Add(name);
worksheet.Cell(1, 1).InsertTable(data);
controller.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheet.sheet";
controller.Response.AddHeader("content-disposition", String.Format(#"attachment;filename={0}.xlsx", name.Replace(" ", "_")));
using (MemoryStream memoryStream = new MemoryStream())
{
workbook.SaveAs(memoryStream);
memoryStream.WriteTo(controller.Response.OutputStream);
memoryStream.Close();
}
controller.Response.End();
}
I call from controller like this:
public ActionResult ExportData()
{
var payrolls = (List<Payroll>)Session["payrolls"];
ExportToExel<Payroll>.Export(payrolls, "Payroll", this);
return RedirectToAction("Index");
}
And the results of course give me a simple table with only the value Date in it, (plus some other trash columns like System [...] PayCategory)
Ultimately, I want the properties of these 3 models to be merged (excluding ID's) to give me a single list that I can pass on to the ExportData method. As in, is there a way to add a column to a list of objects? Something similar to
for(int i = 1; i<payrolls.Count; i++)
payrolls[i].Add.(payrolls[i].PayCategory.PayScenario);
which of course, doesn't work.
Please don't ask why I don't simply have one model instead of these 3. It's not an option unless I want to hard code values.
Thanks
If you can import Linq, you can do something like:
var payrolls = from aPayroll in (List<Payroll>)Session["payrolls"]
select new {
Date = aPayroll.Date,
PayScenario = aPayroll.PayCategory.PayScenario,
Label = aPayroll.PayGroup.Label,
Description = aPayroll.PayGroup.Description,
EntryType = aPayroll.PayGroup.EntryType
};
That would create an anonymous type with the five properties you want.
Assuming you're using ClosedXML, you should also be able to create a DataTable and send it directly to a worksheet.
What you are looking for is an "Anonymous Object".
Example usage:
var anonymousObject = new
{
ID = payrollObj.Id,
// other payroll properties
PayScenario = payrollObj.PayCategory.PayScenario
// other extended properties
};
Here's an article about them
http://www.c-sharpcorner.com/UploadFile/ff2f08/anonymous-types-in-C-Sharp/
The suggestion here is to create a new anonymous object within your loop and pass that onto your excel function instead of the original payrollObject.
Related
This is my first ever post to SO and am very new to C# with most of my IT experience in databases. I am starting to look at some of our code and would like to understand how I would use this class and it methods for resusability purposes.
public class FileCreator
{
public string Territory { get; set; }
public string CV { get; set; }
public string AdDate { get; set; }
public string Category { get; set; }
public string Advertiser { get; set; }
public string Brand { get; set; }
public decimal SumOfSpend { get; set; }
public decimal SumOfVolume { get; set; }
public string Spots { get; set; }
public string PageNumber { get; set; }
internal static List<FileCreator> Create(DataSet data)
{
var result = new List<FileCreator>();
if (data.Tables.Count > 0)
{
result = Create(data.Tables[0]);
}
return result;
}
public static List<FileCreator> Create(DataTable dataTable)
{
var result = new List<FileCreator>();
foreach (DataRow row in dataTable.Rows)
{
result.Add(Create(row));
}
return result;
}
private static FileCreator Create(DataRow row)
{
var fileCreator = new FileCreator();
fileCreator.Territory = (row["Territory"].ToString());
fileCreator.CV = row["CV"].ToString();
fileCreator.AdDate = row["Ad_date"].ToString();
fileCreator.Category = row["Category"].ToString();
fileCreator.Advertiser = row["Advertiser"].ToString();
fileCreator.Brand = row["Brand"].ToString();
fileCreator.SumOfSpend = Convert.ToDecimal(row["SumOfSpend"].ToString());
fileCreator.SumOfVolume = Convert.ToDecimal(row["SumOfVolume"].ToString());
fileCreator.Spots = row["Spots"].ToString();
fileCreator.PageNumber = row["Page Number"].ToString();
return fileCreator;
}
}
Why not just create a new instance of a datatable i.e. var dt = new Datatable()?
I must be missing the point of this approach. How will I benefit from this approach when I normally just create multiple datatables?
Go easy. It's my first post 🙂
Thank you
The point of this is get a strongly typed class instead of relying on hard-coded strings to get the data out and then casting everything to the type you are expecting. Instead, its taking data from the Datatable and transforming it to a "Dto" (Data Transfer Object) where you know all the properties (columns) that exist and their types because they are explicitly declared. There is no guessing! :)
While the given approach here works, I hope it is more for legacy sake. It is more efficient to simply create this Dto class directly instead of creating a Datatable and then mapping it.
i have 2 model classes
public class ProductOptionRequest
{
public string Name { set; get; }
public List<ProductValuesRequest> productValues { get; set; }
}
public class ProductValuesRequest
{
public string ValueName { get; set; }
}
public class ProductOptionValue
{
public int OptionId { get; set; }
public String ValueName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
and wrote one bs method and passing parameter value as value names. but I'm unable to get those values in a list object as productValues. May I know the solution, please.
public async Task<ReturnString> SaveProductOption(ProductOptionRequest request)
{
request.productValues = new List<ProductValuesRequest>();
foreach (ProductValuesRequest valueRequest in request.productValues)
{
ProductOptionValue res = new ProductOptionValue();
res.ValueName = valueRequest.ValueName;
object response = await productOptionValueRepository.InsertAsync(res, true);
}
}
In the first line of your method, you are replacing the productValues property of request object with a new empty list, :
request.productValues = new List<ProductValuesRequest>();
Therefore, in foreach loop, you are iterating on an empty list.
Remove the first line and see if you still have any issues.
You are assigning an emplty list to productValues as
request.productValues = new List() and trying to iterate the empty list.
I have a list created from a stored procedure using EF6.0
I have also created 3 classes
public class Resas
{
public string todo{ get; set; }
public string prop { get; set; }
public string Code { get; set; }
public string statusCode { get; set; }
public string checkin { get; set; }
public string checkout { get; set; }
public List<profiles> profiles { get; set; }
}
public class profiles
{
public string action { get; set; }
public string id { get; set; }
public string profileType { get; set; }
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public List<emailAddresses> emailAdresses { get; set; }
}
public class emailAddresses
{
public string emailAddress { get; set; }
public string emailAddress2 { get; set; }
}
I am doing a for-loop in the list and I need to get certain columns and put it in the array (I will put two, to keep it simple)
myEntities db = new myEntities();
List<rev_Result> revList = new List<rev_Result>();
revList.Clear();
revList = db.rev().ToList();
for (int i = 0; i < revList.Count(); i++)
{
Resas resas = new Resas();
profiles[] profiles = new profiles[1];
resas.todo = revList[i].todo;
resas.profiles[0].lastName = revList[i].lastName;
}
I am not familiar with C# as you can see from the psedo-code above.
I cannot figure out how to feed the Resas with data and then its Profile with data and then move to the next Resas entry.
Any help appreciated.
That's fairly simple using Linq:
Resas resas = new Resas();
resas.profiles = revList
.Select(x => new profiles() { action = x.todo, lastName = x.lastName })
.ToList();
What's happening here is: You loop through every entry in revList and get your wanted data structure (that's what Select is doing). x refers to the current entry in the loop, while the stuff to the right side of the arrow is you 'output': a new instance of your profiles class with the members assigned accordingly. The result of all of this is then converted to a list (before ToList(), think of it as a recipe to create the list) and assigned to resas.profiles.
By the way, a word on conventions: Usually, in C#, you would give your classes a name that starts with a capital letter. Also, your profiles class seems to contain data of exactly one profile, so a better name might be Profile. This also makes your data structure more clear, since List<profiles> seems to be a list of lists of profiles - but that's not what it actually is, is it?
Furthermore, Members generally start with a capital letter as well, so instead of action, lastName, you'd have: Action and LastName.
You can try with Linq. This is the code that should solve your issue, but Resas class doesn't have action property:
List<Resas> ls = revList.Select(x => new Resas() {
action = x.todo,
profiles = new List<profiles>() {
new profiles { lastName = x.lastName }
}
).ToList();
If you need to use action property of inprofiles` class:
List<Resas> ls = revList.Select(x => new Resas() {
profiles = new List<profiles>() {
new profiles {
action = x.todo,
lastName = x.lastName
}
}
).ToList();
I need to add data to view model using LINQ
My view model is :
public class SearchScrapViewModel
{
public WClass wClass{get; set;}
public SClass sClass{get; set;}
public YClass yClass { get; set; }
}
public class WClass
{
public string title { get; set; }
public string link { get; set; }
}
public class SClass
{
public string title { get; set; }
public string link { get; set; }
}
public class YClass
{
public string title { get; set; }
public string link { get; set; }
}
and i need to use these 3 classes with 3 different LINQ query and then pass data to return View(SearchScrapViewModel);
var wikians = //LINQ Logic
select new SearchScrapViewModel
{
wClass.link = link.Attributes["href"].Value, //Error: I am not able to add to wClass
wClass.title = link.InnerText
};
and similarly to other classes
and then pass to return View(SearchScrapViewModel); so that i can access all the 3 classes in View of this controller
How to do that?
You forgot to create an instance of your WClass:
select new SearchScrapViewModel {
wClass = new WClass {
link = link.Attributes["href"].Value,
title = link.InnerText
}
};
Alternatively, you could make WClass (and SClass and YClass) a struct instead of a class, then you don't need to instantiate it. In that case, however, you should probably make the struct immutable.
LINQ is not the be-all-end-all, and I dont know that this is the best approach for what you are looking for. I would suggest looking at the Builder Pattern, to accomplish this. If you really want to, you could do this in one LINQ query (using object initializers), but it might not read as clean as a builder would (but that is my two cents):
select new SearchScrapViewModel
{
wClass = new wClass{title = xyz, link = xyz},
sClass = new sClass...
yClass = new yClass...
}
It is not clear to me why you need a select statement in your example. In any case you can't return SearchScrapViewModel as your return because that is a type and not the instance. Unless your code is simplified for this post and you do need linq, I would suggest:
var wikians =
new SearchScrapViewModel {
wClass = new WClass {
link = link.Attributes["href"].Value,
title = link.InnerText
}
};
return View(wikians);
I am a new to C#, I need a small help on how can I pass multiple parameters between the classes?
Below is a small example but my parameters will more than the 10. Is there another way to this?
public StreamStructure(String name, string id, string classname, int number)
{
this.name = name;
this.id = id;
this.classname = classname;
this.number = number;
}
List ------
List<abc> don = new List<abc>();
foreach (XmlElement abc_cdb in abc_cdbs)
{
abc.Name = abc_cdb.GetAttribute("NAME");
abc.Id = abc_cdb.GetAttribute("id");
abc.Clssname = abc_cdb.GetAttribute("classname");
abc.number = Convert.ToInt32(abc_cdb.GetAttribute("number"));
don.Add(abc);
}
I have used as suggested in ans but I am trying to create a list in C# my first record gets replaced with the 2nd one, since the fields in MyDTO are defined as public. Do you have any idea how to fix this?
Sure, use DTO's (data transfer objects). That is, create a class that has all the fields you want to send and use an instance of it as a parameter. Added bonus is that your method signature won't change even if you change your DTO class.
You are probably better off using C# Initializers or a Data Transfer Object than a large number of constructor parameters. Or combine the two.
public class MyDTO
{
String Name { get; set; }
String Id { get; set; }
String ClassName { get; set; }
int Number { get; set; }
}
var MyDTO = new MyDTO()
{
Name = Name,
Id = Id,
ClassName = ClassName,
Number = Number
}
var stream = new StreamStructure(MyDTO)
To create a list of these objects as in your example, create a new DTO within the loop body.
var don = new List<MyDTO>();
foreach (XmlElement abc_cdb in abc_cdbs)
{
var abc = new MyDTO()
{
Name = abc_cdb.GetAttribute("NAME");
Id = abc_cdb.GetAttribute("id");
ClassName = abc_cdb.GetAttribute("classname");
Number = Convert.ToInt32(abc_cdb.GetAttribute("number"));
};
don.Add( abc );
}
You could pass a domain object that represents the item you are manipulating.
public class Widget
{
public string Name {get;set;}
public int Id {get;set;}
public string ClassName {get;set;}
public int Number {get;set;}
}
var myWidget = new Widget();
myWidget.Name = "Blue Widget";
//etc
StreamStructure(myWidget);
You should write a new class that contains the properties you want to pass to the method, and change your method to include just that new class.
For your example, write a new class like this:
public class RequestObject
{
public string Name { get; set; }
public string ID { get; set; }
public string ClassName { get; set; }
public int Number { get; set; }
}
Then change your method like this:
public StreamStructure(RequestObject requestObject)
{
//DoStuff
}