I'm trying to get anonymous object from query:
var myList = from td in MyObjectList
select new
{
a = td.a,
b = td.b,
c = td.c,
name = (from r in contex.NewList
where r.aa == td.a && r.bb == td.b
select r.Name).ToList()
};
I would like name to have r.Name value cause I expect that name list contains only one element. If it contains 0 elements I would like name to have value NONE if more then 1 element then exception should be thrown or something.
Is it even possible to achieve something like that? Thanks for help.
Instead of .ToList() use
.SingleOrDefault() ?? (td.a == 0 ? "XNone" : "None")
Edit: Changed anwer based on comment.
Also I would recomend not to put such logic into Linq-to-SQL. Sometimes this can result in big chunk of highly-unoptimized SQL code and, unless you dont mind some performance isues, can result in much slower SQL execution.
You can achieve that using SingleOrDefault and a temporary variable within the expression. Something like this:
var myList =
from td in MyObjectList
let nameValue = contex.NewList
.Where(r => r.aa== td.a && r.bb == td.b)
.Select(r => r.Name)
.SingleOrDefault()
select new
{
a = td.a,
b = td.b,
c = td.c,
name = nameValue ?? "NONE"
};
Update: instead of presenting almost the same solution as #Euphorics answer, I've restructured the code a bit. I often find nested LINQ expressions making things less readable. Converting comprehension syntax into call chains could improve that.
Update 2: with some added requirements, the following select should do the trick:
select new
{
a = td.a,
b = td.b,
c = td.c,
name = nameValue ?? (td.a == 0 ? "XNone" : "None")
};
Theoretically you CAN'T.
Unless you have a type from where you could inspect lambda properties.
The trick is to convert your anonymous object to json and deserialize it into a known type that you must have in advance.
Caution working with EF core because your linq query will be executed CLIENT side!!.
That means that all the records will be retrieved from your dbset and evaluate on client.
Do NOT use code like this on EF dbset iquerables.
Any away I will copy some extensions methods code to do it so.
Having a defined type like...
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
Extension convert any object to json string...
public static class ObjectExtensions
{
public static string ToJson(this object source)
{
return JsonConvert.SerializeObject(source, Formatting.None);
}
}
Extension convert any json string to typed object...
public static class StringExtensions
{
public static T FromJson<T>(this string source) where T : class
{
return JsonConvert.DeserializeObject<T>(source);
}
}
Some xUnit Test
[Fact]
public void AddSelectTest()
{
var data = new[]
{
new {Id = 01, Name = "Engineering", GroupName = "Engineering and Development"},
new {Id = 02, Name = "Tool Design", GroupName = "Tool Design and Research"},
new {Id = 03, Name = "Sales", GroupName = "Sales and Marketing"},
new {Id = 04, Name = "Marketing", GroupName = "Marketing and Sales"},
new {Id = 05, Name = "Purchasing", GroupName = "Inventory Management"},
new {Id = 06, Name = "Research and Development", GroupName = "Development and Research"},
new {Id = 07, Name = "Production", GroupName = "Manufacturing and Production"},
new {Id = 08, Name = "Production Control", GroupName = "Control and Production"},
new {Id = 09, Name = "Human Resources", GroupName = "Human Resources and Administration"},
new {Id = 10, Name = "Finance", GroupName = "Finance and Executive General"},
new {Id = 11, Name = "Information Services", GroupName = "Information Services and Administration"},
new {Id = 12, Name = "Document Control", GroupName = "Document Control and Quality Assurance"},
new {Id = 13, Name = "Quality Assurance", GroupName = "Administration and Quality Assurance"},
new {Id = 14, Name = "Facilities and Maintenance", GroupName = "Maintenance and Facilities"},
new {Id = 15, Name = "Shipping and Receiving", GroupName = "Receiving and Shipping"},
new {Id = 16, Name = "Executive", GroupName = "Executive General and Administration"}
};
var queryable = data.AsQueryable();
var first = queryable.Select(d => new { Id = d.Id, Name = d.Name }).FirstOrDefault(d => d.ToJson().FromJson<Department>().Id == 1);
Assert.True(first != null, "Expected a department value but 'null' was found.");
}
Once again... let me say that if you are querying anonymous in memory object it could be ok, but be very cautious if your iquerable cames from EF core, since client side evaluation will happen.
Please enable throw exception warning when client side evaluation happens on your EF core code thought your DbContextOptionsBuilder, to prevent EF Core of executing client side evaluation code. You could do it follows..
builder.UseSqlServer(connection, sql =>
{
sql.EnableRetryOnFailure();
sql.MigrationsAssembly(assembly);
})
.UseQueryTrackingBehavior(track ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking)
.ConfigureWarnings(w => w.Throw(RelationalEventId.QueryClientEvaluationWarning));
Related
In my project I have a GenericImageViewModel which is used by many entities.
Example of getting the ASP User Entity:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = a.UserProfile.ProfileImage
});
The ProfileImage is the GenericImageViewModel and has an implicit operator as follows:
public static implicit operator TRDGenericImageViewModel(TRDImage image)
{
return new TRDGenericImageViewModel
{
Id = image.Id,
AspectRatio = image.Ratio,
Url = image.Url,
};
}
If I run the query Entity Framework throws an exception:
"Unable to cast the type 'TRDImage' to type 'TRDGenericImageViewModel'. LINQ to Entities only supports casting EDM primitive or enumeration types."
If I create the GenericImageViewModel manually for each ViewModel everything is running fine:
var query = UserRepository.Get(Id).Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel {
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
});
But in this case I have to copy and paste the TRDGenericImageViewModel generation in every ViewModel and thats not the way it should goes. If something changed I have to modify all related classes.
So is there a way to avoid this exception?
Using .AsEnumerable() prior lambda is not possible due to selects later in that query.
var query = UserRepository
.Get(Id)
.AsEnumerable()
.Select(a => new TRDIdenityViewModel
{
FirstName = a.UserProfile.FirstName,
LastName = a.UserProfile.LastName,
NickName = a.UserProfile.NickName,
ProfileImage = new TRDGenericImageViewModel
{
Id = a.UserProfile.ProfileImage.Id,
AspectRatio = a.UserProfile.ProfileImage.Ratio,
Url = a.UserProfile.ProfileImage.Url,
},
Statistics = new TRDUserStatisticsViewModel
{
PostCount = a.Posts.Count(),
CommentCount = a.Comments.Count(),
ImageCount = a.Images.Count(),
VideoCount = a.Videos.Count(),
VoteCount = a.PostVotes.Count(),
}
});
When calling .AsEnumerable() only the entities included by the Include statement are counted. But if the user has more than 1000 Posts and 10.000 Votes the query is a data nightmare.
I want to make a menu, and this is my code. I need recursive function.
var newTopMenu = new TopMenuVm
{
TopMenus = (from amp in allMasterPages
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = (from submenu in amp.Page1
orderby amp.DisplayOrder
select new TopMenuVm
{
DisplayOrder = submenu.DisplayOrder,
PageTitle = submenu.PageTitle,
PageId = submenu.PageId,
Url = string.IsNullOrEmpty(submenu.Url) ? GenerateUrl(submenu) : submenu.Url,
}).ToList()//Here is go on infinite...,
}).ToList()
};
How can i do that with linq, please help...
Is something like this what you are looking for?
// allMasterPages is type List<MyPage> - change to whatever it really is
Func<List<MyPage>, List<TopMenuVM>> funcTopMenuVM = null;
funcTopMenuVM = ((pages) => {
return
(from amp in pages
orderby amp.DisplayOrder
select new TopMenuVM()
{
DisplayOrder = amp.DisplayOrder,
Url = string.IsNullOrEmpty(amp.Url) ? GenerateUrl(amp.Page1.OrderBy(x => x.DisplayOrder).FirstOrDefault()) : amp.Url,
PageId = amp.PageId,
PageTitle = amp.PageTitle,
TopMenus = funcTopMenuVM(amp.Page1)
}).ToList();
});
var newTopMenu = new TopMenuVM()
{
TopMenus = funcTopMenuVM(allMasterPages)
};
Of course, it doesn't have to be a Func<>, it could just be a regular method that returns a List<TopMenuVM> and takes a List<MyPage> (or whatever allMasterPages is).
I wrote the following linq query that creates a new object by joining data together as you can see:
var translations = from t in context.Translations
join token in context.Tokens on t.Guid equals token.Guid
join t2 in context.Translations on new { t.Guid, LanguageCode = "fr" } equals new { t2.Guid, t2.LanguageCode} into j //TODO: fr needs to be replaced by the language of the translators account
from j2 in j.DefaultIfEmpty()
where t.LanguageCode == String.Empty
orderby t.Text
select new TranslationView
{
Guid = t.Guid,
LanguageCode = j2.LanguageCode,
SourceText = t.Text,
Translation = j2.Text,
IsNew = j2.Text == null,
Notes = token.Notes,
Required = token.Required,
Type = (Token.TokenType)token.Type,
Location = (Token.LocationType)token.Location
};
The problem is that I am now trying to write a unit test with Rhino.Mocks and it returns the error Object reference not set to an instance of an object.
So my question now is, is there a better way that this query could be written? A way in which it will work in both a real situation and the Unit test situation?
I tried passing a value in in the DefaultIfEmpty() bit and it made it work for the Mock, but then the main code failed.
EDIT
Unit test code:
[Test]
public void Build_Translation_List_TwoItems_Context()
{
//Arrange: Setup context
var context = setupContext();
//Act: Pass the context through
var result = TranslationHelpers.BuildTranslationList(context, 1);
//Result
result.TranslationList.Count.ShouldEqual(2);
result.PagingInfo.TotalItems.ShouldEqual(2);
}
SetupContext Method:
public static ITranslationContext setupContext()
{
var context = new Mock<ITranslationContext>();
context.SetupProperty(x => x.Tokens, new UnitTestHelpers.FakeDbSet<Token>
{
new Token
{
DateAdded = DateTime.Now,
Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"),
LastUpdated = DateTime.Now,
Location = (short)0,
Type = (short)0,
LocationDescription = "Test 1",
Notes = "Testing 1",
Required = "Testing"
},
new Token
{
DateAdded = DateTime.Now,
Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B65"),
LastUpdated = DateTime.Now,
Location = (short)0,
Type = (short)0,
LocationDescription = "Test 3",
Notes = "Testing 3",
Required = "Testing"
},
});
context.SetupProperty(x => x.Translations, new UnitTestHelpers.FakeDbSet<Translation>
{
new Translation{Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"), LanguageCode = String.Empty, Text = "Testing 1"},
new Translation{Guid = Guid.Parse("f3099a43-e12d-4ea3-ba06-265fde807f03"), LanguageCode = "fr", Text = ""},
new Translation{Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B65"), LanguageCode = String.Empty, Text = "Testing 3"},
new Translation{Guid = Guid.Parse("7D6937D8-F7E1-4B92-934E-465683874B67"), LanguageCode = "fr", Text = "Testing 4"}
});
return context.Object;
}
Any help would be much appreciated.
DefaultIfEmpty() will not create a "default" j2. It will just get data even if j2 is null.
It's just like an SQL LEFT JOIN
So you have to test for nullity to avoid NRE.
instead of
LanguageCode = j2.LanguageCode
try to do
LanguageCode =j2 != null ? j2.LanguageCode : string.Empty // or null
Pass mocked context to your method, and LINQ will do it's job. Then verify returned list of TranslationView objects.
Here is how your test should look like:
List<Translations> translations = // create translations
List<Tokens> tokens = // create tokens
var context = MockRepository.GenerateStub<IMyContext>();
context.Stub(c => c.Translations).Return(translations);
context.Stub(c => c.Tokens).Return(tokens);
var foo = new Foo(context);
var views = foo.GetTranslationView();
// verify returned views
I am having trouble with this I have 3 Data tables i use over and over again which are cached I would like to write a LINQ statement which would do the following is this possible?
T-SQL VERSION:
SELECT P.[CID],P.[AID]
,B.[AID], B.[Data], B.[Status], B.[Language]
FROM MY_TABLE_1 P
JOIN
(
SELECT A.[AID], A.[Data], A.[Status], A.[Language] FROM MY_TABLE_2 A
UNION ALL
SELECT B.[AID], B.[Data], B.[Status], B.[Language] FROM MY_TABLE_3 B
) B on P.[AID] = B.[AID]
WHERE B.[Language] = 'EN' OR B.[Language] = 'ANY' AND B.STATUS = 1 AND B.[Language] = 'EN' OR B.[Language] = 'ANY' AND B.STATUS = 1
Then i would like it to create a result set of the following
Results:
|CID|AID|DATA|STATUS|LANGUAGE
Try this:
from p in Context.MyTable1
join b in Contact.MyTable2.Concat(Contact.MyTable3)
on p.aid equals b.aid
where b.Language == "EN" || b.Language == "ANY"
where b.Status == 1
select new
{
p.CID,
p.AID,
b.Data,
b.Status,
b.Language
};
Don't do it this way.
Your two options
Create a view which represents your Union statement (Table2 and Table3)
Create a relationship on the DBML between Table1 and the new view on the AID column.
Do a SelectMany to get your required return result.
or (and preferred)
Create a stored procedure that accepts the language / status (assuming they are parameters) and returns this data set. This will be the most efficient method.
You are doing database work in your business logic! Use the database for what it was intended.
Make sure you reference System.Data.DataSetExtensions, and use the AsEnumerable() method to use LINQ to DataSets.
var myTable1 = new [] {
new { CID = "123", AID = 345, Data = 32323, Status = 1, Language = "EN"},
new { CID = "231", AID = 123, Data = 11525, Status = 2, Language = "EN"},
new { CID = "729", AID = 513, Data = 15121, Status = 1, Language = "ANY"},
new { CID = "231", AID = 123, Data = 54421, Status = 2, Language = "EN"}}
.ToDataTable().AsEnumerable();
var myTable2 = new [] {
new { CID = "512", AID = 513, Data = 32323, Status = 1, Language = "ANY"},
new { CID = "444", AID = 123, Data = 11525, Status = 2, Language = "BLAH"},
new { CID = "222", AID = 333, Data = 15121, Status = 1, Language = "ANY"},
new { CID = "111", AID = 345, Data = 54421, Status = 2, Language = "EN"}}
.ToDataTable().AsEnumerable();
var myTable3 = new [] {
new { CID = "888", AID = 123, Data = 32323, Status = 2, Language = "EN"},
new { CID = "494", AID = 333, Data = 11525, Status = 1, Language = "FR"},
new { CID = "202", AID = 513, Data = 15121, Status = 1, Language = "EN"},
new { CID = "101", AID = 345, Data = 54421, Status = 2, Language = "ANY"}}
.ToDataTable().AsEnumerable();
var q = from p in myTable1
join b in myTable2.Union(myTable3) on p.Field<int>("AID") equals b.Field<int>("AID")
where (b.Field<string>("Language") == "EN" || b.Field<string>("Language") == "ANY") && b.Field<int>("Status") == 1
select new
{
CID = p.Field<string>("CID"),
B_AID = p.Field<int>("AID"),
P_AID = b.Field<int>("AID"),
Data = b.Field<int>("Data"),
Status = b.Field<int>("Status"),
Language = b.Field<string>("Language")
};
var table = q.ToDataTable();
I used an extension method you can find here to test this, it's pretty useful if you are doing a lot of LINQ over DataTables.
Is it possible to flatten a one-to-many relationship using dynamic LINQ?
For example, I might have a list of Users and the User class contains a list of many UserPreferences. The UserPreference class is essentially a name/value pair.
A user will define what types of user preferences are available for a group of users.
public class User
{
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public IList<UserPreference> UserPreferences
{
get;
set;
}
}
public class UserPreference
{
public UserPreference(string name, object userValue)
{
this.Name = name;
this.UserValue = userValue;
}
public string Name
{
get;
set;
}
public object UserValue
{
get;
set;
}
}
Therefore one user group might be defined in the following way:
List<User> users = new List<User>();
User user1 = new User();
user1.FirstName = "John";
user1.LastName = "Doe";
user1.UserPreferences.Add(new UserPreference("Favorite color", "Red"));
User user2 = new User();
user2.FirstName = "Jane";
user2.LastName = "Doe";
user2.UserPreferences.Add(new UserPreference("Favorite mammal", "Dolphin"));
user2.UserPreferences.Add(new UserPreference("Favorite color", "Blue"));
users.Add(user1);
users.Add(user2);
return users;
The desired output would be:
First Name Last Name Favorite Color Favorite Mammal
John Doe Red NULL
Jane Doe Blue Dolphin
Is there a way to create an anonymous type so that UserPreferences would get rolled up into the User?
For example,
var u = UserScopedSettingAttribute.Select("new (FirstName as FirstName, UserValue as FavoriteColor)", null);
string name = u.FirstName;
string color = u.FavoriteColor;
Ultimately this list of Users will get bound to an ASP.NET GridView web control. There will be a large volume of data involved in this operation and performance will be critical.
Any suggestions are appreciated!
I know it doesn't exactly answer your question, but compiling strings into new classes at runtime like dlinq does has always had kind of a bad smell to it. Consider just simply using a DataTable like this,
DataTable prefs = new DataTable();
IEnumerable<DataColumn> cols = (from u in users
from p in u.UserPreferences
select p.Name)
.Distinct()
.Select(n => new DataColumn(n));
prefs.Columns.Add("FirstName");
prefs.Columns.Add("LastName");
prefs.Columns.AddRange(cols.ToArray());
foreach (User user in users)
{
DataRow row = prefs.NewRow();
row["FirstName"] = user.FirstName;
row["LastName"] = user.LastName;
foreach (UserPreference pref in user.UserPreferences)
{
row[pref.Name] = pref.UserValue;
}
prefs.Rows.Add(row);
}
This should do it. Flattening is generally done with SelectMany extension method, but in this case I am using a let expression. The code to remove the null preferences is a bit ugly and could prob be improved but it works:
var flattenedUsers = from user in GetUsers()
let favColor = user.UserPreferences.FirstOrDefault(pref => pref.Name == "Favorite color")
let favMammal = user.UserPreferences.FirstOrDefault(pref => pref.Name == "Favorite mammal")
select new
{
user.FirstName,
user.LastName,
FavoriteColor = favColor == null ? "" : favColor.UserValue,
FavoriteMammal = favMammal == null ? "" : favMammal.UserValue,
};
My best suggestion would be to not use dynamic LINQ, but add a flatuser class and then loop through the users. The code for this is simple, and if you were able to get a linq query with similar results it would generate the same code, although you can't really tell how optimized it would be as it might involve some joins that would incur a performance penalty instead of just looping. If you were pulling this from a database using LINQ to SQL then you could use an entity relation to to get the data using linq instead of this loop.
Loop:
List<FlatUser> flatusers = new List<FlatUser>();
foreach (User u in users)
{
foreach (UserPreference up in u.UserPreferences)
{
flatusers.Add(new FlatUser
{
FirstName = u.FirstName,
LastName = u.LastName,
Name = up.Name,
UserValue = up.UserValue
});
}
}
Flat User Class:
public class FlatUser
{
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string Name
{
get;
set;
}
public object UserValue
{
get;
set;
}
}
Unfortunately
var u = UserScopedSettingAttribute.Select("new {FirstName as FirstName, UserValue as FavoriteColor}", null);
string name = u.FirstName;
string color = u.FavoriteColor;
won't work. When you use DLINQ Select(string) the strongest compile time class information you have is Object, so u.FirstName will throw a compile error. The only way to pull the properties of the runtime generated anonymous class is to use reflection. Although, if you can wait, this will be possible with C# 4.0 like this,
dynamic u = UserScopedSettingAttribute.Select("new {FirstName as FirstName, UserValue as FavoriteColor}", null);
string name = u.FirstName;
string color = u.FavoriteColor;
I think the pragmatic answer here is to say your attempting to force C# to become a dynamic language and any solution is going to be really pushing C# to its limits. Sounds like your trying to transform a database query of columns that are only determined at query time into a collection that is based on those columns and determined at run time.
Linq and Gridview binding is really pretty and succinct and all but you have to start thinking about weighing the benefit of getting this compiler bending solution to work just so you don't have to dynamically generate gridview rows and columns by yourself.
Also if your concerned about performance I'd consider generating the raw HTML. Relying on the collection based WebForms controls to efficiently display large sets of data can get dicey.
You add in a couple of OnItemDataBound events and boxing and unboxing is going to really gum up the works. I'm assuming too your going to want to add interactive buttons and textboxes to the rows as well and doing 1000 FindControls has never been fast.
There are probably more efficient ways to do this, but to actually answer your question, I came up with the following code. (Note that I've never worked with DynamicLinq before, so there may be a better way to use it to accomplish your goal.)
I created a console application, pasted in the classes from your post, then used the following code.
static void Main(string[] args)
{
var users = GetUserGroup();
var rows = users.SelectMany(x => x.UserPreferences.Select(y => new { x.FirstName, x.LastName, y.Name, y.UserValue }));
var userProperties = rows.Select(x => x.Name).Distinct();
foreach (var property in userProperties)
{
Console.WriteLine(property);
}
Console.WriteLine();
// The hard-coded variety.
var results = users.Select(x => new
{
x.FirstName,
x.LastName,
FavoriteColor = x.UserPreferences.Where(y => y.Name == "Favorite color").Select(y => y.UserValue).FirstOrDefault(),
FavoriteAnimal = x.UserPreferences.Where(y => y.Name == "Favorite mammal").Select(y => y.UserValue).FirstOrDefault(),
});
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The dynamic variety.
DynamicProperty[] dynamicProperties = new DynamicProperty[2 + userProperties.Count()];
dynamicProperties[0] = new DynamicProperty("FirstName", typeof(string));
dynamicProperties[1] = new DynamicProperty("LastName", typeof(string));
int propIndex = 2;
foreach (var property in userProperties)
{
dynamicProperties[propIndex++] = new DynamicProperty(property, typeof(string));
}
Type resultType = ClassFactory.Instance.GetDynamicClass(dynamicProperties);
ConstructorInfo constructor = resultType.GetConstructor(new Type[] {});
object[] constructorParams = new object[] { };
PropertyInfo[] propInfoList = resultType.GetProperties();
PropertyInfo[] constantProps = propInfoList.Where(x => x.Name == "FirstName" || x.Name == "LastName").OrderBy(x => x.Name).ToArray();
IEnumerable<PropertyInfo> dynamicProps = propInfoList.Where(x => !constantProps.Contains(x));
// The actual dynamic results creation.
var dynamicResults = users.Select(user =>
{
object resultObject = constructor.Invoke(constructorParams);
constantProps[0].SetValue(resultObject, user.FirstName, null);
constantProps[1].SetValue(resultObject, user.LastName, null);
foreach (PropertyInfo propInfo in dynamicProps)
{
var val = user.UserPreferences.FirstOrDefault(x => x.Name == propInfo.Name);
if (val != null)
{
propInfo.SetValue(resultObject, val.UserValue, null);
}
}
return resultObject;
});
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Display the results.
var displayResults = dynamicResults;
//var displayResults = results;
if (displayResults.FirstOrDefault() != null)
{
PropertyInfo[] properties = displayResults.First().GetType().GetProperties();
int columnWidth = Console.WindowWidth / properties.Length;
int index = 0;
foreach (PropertyInfo property in properties)
{
Console.SetCursorPosition(index++ * columnWidth, Console.CursorTop);
Console.Write(property.Name);
}
Console.WriteLine();
foreach (var result in displayResults)
{
index = 0;
foreach (PropertyInfo property in properties)
{
Console.SetCursorPosition(index++ * columnWidth, Console.CursorTop);
Console.Write(property.GetValue(result, null) ?? "(null)");
}
Console.WriteLine();
}
}
Console.WriteLine("\r\nPress any key to continue...");
Console.ReadKey();
}
static List<User> GetUserGroup()
{
List<User> users = new List<User>();
User user1 = new User();
user1.FirstName = "John";
user1.LastName = "Doe";
user1.UserPreferences = new List<UserPreference>();
user1.UserPreferences.Add(new UserPreference("Favorite color", "Red"));
user1.UserPreferences.Add(new UserPreference("Birthday", "Friday"));
User user2 = new User();
user2.FirstName = "Jane";
user2.LastName = "Doe";
user2.UserPreferences = new List<UserPreference>();
user2.UserPreferences.Add(new UserPreference("Favorite mammal", "Dolphin"));
user2.UserPreferences.Add(new UserPreference("Favorite color", "Blue"));
users.Add(user1);
users.Add(user2);
return users;
}