I am using generic method to fill my dropdown for all types
below is my code.
the entity type are as follow
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
public class DropDown
{
public string Id { get; set; }
public string Name { get; set; }
}
i am able to fetch data successfully at
var data = DataFetcher.FetchData<T>();
private static void Main( string[] args )
{
List<DropDown> cities = BLL.GetDataList<City>();
List<DropDown> states = BLL.GetDataList<State>();
List<DropDown> roles = BLL.GetDataList<Role>();
}
public static class BLL
{
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data as List<DropDown>;
}
}
I knew this cast data as List<DropDown> will fail,thats why its returning null back to calling method,
How can i cast Generic list to List of Known Type?
You have to ask yourself: how do I want to convert T to DropDown? If you can't answer this, the answer is: you can't.
I guess your DropDown class has an object Value property, that holds the dropdown value, and you wish to assign the data entity to that property.
Then you can project the list of data entities to DropDowns as such:
var data = DataFetcher.FetchData<T>();
return data.Select(d => new DropDown { Value = d }).ToList();
As for your edit: so you have at least one type, the displayed Role, that has an Id and Name property. But type T doesn't guarantee this, so you'd need to introduce an interface:
public interface INamedIdentifyableEntity
{
string Id { get; set; }
string Name { get; set; }
}
And apply this to your entities. Then introduce it as a generic constraint and do the mapping:
return data.Select(d => new DropDown
{
Id = d.Id,
Name = d.Name,
}).ToList();
But you don't want this, as here you are tying these two properties to dropdowns. Tomorrow you'll want an entity with Code instead of Id and Text instead of Name, so you'll have to add more interfaces, more overloads, and so on.
Instead you might want to use reflection, where you can specify the member names in the call:
List<DropDown> cities = BLL.GetDataList<City>(valueMember: c => c.CityCode, displayMember: c => c.FullCityname);
And use these member expressions to look up data's values and fill those into the DropDown.
However, you're then reinventing the wheel. Leave out your DropDown class entirely, and leave the dropdown generation to the front end, in this case MVC:
var cities = DataFetcher.FetchData<City>();
var selectList = new SelectList(cities.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCityId),
Text = c.FullCityName,
Value = c.CityCode,
});
Or:
var selectList = new SelectList(cities, "CityCode" , "FullCityName", selectedCityId);
One solution is to use AutoMapper.
First create a map between your models like this:
AutoMapper.Mapper.CreateMap<Role, DropDown>();
Do the same thing for City and State classes if you need to.
Then you can use AutpMapper to convert your objects to DropDown like this:
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data.Select(x => AutoMapper.Mapper.Map<DropDown>(x)).ToList();
}
If I understood the question correctly, you could use Linq as follows.
return data.Cast<DropDown>().ToList();
Related
I have a list of Order objects. Order has the properties: int Id, decimal Price, string OrderNumber, string ShipperState, DateTime TimeStamp;
I know which columns I want to transform (Price, TimeStamp) and I want to keep the other columns without needing to specify them.
This example is transforming specified columns but I still need to include the non-transformed columns.
var myList = model.Orders.Select(x => new
{
x.Id,
x.OrderNumber,
// decimal to string
Price = x.Price.ToString("C", new CultureInfo("en-US")),
x.ShipperState,
// DateTime to string
TimeStamp = x.TimeStamp.ToString("MM/dd/yyyy H:mm")
}
If I were to add a column string ShipperCity to the Order class, I would like myList to also have that property without having to go back and update the projection.
An ideal answer would not rely on external libraries, reflection and only be a line or two.
If you do not want to modify the model class as #David suggested you can write extension methods for it like this:
public static class OrderExtensions
{
public static string GetFormattedPrice(this Order order)
=> order.Price.ToString("C", new CultureInfo("en-US"));
public static string GetFormattedTimestamp(this Order order)
=> order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
UPDATE #1
The effect of this alternative is that whereever you wanted to use the transformed order.Price and order.Timestamp there you have to use order.GetFormattedPrice() and order.GetFormattedTimestamp() respectively.
In the question it was not specified that where the data come from and what type of application the data is used in.
For example methods cannot be used in XAML binding and everywhere else where a property is required.
Please note:
In C# (almost) everything is strongly typed hence once the class and the properties in it are defined you cannot set one of its property value to a different type of data and also you cannot change the type of the property. So by default you cannot avoid projection when you need some transformation. If you need all the properties - either the original value or the transformed value - you have to list all of them in the projection.
almost everything except dynamic
You can actually transform the type and the value of a property but only if it is defined as dynamic. For example this works below:
public class Order
{
public int Id { get; set; }
public string OrderNumber { get; set; }
// Original: decimal; Converted: string;
public dynamic Price { get; set; }
public string ShipperState { get; set; }
// Original: DateTime; Converted: string;
public dynamic Timestamp { get; set; }
}
public static class OrderExtensions
{
public static void Transform(this Order order)
{
if (order.Price.GetType() == typeof(decimal))
order.Price = order.Price.ToString("C", new CultureInfo("en-US"));
if (order.Timestamp.GetType() == typeof(DateTime))
order.Timestamp = order.Timestamp.ToString("MM/dd/yyyy H:mm");
}
}
class Program
{
static void Main(string[] args)
{
var originalList = new List<Order>()
{
new Order() { Id = 1, OrderNumber = "1", Price = 100m, Timestamp = DateTime.Now },
new Order() { Id = 2, OrderNumber = "2", Price = 200m, Timestamp = DateTime.Now },
new Order() { Id = 3, OrderNumber = "3", Price = 300m, Timestamp = DateTime.Now }
};
originalList.ForEach(order => order.Transform());
}
}
Although this example works there are some things to know:
dynamic type
This example looks like a hack, maybe it can be considered as a hack. :)
In this example the original Order objects are changed not their projection/clone/etc.
dynamic properties are not allowed in Entity Framework models as you cannot specify the SQL column type for them even using the methods of DbModelBuilder. I did not try it in other use-cases but it seems to be a very restricted possibility.
For dynamic properties there is no IntelliSense, so after typing order.Price. no list would appear with any method or property.
You have to use these properties very carefully as there is no compile-time check. Any typo or other mistake will throw an exception only during run-time.
If this option somehow fits the needs it might be worth implementing the conversion of the string value back to the original type.
That's all the update I could add to my original answer. Hope this is an acceptable answer to your comment.
I have following classes:
public class Selections
{
public List<Selection> selection { get; set; }
}
public class Selection
{
public Promotion promotion { get; set; }
public Products products { get; set; }
}
public class Products
{
public List<int> productId { get; set; }
}
I am creating List and assigning property values but when I am adding the list I'm getting error:
The best overloaded method match for
'System.Collections.Generic.List.Add(Selection)' has some
invalid arguments
C# code:
Selections productSelections = new Selections();
List<Selection> listOfProductSelections = new List<Selection>();
Selection dataSelection = new Selection()
{
promotion = new ProviderModels.Common.Promotion()
{
promotionID = Convert.ToInt32(applicablePromotion.PromotionId),
lineOfBusiness = applicablePromotion.LineOfBusiness
},
products = new ProviderModels.Common.Products()
{
productId = GetIdsOfSelectedProducts(context, selectedOffer)
}
};
productSelections.selection.Add(listOfProductSelections);
Am I missing something?
You are adding a list to another list. You want to add the list items.
Instead of
productSelections.selection.Add(listOfProductSelections);
write
productSelections.selection.AddRange(listOfProductSelections);
But you have to be sure you have initialized the selection property at that point, otherwise you'll run into a NullReferenceException.
By the way, check all your error messages. You will see a second message telling you which type is excpected and what you were using.
you should use AddRange as listOfProductSelections is a list.
productSelections.selection.AddRange(listOfProductSelections)
productSelections.selection is a reference to a List, consquently when you try to add an item to it (last line of your example) the Add method expects a parameter of type of Selection - you're passing listOfProductSelections which is a reference to another list.
Maybe you wanted to add dataSelection which is of the required type? If not, you can use AddRange as the other respondents have suggested.
This question already has answers here:
C#: how to return a list of the names of all properties of an object?
(4 answers)
Closed 6 years ago.
I'd like to be able to get the values and count from any model I create.
For example let's say I have a model that looks like this.
public class test
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I want to be able to write code that will look at the model and then get ID, Name, Address and put them into an array. And I don't want the values. But the values from the model. Not the data. As well as getting the count of the values. 3.
EDIT: per your clarifications in the comments
You can use reflection to extract the property names into a list
var foo = new test();
IList<string> properties = foo.GetType().GetProperties()
.Select(p => p.Name).ToList();
OLD ANSWER
Try converting your object to a NameValueCollection (https://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection(v=vs.110).aspx). This collection offers a count, and allows you hash table access to the values. You can also retrieve an IEnumerable for the Values (or Keys) to suit your needs.
var foo = new test();
NameValueCollection formFields = new NameValueCollection();
foo.GetType().GetProperties()
.ToList()
.ForEach(pi => formFields.Add(pi.Name, pi.GetValue(foo, null).ToString()));
NOTE: if .ToString() is too destructive, you can swap the NameValueCollection with the IDictionary implementation of your choice.
Code modified from this question: how to convert an instance of an anonymous type to a NameValueCollection
In the other class simply call test.ID = identification;
identification being the variable that you would like ID to get the value from.
test being your class name.
I believe you are looking for something like this (wrapped in example code).
class Program
{
public class Test
{
public int ID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
static void Main(string[] args)
{
var propertyInfo = typeof(Test).GetProperties();
var propertyCount = propertyInfo.Count();
Console.WriteLine($"Property count is {propertyCount}");
foreach (var info in propertyInfo)
{
Console.WriteLine($"Property Name: {info.Name}");
}
Console.ReadKey();
}
}
Which would give you the output:
Code Output
This allows you to get the property count and property names of a class.
I need to implement this approach - two kinds of data sent as one set of parameters in a single Type - so that one type will hold those two parameters. That way I will be able to pass that Type to be processed by some method.
the first data item is:
Columns to be displayed , named: displayed
the second data item:
A copy (or only a portion) of that Columns displayed, as it has the same source, only these columns will not be displayed... in other words, Columns to omit, so I've named it: omitted
both are of a type Columns that I named - SelectedColumns
public class SelectedcColoumns
{
public enum renederingMode
{
Displayed,
omitted
}
public class omitted
{
}
public class displayed
{
}
}
As the request for that SetOfColumns to be displayed is done by choosing table-name. So the Column class as data to be displayed varies based on the user choice the available source For SelectedColumns to choose from, is as shown below:
public class tableNames
{
public static readonly string tblCustomers = "tblCustomers";
public static readonly string tblProducts = "tblProducts";
}
public class TblColumns
{
public class tblCustomers
{
public const string custID = "custID",
Name = "Name",
Phone = "Phone";
Email = "Email";
}
public class tblProducts
{
public const string PrudctID = "PrudctID ",
PrudctName = "PrudctID",
PrudctCategory = "PrudctCategory";
}
...etc'
}
When the user selects a set of tables Columns ... from any table user could, in this example.. choose either Customers or Products columns (e.g. SelectedColumns - is tblCustomers Columns), I then need to have another list, of those that the user selected to omit (not to display) from all of available table Columns.
Say the user chose to have Table Customers as a table. He chose to omit tblCustomers.custID + tblCustomer.Email because he only needs the name and phone to be displayed.
The problem I've encountered is while having these parameters in my reach (table name + columns to omit), How could I send it to process (passing it as One Parameter)? So that is why I've created a dedicated class, to hold this Type as sent parameter: all columns + omitted Columns in one piece.
This is where I am currently stuck; I need to know how to use it to build / construct the parameter out of user selection.
public class SelectedColoumns
{
public enum renederingMode
{
Displayed,
omitted
}
public class omitted
{
List<string> omitCols_ListStr = new List<string>();
}
public class displayed
{
List<string> dispCols_ListStr = new List<string>();
}
}
In this part, I retrieve list of Columns through reflection as the supplier of data, via the following method:
Get any Nested Class-Fields, As List<string>, By a nested class-name and it's parent - Type.
public static List<string> anyNestedClassFiledsAsListByType<ClassToReturnOneOfitsNested_Fields>(string NetedClassName)
{
var RetNestedClassFildsListValues = typeof(ClassToReturnOneOFitsNested).GetNestedTypes()
.First(t => String.Compare(t.Name, NetedClassName, true) == 0).GetFields(BindingFlags.Public | BindingFlags.Static)
.Where(f => f.FieldType == typeof(string)).Select(f => (string)f.GetValue(null)).ToList();
return RetNestedClassFildsListValues;
}
so to produce this I could use the method above Like that
var TableColumns_ALL =
anyNestedClassFldsAsListByType<TblColumns>(tableNames.tblCustomers);
My question is related to the class that needs to send TableColumns_ALL + the selected Columns to omit to be then processed by renderSelectedTable() below.
So it's even more basic than the complexity of reflection, but still some how i do not know the popper way to construct, the SelectedColumns class, so it will accommodate and format the structure of this new data type that will be sent as a parameter the method is something like this.
public void renderSelectedTable(SelectedColoumns CurrentUserSelectedCols)
{
StringBuilder NwTRLoopSB = new StringBuilder();
string curRowStyle= string.Empty,
nwLine = Environment.NewLine + "\t\t\t",
BaseTemplateTD = string.Empty;
NwTRLoopSB.Append(
string.Format(
"<table id='tbl_Settings' cellspacing='0' border='1'><tr id='TR_headers'{0}>{1}",
curRowStyle,
nwLine
)._Dhtml_DoubleQoutes()
);
foreach (var Item in SelectedListStr.Select((Val, counter) => new { Value = Val, Index = counter }))
{
curRowStyle = Lsts.DynamicStyle_Generator(Item.Index);
if(Lsts.ExcludeColumns(Item.Value, OmittedCols))
{
BaseTemplateTD = string.Format("<td>{0}</td>{1}", Item.Value, nwLine)._Dhtml_DoubleQoutes();
NwTRLoopSB.Append(BaseTemplateTD);
}
}///ENd TR cells generator Section
NwTRLoopSB.Append("</tr></table>");
return NwTRLoopSB.ToString();
}
I would approach it this way:
public class Column{
public string Name { get; set; }
public bool Visible { get; set; }
}
public class Grid{
public List<Column> Columns { get; set; }
}
So the I could easily define my full table with either visible or ommited columns.
In the OP's example:
public class SelectedColumns
{
//instead of the enum you would have boolean in the column type "Visible" (whether is shown or not)
public enum renederingMode
{
Displayed,
omitted
}
// instead of both these you would have a List o Column types that have a name AND a boolean, so you have your List<string> and a boolean to indicate whether it is visible or ommited. Well at least that's how I understood it.
public class ommited
{
}
public class displayed
{
}
}
so.. first.. a design note: Given a list of columns.. you will either display a column, or not display a column.. there are no other options where visibility is concerned. So you really only need to pass in a single list of EITHER the columns to display, OR the columns to omit - but NOT both.
If you choose to make that modification then you can simply pass in a single List<string>
If you choose to keep your current design then you will need a class with two properties:
public class SelectedColumns {
public List<string> displayed { get; set; }
public List<string> omitted { get; set; }
}
I have a class definition that looks like this:
public class MyObjectModel
{
public int ObjectID { get; set; }
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
}
I have a list of these MyObjectModel and I want to sort them by name with a custom sort process because that involves checking if the data contains a LastName (in this case sort using the LastName) or just FirstLastName (in this case I'm going to break the string and sort on the second term, if there's one, or just the whole string if there's only one word.)
I'm not sure on two things:
should I use IComparer or IComparable?
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID.
Use Linq:
List<MyObjectModel> objects = new List<MyObjectModel>();
List<int> ids = objects.OrderBy(o => FunctionWhichReturnsNameForSort(o)).Select(o => o.ObjectID).ToList();
FunctionWhichReturnsNameForSort can be implemented in another class, or an extension, or as a member.
// should be in a static helper class
public static string NameForSort(this MyObjectModel obj)
{
if (!string.IsNullOrEmpty(obj.LastName)) return obj.LastName;
return obj.FirstLastName.Split(... // your splitting logic goes here
}
var ids = objects.OrderBy(o => o.NameForSort()).Select(o => o.ObjectID).ToList();
When you really need this strange double solution then you will run into this and similar problems more often. As a more general solution, consider putting the business logic for names in a few read-only properties:
//for when the user's data is split in 2 fields
public string FirstName { get; set; }
public string LastName { get; set; }
//for when the user's data is all in one field
public string FirstLastName { get; set; }
public string FullName
{
get { ... } // pick from LastName, FirstName, FirstLastName
}
public string SortName
{
get { ... } // pick from LastName, FirstLastName
}
Once it determines the order of the sort (I can do that), how do I make it so that the output of the method is a list of ints representing ObjectID
result = MyObjectList
.OrderBy(m => m.SortName) // sort on SortName
.Select(m => m.ObjectID) // select the Id
.ToList();
If this sorting is specific to one use case, it can be achieved using LINQ:
var sortedIds = models.OrderBy(SecondName).Select(m => m.ObjectId).ToList();
private static string SecondName(MyObjectModel model)
{
if (!string.IsNullOrWhitespace(model.LastName)) return model.LastName;
return model.FirstLastName.Split(' ').Last();
}
While you can use LINQ, as others have suggested, that would involve creating a brand new list, not mutating the existing list. That may or may not be preferable. If you want to sort the list itself that's easy enough too:
List<string> list = new List<string>(){"a","b","c"};
list.Sort((a,b)=> a.CompareTo(b));
Just take your list, call Sort, and pass in a lambda that takes two items and returns an integer indicating which is greater. In your case, just call some method on a and b to get a string and then use CompareTo or string.Compare on those two strings.