Generic list of generic lists - c#

I've been playing with various ways of using generics and have hit a road block.
Consider the following classes:
public abstract class DataElement
{
public int Id { get; set; }
}
public class School : DataElement
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Course : DataElement
{
public int Id { get; set; }
public int SchoolId { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Student : DataElement
{
public int Id { get; set; }
public int CourseId { get; set; }
public string Name { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Considering a hypothetical scenario where none of this data changes, I'm trying to create a DataDictionary class to house those objects in their respective Lists all within one top-level List property. This crazy idea came to me, when I was writing code to load the different data types from JSON files. I was able to write one load method that could read all three types of data using generics, and that sent me down this particular rabbit hole.
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id);
}
public class DataDictionary : IDataDictionary
{
public List<IList> Data = new List<IList>();
// Return all objects from the list of type T
public List<T> GetAllItemsFromList<T>()
{
return Data.OfType<T>().ToList(); // This works, returning the appropriate list.
}
// Return specific object from the list of type T by Id property value
public T GetSingleItemFromList<T>(int id)
{
List<T> list = Data.OfType<List<T>>().First(); // This works, resolving to the correct list (e.g. Courses).
return list.Where(i => i.Id == id).First(); // This doesn't work. It doesn't appear to know about the Id property in this context.
}
}
GetAllItemsFromList works fine, returning the appropriate list of items
List<School> newList = GetAllItemsFromList<School>();
However, I am unable to figure out how to return a single item by Id from its respective list.
School newSchool = GetSingleItemFromList<School>(1);
It could be that I'm just trying to be too clever with this, but I can't help but think there is a way to do this, and I'm just missing it.

This doesn't work. It doesn't appear to know about the Id property in this context.
Yes, because you have not specified any constraints to the type parameter T, so it can be anything. There are multiple options:
Create an interface IHaveId and constraint T to it (or use DataElement):
public interface IHaveId
{
int Id { get; set; }
}
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id) where T : IHaveId; // or where T : DataElement
}
public class DataDictionary : IDataDictionary
{
public T GetSingleItemFromList<T>(int id) where T : IHaveId // or where T : DataElement
{
List<T> list = Data.OfType<List<T>>().First();
return list.Where(i => i.Id == id)
.First();
}
}
add additional parameter to select the id:
public T GetSingleItemFromList<T>(int id, Func<T, int> idSelector)
{
List<T> list = Data.OfType<List<T>>().First();
return list.First(i => idSelector(i) == id);
}
use reflection - I would argue the least recommended option

The problem is that T parameter in function GetSingleItemFromList<T>(int id) could be anything. In order of this to work you should add a constraint:
GetSingleItemFromList<T>(int id) where T: DataElement

Related

How to I return a List of DTOs from my API controller

I have a class :
public class Participant
{
[Key]
public int ParticipantId { get; set; }
[Column(TypeName ="nvarchar(50)")]
public string Email { get; set; }
[Column(TypeName = "nvarchar(50)")]
public string Name { get; set; }
public int Score { get; set; }
public int Timetaken { get; set; }
}
My endpoint:
public IActionResult GetAll()
{
var parts = _context.Participants.ToList();
if(!parts.Any())
return NotFound();
var participant = new ParticipantDto(parts);
return Ok(participant);
}
My Participant Dto:
public class ParticipantDto
{
private readonly Participant _participants;
public ParticipantDto(Participant participants)
{
_participants = participants;
}
}
I am trying the approach of passing the Participant object in the constructor and then assigning the Participant properties to DTO. I am aware how to do it for one Participant :
public string EmailAddress = _participants.Email;
etc
However, what If I want to return a List, how do I need to update my Dto to handle that?
For this statement,
var participant = new ParticipantDto(parts);
it is incorrect as you are passing the List<Participant> instance to the ParticipantDto constructor which the constructor is expected for the parameter value with the Participant type. You will get the compiler error for the unmatching type.
Hence, you need to iterate the list to transform each Participant element to the ParticipantDto type. You can use .Select() from System.Linq to do the iteration and transformation.
public IActionResult GetAll()
{
var parts = _context.Participants.ToList();
if(!parts.Any())
return NotFound();
List<ParticipantDto> participants = parts
.Select(x => new ParticipantDto(participant))
.Tolist();
return Ok(participants);
}
While in ParticipantDto, you need to perform the value mapping between the properties.
public class ParticipantDto
{
public ParticipantDto(Participant participant)
{
Email = participant.Email;
Name = participant.Name;
// Following assigning value from the properties of Participant to properties of ParticipantDto
}
public string Email { get; set; }
public string Name { get; set; }
// Following properties
}
Bonus:
You may look for AutoMapper which is a popular library for mapping between classes. (Wouldn't cover too much as the answer aims to focus and fix your current problem)

Check if T implements an interface and return T after populating interface properties

I have a generic method that takes a list of T as a parameter. I am trying to get certain data based on the type of interface and populate only those properties of each item in the list, that are defined in the interface.
So my base interface is:
public interface IStatusResult
{
int Status { get; set; }
string StatusName { get; set; }
}
I have a number of other interfaces that implement this interface, which i want to use to figure out what Status/Status Names key/value pairs to retrieve:
public interface IROQStatusResult: IStatusResult { }
public interface IOrderStatusResult: IStatusResult { }
public interface IActivityStatusResult: IStatusResult { }
Finally, i implement this interface in my search result class:
public class RecommendedOrderQuantitySearchResult:IROQStatusResult {
public int Id { get; set; }
public DateTime TargetDate { get; set; }
public int Status { get; set; }
public string StatusName { get; set; }
}
So once a get a List from the Db i want to set the status names in the Generic method:
public static List<T> PopulateStatusNames<T>(List<T> items) where T:class
{
Dictionary<int, string> statuses = new Dictionary<int, string>();
if (typeof(IROQStatusResult).IsAssignableFrom(typeof(T))) {
statuses = GlobalConstants.GetROQStatuses();
}
if (typeof(IOrderStatusResult).IsAssignableFrom(typeof(T))){
statuses = GlobalConstants.GetOrderStatuses();
}
foreach (var item in items)
{
item.StatusName = statuses[item.Status];
}
return items;
}
Currently, item.StatusName is giving me an error: Item does not have property StatusName
How can i implement this?
Use OfType, it will return only those that can be safely cast into the type:
foreach (var item in items.OfType<IStatusResult>())
{
item.StatusName = statuses[item.Status];
}

Null list after query

I have a problem where I create an object containing a list, load it into my database, run a query that returns the object, but find the list null. All other properties of the object are as they should be. I'm using a list called "Ints" that is filled with a few integers but I've tried using other types.
Here's my model:
public class CourseModel
{
public int CourseModelId { get; set; }
public virtual ICollection<int> Ints { get; set; } // the variable in question
public string Name { get; set; }
public string Overview { get; set; }
}
And here's my database population (The database is called LearnYou):
public class LearnYouDbContextInitializer : DropCreateDatabaseAlways<LearnYouDbContext>
{
protected override void Seed(LearnYouDbContext context)
{
context.Courses.Add(new CourseModel()
{
Name = "C# Programming",
Overview = "You'll learn some C#",
Ints = new List<int> { 1, 42, 3 },
});
context.SaveChanges();
}
}
Here's the controller code for querying the object:
// GET: Course/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
CourseModel courseModel = db.Courses.Find(id);
// DEBUGGING THE PREVIOUS LINE SHOWS INTS IS NULL
if (courseModel == null)
{
return HttpNotFound();
}
return View(courseModel);
}
The "Ints" property is not null after saving the context in the database population part but is always null when it's queried (I visit the page ~Edit/1 to debug). I just can't figure out why when all other properties are fine. Any ideas? Thanks.
An ICollection in a model indicates a Parent->Child relationship. However, I doubt EF will be able to determine how to create a child table for an ICollection of integers. Here is what I would do.
Create a new model Ints (or whatever it actually represents):
public class Ints {
public int Value { get; set;}
}
Modify your original model to use it:
public class CourseModel
{
public int CourseModelId { get; set; }
public virtual ICollection<Ints> Ints { get; set; } // See the difference?
public string Name { get; set; }
public string Overview { get; set; }
}
That should make it work.
It Is not working because you are mapping directly to a int primitive type of .net and Entity Framework doesn't allow it.
In this case what you can do is create your onw object for example and sql table like
public class Ints {
{
public Course Course { get; set; }
public int IntValue { ger; set ; }
}
And referencing it from CourseModel
public virtual List<Ints> Ints { get; set; }

How can I pass different types of list<T> into a method for grouping by similar fields

I would like to create a generic method that will accept lists of different types with similar fields that I can group by.
For instance, I have two lists:
- List<MemberPurchases> Purchases
- List<MemberReturns> Returns
I want a method that will accept both of the different types of lists and then group them by a similar field: (Purchases.MemberID or Returns.MemberID).
Here is what I have, but it won't let me do any linq queries on the list when passed in:
Public static int GetMemberActivity<T>(List<T> memberList)
{
var groupedList = memberList.GroupBy(x => x.
}
At that point I only get four options (which none of them are the elements in the list):
Equals
GetHashCode
GetType
ToString
What I'm looking for is:
var groupedList = memberList.GroupBy(x => x.MemberID);
How can I fix this so I can pass in any list that has the MemberID element and group by it via LINQ.
How about defining an interface, with the similar property in it, which your classes can then implement:
public interface IMember
{
int MemberID { get; set; }
}
public class MemberPurchases : IMember
{
public int MemberID { get; set; }
public string SomeProperty { get; set; }
}
public class MemberReturns : IMember
{
public int MemberID { get; set; }
public string AnotherProperty { get; set; }
}
Then use that interface in your method:
public static int GetMemberActivity(List<IMember> memberList)
{
var groupedList = memberList.GroupBy(x => x.MemberID);
return groupedList.Count(); // I'm just guessing what you want to return here
}
Cast your list to the interface, then pass it to the method:
var result = GetMemberActivity(Purchases.ToList<IMember>());
You should have a common interface, and accept a strongly typed List of that common interface.
public interface IMember
{
int MemberID { get; set; }
}
public class MemberPurchases : IMember
{
public int MemberID { get; set; }
//...
}
public class MemberReturn : IMember
{
public int MemberID { get; set; }
//...
}
So you can have:
public static int GetMemberActivity(List<IMember> memberList)
{
var groupedList = memberList.GroupBy(x => x.MemberID);
}
(1) Several possible approaches, flexible, but type-safety not enforced:
Extract value of MemberID using reflection.
Check the type of passed object (e.g. using is or as statement), cast the generic object to correct type, then access the property directly.
Use GroupBy method in ObjectQuery that has string parameter for grouping keys. Of course you need to change your method parameter type from List to another type.
(2) Recommended type-safe way --> define a new interface to get MemberID value, then apply it to MemberPurchases and MemberReturns. As those classes are generated by EF as partial, you can create a new partial class file with same name, then implement the interface. (partial class means that a class can have multiple source-file to describe a single same class, so you can extend the class directly). Here's an code example in newly created .cs file:
public interface IMember
{
object MemberGenericID{get;}
}
public partial class MemberPurchases : IMember
{
object IMember.MemberGenericID
{
get { return MemberID; }
}
}
public partial class MemberReturns : IMember
{
object IMember.MemberGenericID
{
get { return MemberID; }
}
}
Then you can do this:
void Group(List<IMember> list)
{
list.GroupBy(x => x.MemberGenericID)
}

Returning a Sorted List of Objects based on an Abstract Class

I have an abstract class that defines basic behavior for socially used objects throughout the site (Social Collaboration Objects).
internal abstract class SCO
{
public double HotScore { get; set; }
public double UpVotes { get; set; }
public double DownVotes { get; set; }
public double VoteTotal { get; set; }
public DateTime Created { get; set; }
public SCO(DBItem item, List<Vote> votes )
{
var voteMeta = InformationForThisVote(votes, item.ID);
UpVotes = voteMeta.UpVotes;
DownVotes = voteMeta.DownVotes;
VoteTotal = UpVotes - DownVotes;
HotScore = Calculation.HotScore(Convert.ToInt32(UpVotes), Convert.ToInt32(DownVotes), Convert.ToDateTime(item["Created"]));
Created = Convert.ToDateTime(item["Created"]);
}
private static VoteMeta InformationForThisVote(List<Vote> votes, int itemId)
{
// Loop through votes, find matches by id,
// and record number of upvotes and downvotes
}
private User GetCreatorFromItemValue(DBItem item)
{
// Cast User Object from property of DataBase information
}
}
Here is a sample of inherited object:
class Post : SCO
{
public string Summary { get; set; }
public Uri Link { get; set; }
public Uri ImageUrl { get; set; }
public Post(DBItem item, List<Vote> votes)
: base(item, votes)
{
Summary = (string) item["Summary"];
Link = new UriBuilder((string) item["Link"]).Uri;
ImageUrl = new UriBuilder((string) item["ImageUrl"]).Uri;
}
}
Something else these classes all have in common is that the majority of the time they will be returned as a Sorted Collection. The difficulty here is you cannot embed a collection into an abstract class, because there is no way to instantiate the abstract class itself.
What I have working so far is having a Sort method as part of the abstract shown here:
protected static List<ESCO> SortedItems(List<ESCO> escoList, ListSortType sortType)
{
switch (sortType)
{
case ListSortType.Hot:
escoList.Sort(delegate(ESCO p1, ESCO p2) { return p2.HotScore.CompareTo(p1.HotScore); });
return escoList;
case ListSortType.Top:
escoList.Sort(delegate(ESCO p1, ESCO p2) { return p2.VoteTotal.CompareTo(p1.VoteTotal); });
return escoList;
case ListSortType.Recent:
escoList.Sort(delegate(ESCO p1, ESCO p2) { return p2.Created.CompareTo(p1.Created); });
return escoList;
default:
throw new ArgumentOutOfRangeException("sortType");
}
}
Which allows me to have this in my Inherited Children classes:
public static List<Post> Posts(SPListItemCollection items, ListSortType sortType, List<Vote> votes)
{
var returnlist = new List<ESCO>();
for (int i = 0; i < items.Count; i++) { returnlist.Add(new Post(items[i], votes)); }
return SortedItems(returnlist, sortType).Cast<Post>().ToList();
}
This works, but it feels a little clunky. I'm still repeating a lot of code in my Sub-Classes, and I feel like that cast is an unnecessary performance deduction.
How do I best provide a way to return a sorted list of Objects based on an abstract class which are sorted in the same way, with the same properties?
It looks like there's no need for the abstract class, since there are no abstract members. Wouldn't it be best just to use a concrete base class (adding virtual members if necessary)?
Better yet, use an interface that contains all the members needed for sorting (score, votes, etc); pass collections of the interface to your sort method.
Edit Here's a simplified example:
internal interface ISCO
{
double HotScore { get; set; }
}
class SCO : ISCO
{
public double HotScore { get; set; }
public static IEnumerable<T> Sort<T>(IEnumerable<T> items) where T : ISCO
{
var sorted = items.ToList();
sorted.Sort();
return sorted;
}
}
Then you can use that single sort method for every derived class:
List<Post> PostItems = // ...
IEnumerable<Post> sorted = SCO.Sort<Post>(PostItems);

Categories

Resources