Passing more than 10 parameters between classes in C#? - c#

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
}

Related

How would I use this Factory class and whats the point?

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.

Can you declare a List of a class within another class?

I am trying to create a simple order sheet that has products, customers, order lines, and an order sheet. Currently, I am just hardcoding in products and customers for simple testing purposes. With the order sheet, I want to have a List of the order line items (contains quantity and price from products, and a few other bits of information) in the order class. When I create the order in the program, it's not creating the List of order line items that I have in the constructor. I have tried to .Add within the constructor which didn't work as well in the program, in the program it states it does not contain the definition Add.
When I try to access .test it shows as NULL.
namespace ObjectsCSharpe.Library
class Order
{
public Order()
{
var testLine = new List<OrderLineItems>();
}
public List<OrderLineItems> testLine { get; set; }
}
class OrderLineItems
{
public OrderLineItems()
{
this.orderID = 0;
this.lineNumber = 0;
this.product = new Product();
this.quantity = 0;
this.test = "OLI";
this.lineTotal = 0.00;
}
public int orderID { get; set; }
public int lineNumber { get; set; }
public Product product { get; set; }
public int quantity { get; set; }
public string test { get; set; }
public double lineTotal { get; set; }
}
class Program
{
static void Main(string[] args)
{
Order orderSheet = new Order();
OrderLineItems temp456 = new OrderLineItems();
orderSheet.testLine.Add(temp456);
string abc = orderSheet.testLine[0].test;
Console.WriteLine(abc);
}
}
I'll start with this excerpt:
public Order()
{
var testLine = new List<OrderLineItems>();
}
This is the constructor for the Order type. In this code, the var keyword means you are declaring a new variable, where the scope of the variable is limited to that method. The type also contains this:
public List<OrderLineItems> testLine { get; set; }
So there is a separate testLine variable in the type. But, in the constructor, the use of var means this other variable was not touched.
Later on we have this code:
orderSheet.testLine.Add(temp456);
Unfortunately, because of the earlier mistake, orderSheet.testLine is still null, and you can't call a method on a null reference.
You can fix this as easily as removing var from the constructor:
public Order()
{
testLine = new List<OrderLineItems>();
}
or, even better, remove the entire constructor from the type completely. Initialize the list property where it is declared and make it get-only:
public class Order
{
public List<OrderLineItems> testLine {get;} = new List<OrderLineItems>();
}
(Note: You can still add items to get-only List property.)
I think you need to make public keyword while creating all your class.
public class Order{
//Add Your class declaration...
}

Parsing XML into classes using LINQ in C#

I'm having a lot of trouble parsing an XML document into my custom classes. I've tried to read what I can find on the web and on here, but I'm still not getting anywhere. I'm working on a real estate app, and am trying to model a basic property where you have:
1 property
1 property can have multiple buildings
Each building can have multiple tenants.
I decided to try to store the data in an xml document, and I made an example as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Property>
<Name>Grove Center</Name>
<Building>
<Name>Building1</Name>
<Tenant>
<Name>Tenant1</Name>
<SquareFeet>2300</SquareFeet>
<Rent>34000</Rent>
</Tenant>
<Tenant>
<Name>Tenant2</Name>
<SquareFeet>3100</SquareFeet>
<Rent>42000</Rent>
</Tenant>
<Tenant>
<Name>Tenant3</Name>
<SquareFeet>1700</SquareFeet>
<Rent>29000</Rent>
</Tenant>
</Building>
<Building>
<Name>Building2</Name>
<Tenant>
<Name>Tenant1</Name>
<SquareFeet>6150</SquareFeet>
<Rent>80000</Rent>
</Tenant>
<Tenant>
<Name>Tenant2</Name>
<SquareFeet>4763</SquareFeet>
<Rent>60000</Rent>
</Tenant>
</Building>
</Property>
Actually my first question is if this format is even correct.. I saw some xml examples where they added an extra tag such as <buildings> before they started listing out the individual <Building> tags for each building. Is that necessary? The W3C examples I saw didn't do it that way.. but this post on stackexchange was pretty close to what im doing: Parsing XML with Linq with multiple descendants
Here is the code for my classes in C#:
public class Property
{
public string Name { get; set; }
public List<Building> Buildings = new List<Building>();
}
public class Building
{
public string Name { get; set; }
public List<Tenant> Tenants = new List<Tenant>();
}
public class Tenant
{
public string Name { get; set; }
public int SF { get; set; }
public decimal Rent { get; set; }
}
I'm not sure if using the new keyword on my lists right in the class definition is good practice.. but I was getting errors trying to add a building or tenant to the list later on in my program so I didn't know what else to do. Right now I'm not much further in my main code than:
Property p = new Property();
XDocument doc = XDocument.Load(#"C:\Users\SampleUser\Desktop\sample-property.xml");
Any help is appreciated, thanks
Following query will give you the correct result:-
Property p = new Property
{
Name = (string)doc.Root.Element("Name"),
Buildings = doc.Root.Elements("Building")
.Select(x => new Building
{
Name = (string)x.Element("Name"),
Tenants = x.Elements("Tenant")
.Select(t => new Tenant
{
Name = (string)t.Element("Name"),
SF = (int)t.Element("SquareFeet"),
Rent = (decimal)t.Element("Rent")
}).ToList()
}).ToList()
};
Theres a few things you might want to change.
The property names must match the xml tags, or you have to specify the mapping manually. In your example code, Buildings and Tenants are declared as fields, you should change it to properties. If you want, you can then initialize them to empty list in the constructors:
public class Property
{
public string Name { get; set; }
[XmlElement("Building")]
public List<Building> Buildings { get; set; }
public Property()
{
Buildings = new List<Building>();
}
}
public class Building
{
public string Name { get; set; }
[XmlElement("Tenant")]
public List<Tenant> Tenants { get; set; }
public Building()
{
Tenants = new List<Tenant>();
}
}
public class Tenant
{
public string Name { get; set; }
[XmlAttribute("SquareFeet")]
public int SF { get; set; }
public decimal Rent { get; set; }
}
Further, I would recommend deserializing the file rather than using linq. Consider these helper methods:
public static class XmlHelper
{
public static T DeserializeFromXmlString<T>(string xml)
{
var xmlSerializer = new XmlSerializer(typeof (T));
using (var stringReader = new StringReader(xml))
{
return (T) xmlSerializer.Deserialize(stringReader);
}
}
public static T DeserializeFromXmlFile<T>(string filename) where T : new()
{
return DeserializeFromXmlString<T>(File.ReadAllText(filename));
}
}
Deserialization is then easy:
var listOfProperties = XmlHelper.DeserializeFromXmlFile<Property>(#"C:\Users\SampleUser\Desktop\sample-property.xml");
Intializing your public fields with empty lists is perfectly fine and good practice to avoid the errors you got. If you do not initialize them, they are null, hence the errors.
You could use properties instead of fields for your lists however.
Starting with C# 6 you can use simplified auto-property assignment:
public List<Building> Buildings {get;set;} = new List<Building>();
For C# < 6 you can use auto properties and initialize the property within the constructor or use a property with backing field.
//Auto property with assignment in constructor
public class Property
{
public string Name { get; set; }
public List<Building> Buildings {get;set;};
public Property(){
Buildings = new List<Building>();
}
}
//Property with backing field
public class Property
{
private List<Building> _buildings = new List<Building>();
public string Name { get; set; }
public List<Building> Buildings {get {return _buildings;} set {_buildings = value;}};
}
For reading XML and creating the object graph, you can use LINQ in conjuction with object initializers.
Func<IEnumerable<XElement>, IEnumerable<Tenant>> getTenants = elements => {
return elements.Select (e => new Tenant {
Name = e.Element("Name").Value,
Rent = decimal.Parse(e.Element("Rent").Value),
SF = int.Parse(e.Element("SquareFeet").Value)
});
};
Func<IEnumerable<XElement>, IEnumerable<Building>> getBuildings = elements => {
return elements.Select (e => new Building{
Name = e.Element("Name").Value,
Tenants = getTenants(e.Elements("Tenant")).ToList()
});
};
//xdoc is your parsed XML document
//e.g. var xdoc = XDdocument.Parse("xml contents here");
var property = new Property{
Name = xdoc.Root.Element("Name").Value,
Buildings = getBuildings(xdoc.Root.Elements("Building")).ToList()
};

Add temporary property to list of objects

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.

How to pass values to ViewModel using LINQ?

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

Categories

Resources