I'm trying to retrieve some entities using Entity Framework by querying an XML column. Entity Framework doesn't support this so I had to use raw SQL.
var people = context.People.SqlQuery("SELECT * FROM [People] WHERE [DataXML].value('Properties/Age', 'int') = 21").AsQueryable().AsNoTracking();
My person class:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Column("YearsSinceBirth")]
public int Age { get; set; }
[Column(TypeName = "xml")]
public string DataXML { get; set; }
}
This should work, however, it falls over when trying to map it back to an object. Specifically, it's falling over on the Age property, which has it's column name overridden to "YearsSinceBirth".
'The data reader is incompatible with the specified
'MyProject.CodeBase.DataModel.DbEntities.Person'. A member of the
type, 'Age', does not have a corresponding column in the data reader
with the same name.'
I'm guessing that Entity Framework doesn't map database column names to object property names and therefore is expecting the column to be named 'Age' rather than 'YearsSinceBirth'.
I don't want to have to list each column and their mapping in the SQL query (like SELECT YearsSinceBirth As Age) as the actual project I'm working on which has this column has a lot more columns and that would mean this query would break every time the schema changed (kinda defeating the purpose of Entity Framework).
If this is EF Core, your problem is not that SqlQuery() doesn't support mapping column names (it does). Rather your problem is that your table doesn't contain a column called YearsSinceBirth, and you are returning 'select *'.
If you have a column called YearsSinceBirth, this works fine. Although you will be retrieving the value in the YearsSinceBirth column, not the value in the XML document. EG
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
//using Microsoft.Samples.EFLogging;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
using System.Data.SqlClient;
namespace EFCore2Test
{
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Column("YearsSinceBirth")]
public int Age { get; set; }
[Column(TypeName = "xml")]
public string DataXML { get; set; }
}
public class Location
{
public string LocationId { get; set; }
}
public class Db : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Location> Locations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(local);Database=EFCoreTest;Trusted_Connection=True;MultipleActiveResultSets=true");
base.OnConfiguring(optionsBuilder);
}
}
class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.EnsureDeleted();
//db.ConfigureLogging(s => Console.WriteLine(s));
db.Database.EnsureCreated();
var p = new Person()
{
Name = "joe",
Age = 2,
DataXML = "<Properties><Age>21</Age></Properties>"
};
db.People.Add(p);
db.SaveChanges();
}
using (var db = new Db())
{
var people = db.People.FromSql("SELECT * FROM [People] WHERE [DataXML].value('(/Properties/Age)[1]', 'int') = 21").AsNoTracking().ToList() ;
Console.WriteLine(people.First().Age);
Console.ReadLine();
}
Console.WriteLine("Hit any key to exit");
Console.ReadKey();
}
}
}
You can use a pattern similar to this to project entity attributes from an XML or JSON column:
public class Person
{
private XDocument xml;
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public int Age
{
get
{
return int.Parse(xml.Element("Properties").Element("Age").Value);
}
set
{
xml.Element("Properties").Element("Age").Value = value.ToString();
}
}
[Column(TypeName = "xml")]
public string DataXML
{
get
{
return xml.ToString();
}
set
{
xml = XDocument.Parse(value);
}
}
}
You can dynamically create select query with aliases, if they needed, with the help of reflection and ColumnAttribute checking:
public string SelectQuery<T>() where T : class
{
var selectQuery = new List<string>();
foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var attr = prop.GetAttribute<ColumnAttribute>();
selectQuery.Add(attr != null ? $"{attr.Name} as {prop.Name}" : prop.Name);
}
return string.Join(", ", selectQuery);
}
Usage:
var people = context.People.SqlQuery($"SELECT {SelectQuery<Person>()} FROM [People] WHERE [DataXML].value('Properties/Age', 'int') = 21")
.AsQueryable().AsNoTracking();
I am using Entity Framework 5.0 and I created my database from model. The below is the screenshot of the edmx diagram.
I am working towards to a below structure of data:
On given Client ID give me list of Theader which belongs to that ClientID and its TReports so I modeled my models as below:
public class TReportHeaderModel
{
public int ID { get; set; }
public int ClientID { get; set; }
public string THeaderTitle { get; set; }
public int RowNumber { get; set; }
public IList<TReportModel> TReports { get; set; }
}
public class TReportModel
{
public int ID { get; set; }
public string TReportName { get; set; }
public string URL { get; set; }
public int RowNumber { get; set; }
}
So when I query to get Theaders and its each report for given clientID:
I am listing the headers first for given clientID:
public IList<TReportHeaderModel> GetHeadersByClient(int ClientID)
{
using (var connection = new TReportEntitiesConnection())
{
var clientHeaders= (from st in connection.THeaders
where ClientID == st.ClientID
select new TReportHeaderModel
{
ID=st.ID,
THeaderTitle=st.THeaderTitle,
RowNumber=st.RowNumber
}).ToList();
return (clientHeaders);
}
}
And then to get the list of reports for each title and this is where I am stuck--->
public IList<TReportModel> GetChildReportsByHeader(int THeaderID)
{
using (var connection = new TReportEntitiesConnection())
{
// ....
}
}
Instead of separating it by get the headers by client first and then get the report by header id, is it possible to combine it in one method? sorry for the confusing explanation but I am new to LINQ Query so please understand.
The below is the ideal structure for the UI implemetation:
Client ID =2
Header 1
TReportName
URL
Header 2
TReportName
URL
is it possible to combine it in one method?
If I understand you correctly, this is what you're looking for:
using (var connection = new TReportEntitiesConnection())
{
var clientHeaders = (
from st in connection.THeaders
where ClientID == st.ClientID
select new TReportHeaderModel
{
ID=st.ID,
THeaderTitle = st.THeaderTitle,
RowNumber = st.RowNumber,
Reports = from r in st.TReports
select new TReportModel
{
ID = r.ID,
TReportName = r.TReportName,
URL = r.URL,
RowNumber = r.RowNumber,
}
}
).ToList();
}
return clientHeaders;
Note that for this to work, TReportHeaderModel.TReports should be IEnumerable<TReportModel>.
Normally I would suggest you separate the methods for getting your data and transforming your data into DTOs like this (And usually I have the connection defined at the class level, not at the method level because I will reuse the connection many times, and I prefer keeping my data accesses as lazy as possible):
TReportEntitiesConnection conn = new TReportEntitiesConnection();
Then I will create extension methods like so:
public static class MyExtensions
{
public IQueryable<THeader> ByClientId(this IQuerable<THeader> conn, int ClientID)
{
return conn
.Include(h=>h.Reports)
.Where(h=>h.ClientID==ClientID);
}
public TReportHeaderModel ToDto(this THeader t)
{
return new TReportHeaderModel
{
ID=t.ID,
ClientID=t.ClientID,
THeaderTitle=t.THeaderTitle,
RowNumber=t.RowNumber,
Reports=t.Reports.ToDto()
};
}
public TReportModel ToDto(this TReport r)
{
return new TReportModel
{
ID=r.ID,
TReportName=r.TReportName,
URL=r.URL,
RowNumber=r.RowNumber
};
}
public IEnumerable<TReportHeaderModel> ToDto(this IEnumerable<THeader> h)
{
return h.Select(x=>x.ToDto());
}
public IEnumerable<TReportModel> ToDto(this IEnumerable<TReport> r)
{
return r.Select(x=>x.ToDto());
}
}
Then you can use it like so:
var result=conn.THeaders.ByClientId(200).ToDto();
If you prefer not having your connection at the module level, that is easy too:
using(var connection = new TReportEntitiesConnection())
{
var result=connection.THeaders.ByClientId(200).ToDto();
}
(or use AutoMapper and skip all the manual Dto conversions)
I'm creating a synchronize function between a device and server for a large database. I have a lot of listing tables (the items in a dropdown/picker).
I don't want to write a lot of code and I'm looking for an elegant solution :)
On a device in SQLite I defined listing table like
public class NameTable : IBusinessEntity {
public int Id { get; set; } = 0;
public string Description { get; set; }
}
When I save in database a new record (item) I call this function
public int SaveItem<T>(T item) where T : IBusinessEntity {
lock (locker) {
if (item.Id != 0) {
database.Update(item);
return item.Id;
}
else {
return database.Insert(item);
}
}
}
Now when the device receives a new record from the server the structure is like
public class SyncResult {
public int DeviceId { get; set; }
public int ServerId { get; set; }
public string TableName { get; set; }
public string Description { get; set; }
}
Then I want to save (insert a new record if DeviceId == 0 or update an existing item).
My question is: how can I call SaveItem where T is the TableName from SyncResult?
Thank you in advance for any help (I can offer a beer for that!)
SaveItem is a member of MyDatabase class. Basically my problem is how to pass to SaveItem<T> the T as string.
I don't know if I explained clearly my issue.
You could map a TableName to a Type for example Dictionary<string,Type> Use the Activator class to construct the type. Use reflection to fill the data.
I've followed MSDN on how to handle enumerations in Code First for EF6. It worked, as supposed to but the field in the created table that refers to the enumerator is a simple int.
I'd prefer a second table to be produced, the values of which would follow the definition of the enumerator in C# code. So, instead of only getting a table corresponding to Department in the example on MSDN, I'd also like to see a second table populated by the items from Faculty.
public enum Faculty { Eng, Math, Eco }
public partial class Department
{
[Key] public Guid ID { get; set; }
[Required] public Faculty Name { get; set; }
}
Researching the issue, I stumbled upon a solution, which suggests creating a table for the enumeration and populating it explicitly by seeding.
It appear to me as a cumbersome approach and a lot of work that should be handled automagically. After all, the system knows what actual values that constitute the enumeration. From DB point of view it's still data rows, just as the entities that I create but from OO aspect, it's not really a data - rather a type (loosely expressed) that can assume a finite and onbeforehand known number of states.
Is the approach of populating the table "manually" recommended?
Since EF doesn't handle it automatically, yes, this is the recommend way.
I suggest some modifications in article that you provided.
Rename your enum
public enum FacultyEnum { Eng, Math, Eco }
Create a class that represent the table
public class Faculty
{
private Faculty(FacultyEnum #enum)
{
Id = (int)#enum;
Name = #enum.ToString();
Description = #enum.GetEnumDescription();
}
protected Faculty() { } //For EF
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
[MaxLength(100)]
public string Description { get; set; }
public static implicit operator Faculty(FacultyEnum #enum) => new Faculty(#enum);
public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}
Your model reference the class
public class ExampleClass
{
public virtual Faculty Faculty { get; set; }
}
Create a extension method to get description from enum and seed values
using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
public static class Extensions
{
public static string GetEnumDescription<TEnum>(this TEnum item)
=> item.GetType()
.GetField(item.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.FirstOrDefault()?.Description ?? string.Empty;
public static void SeedEnumValues<T, TEnum>(this IDbSet<T> dbSet, Func<TEnum, T> converter)
where T : class => Enum.GetValues(typeof(TEnum))
.Cast<object>()
.Select(value => converter((TEnum)value))
.ToList()
.ForEach(instance => dbSet.AddOrUpdate(instance));
}
Add the seed in Configuration.cs
protected override void Seed(Temp.MyClass context)
{
context.Facultys.SeedEnumValues<Faculty, FacultyEnum>(#enum => #enum);
context.SaveChanges();
}
Add the enum table in your DbContext
public class MyClass : DbContext
{
public DbSet<ExampleClass> Examples { get; set; }
public DbSet<Faculty> Facultys { get; set; }
}
Use it
var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;
if (example.Faculty == FacultyEnum.Math)
{
//code
}
To remember
If you don't add virtual in Faculty property, you must use Include method from DbSet to do Eager Load
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
If Faculty property is virtual, then just use it
var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
Based on #Alberto Monteiro answer i've created generic class in case when you have several tables. The notice here is that Id is the type of TEnum. Using it in such way will provide option to use Enum for declaring property type.
public class Question
{
public QuestionTypeEnum QuestionTypeId { get; set; } // field property
public QuestionType QuestionType { get; set; } // navigation property
}
By default Enum using integers, so the db provider will create field with "int" type.
EnumTable.cs
public class EnumTable<TEnum>
where TEnum : struct
{
public TEnum Id { get; set; }
public string Name { get; set; }
protected EnumTable() { }
public EnumTable(TEnum enumType)
{
ExceptionHelpers.ThrowIfNotEnum<TEnum>();
Id = enumType;
Name = enumType.ToString();
}
public static implicit operator EnumTable<TEnum>(TEnum enumType) => new EnumTable<TEnum>(enumType);
public static implicit operator TEnum(EnumTable<TEnum> status) => status.Id;
}
ExceptionHelpers.cs
static class ExceptionHelpers
{
public static void ThrowIfNotEnum<TEnum>()
where TEnum : struct
{
if (!typeof(TEnum).IsEnum)
{
throw new Exception($"Invalid generic method argument of type {typeof(TEnum)}");
}
}
}
Now you just can inherit the EnumTable
public enum QuestionTypeEnum
{
Closed = 0,
Open = 1
}
public class QuestionType : EnumTable<QuestionTypeEnum>
{
public QuestionType(QuestionTypeEnum enumType) : base(enumType)
{
}
public QuestionType() : base() { } // should excplicitly define for EF!
}
Seed the values
context.QuestionTypes.SeedEnumValues<QuestionType, QuestionTypeEnum>(e => new QuestionType(e));
Another possibility, if you want to keep your model simpler, POCO style, use the enum as a property that will be stored as an integer by entity framework.
Then, if you want the "enum tables" to be created and updated in your DB, I recommend using the nuget package https://github.com/timabell/ef-enum-to-lookup and use it in a EF Migration seed method for example:
public enum Shape
{
Square,
Round
}
public class Foo
{
public int Id { get; set; }
public Shape Shape { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
}
using(var context = new MyDbContext())
{
var enumToLookup = new EnumToLookup
{
TableNamePrefix = string.Empty,
NameFieldLength = 50,
UseTransaction = true
};
enumToLookup.Apply(context);
}
This will create the "Shape" table with 2 rows named Square and Round, with the relevant foreign key constraint in the table "Foo"
Excellent #AlbertoMonterio! To get this to work with ASP.NET CORE / EF Core I made a few adjustments to Alberto's solution.
For brevity, only the modifications are shown below:
Create a extension method to get description from enum and seed values
using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Microsoft.EntityFrameworkCore; //added
using Microsoft.EntityFrameworkCore.Metadata.Builders; //added
public static class Extensions
{
//unchanged from alberto answer
public static string GetEnumDescription<TEnum>(this TEnum item)
=> item.GetType()
.GetField(item.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.FirstOrDefault()?.Description ?? string.Empty;
//changed
public static void SeedEnumValues<T, TEnum>(this ModelBuilder mb, Func<TEnum, T> converter)
where T : class => Enum.GetValues(typeof(TEnum))
.Cast<object>()
.Select(value => converter((TEnum)value))
.ToList()
.ForEach(instance => mb.Entity<T>().HasData(instance));
}
Add the seed in Configuration.cs
Add Seeding to OnModelCreating of DataContext
protected override void OnModelCreating(ModelBuilder builder)
{
builder.SeedEnumValues<Faculty, EnumEntityRole>(e => e);
}
Another approach that works (and feels simpler to me) in EF Core:
Your Enum
public enum Color
{
Red = 1,
Blue = 2,
Green = 3,
}
Db Tables
public class CustomObjectDto
{
public int ID { get; set; }
// ... other props
public Color ColorID { get; set; }
public ColorDto ColorDto { get; set; }
}
public class ColorDto
{
public Color ID { get; set; }
public string Name { get; set; }
}
Your DbContext
public class Db : DbContext
{
public Db(DbContextOptions<Db> options) : base(options) { }
public DbSet<CustomObjectDto> CustomObjects { get; set; }
public DbSet<ColorDto> Colors { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Seed database with all Colors
foreach (Color color in Enum.GetValues(typeof(Color)).Cast<Color>())
{
ColorDto colorDto = new ColorDto
{
ID = color,
Name = color.ToString(),
};
modelBuilder.Entity<ColorDto>().HasData(colorDto);
}
}
}
In code I basically only use the enum Color (never ColorDto). But it's still nice to have the 'Colors' table with an FK in the 'CustomObjects' table for sql queries and views.
I might be a bit late for the party but I didn't find the answer I was looking for here.
While looking around in the EntityFramework documentation I found the solution, it is the first example in Value Conversions
With this you can make a nice extension method if you want. i.e.
public static void HasEnum<TEntity, TProperty>(this EntityTypeBuilder<TEntity> entityBuilder, Expression<Func<TEntity, TProperty>> propertyExpression)
where TEntity : class
where TProperty : Enum
{
entityBuilder.Property(propertyExpression)
.HasConversion(
v => v.ToString(),
v => (TProperty)Enum.Parse(typeof(TProperty), v)
);
}
Then use it in your OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourEntity>()
.HasEnum(e => e.YourProperty);
}
You should add : byte in front of enum declaration :
enum MyFieldEnum : byte{
one = 1,
two = 2,
three = 4
}
In database, you should see TINYINT and no need to casting !
UPDATE: I found a better way that works well in EntityFrameworkCore 5.0.8
Add JsonConverter attributes to your enum
[Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))]
public enum FacultyEnum
{
[EnumMember(Value = "English Professor")]
Eng,
[EnumMember(Value = "Math Professor")]
Math,
[EnumMember(Value = "Economics Professor")]
Eco
}
Create a class the represents the table
public class Faculty
{
public int Id { get; set; }
public string Name { get; set; }
public FacultyEnum Description { get; set; }
}
Use Fluent API in OnModelCreating in your DbContext to use the enum strings and set check constraints
var enumToString = new EnumToStringConverter<FacultyEnum>();
modelBuilder.Entity<Faculty>(entity =>
{
entity.ToTable(nameof(FacultyMembers));
//convert enums to string
entity.Property(e => e.Description).HasConversion(enumToString);
//build check constraint from enum
var allowedEnumStrings = string.Join(',',
typeof(Faculty).GetMembers()
.Select(x => x.GetCustomAttribute(typeof(EnumMemberAttribute), false)).Where(x => x != null)
.Select(x => $"'{((EnumMemberAttribute)x).Value}'"));
entity.HasCheckConstraint($"CK_{nameof(FacultyMembers)}_{nameof(Faculty.Description)}", $"{nameof(Faculty.Description)} in ({allowedEnumStrings})");
});
Old Way
Alberto Monteiro answered this very well. I had to make a few adjustments to get it to work with EF core.
Rename your enum and add description decorators
public enum FacultyEnum
{
[Description("English Professor")]
Eng,
[Description("Math Professor")]
Math,
[Description("Economics Professor")]
Eco
}
Create a class that represent the table
public class Faculty
{
private Faculty(FacultyEnum #enum)
{
Id = (int)#enum;
Name = #enum.ToString();
Description = #enum.GetEnumDescription();
}
protected Faculty() { } //For EF
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
[MaxLength(100)]
public string Description { get; set; }
public static implicit operator Faculty(FacultyEnum #enum) => new Faculty(#enum);
public static implicit operator FacultyEnum(Faculty faculty) => (FacultyEnum)faculty.Id;
}
Your model reference the class
public class ExampleClass
{
public virtual Faculty Faculty { get; set; }
}
Create a extension method to get description from enum and seed values
using System;
using System.ComponentModel;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
public static class Extensions
{
public static string GetEnumDescription<TEnum>(this TEnum item)
=> item.GetType()
.GetField(item.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false)
.Cast<DescriptionAttribute>()
.FirstOrDefault()?.Description ?? string.Empty;
}
Add the seed in YourDbContext.cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Faculty>().HasData(FacultyEnum.Eng, FacultyEnum.Math, FacultyEnum.Eco);
}
Add the enum table in your DbContext
public class MyClass : DbContext
{
public DbSet<ExampleClass> Examples { get; set; }
public DbSet<Faculty> Facultys { get; set; }
}
Use it
var example = new ExampleClass();
example.Faculty = FacultyEnum.Eng;
if (example.Faculty == FacultyEnum.Math)
{
//code
}
To remember
If you don't add virtual in Faculty property, you must use Include method from DbSet to do Eager Load
var exampleFromDb = dbContext.Examples.Include(x => x.Faculty).SingleOrDefault(e => e.Id == 1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
If Faculty property is virtual, then just use it
var exampleFromDb = dbContext.Examples.Find(1);
if (example.Faculty == FacultyEnum.Math)
{
//code
}
I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.