Type.GetProperties(bindingFlags) is not giving fields from parent classes - reflection

I am trying to list all the properties in the type as shown below.
I am loading the DLL file using Assembly.LoadFile(dllFilePath).
Getting all properties in assembly using assembly.GetTypes().ToList().
Classes:
public class A
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
public class B : A
{
public int Property5 { get; set; }
}
Methods:
static void Main()
{
Assembly assembly = Assembly.LoadFile(dllFilePath);
List<Type> types = assembly.GetTypes().ToList();
GetAllProperties(typeof(types.FirstOrDefult(a => a.Name == "B")));
}
private void GetAllProperties(Type type)
{
BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic
| BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Static
| BindingFlags.FlattenHierarchy;
// Test 1: No inherited properties.
PropertyInfo[] propertyInfoList1 = type.GetProperties(bindingFlags);
List<string> propertyNameList1 = new List<string>();
foreach (PropertyInfo propertyInfo1 in propertyInfoList1)
{
propertyNameList1.Add(propertyInfo1.Name);
}
// Test 2: No inherited properties.
PropertyInfo[] propertyInfoList2 = Activator.CreateInstance(type).GetType().GetProperties(bindingFlags);
List<string> propertyNameList2 = new List<string>();
foreach (PropertyInfo propertyInfo2 in propertyInfoList2)
{
propertyNameList2.Add(propertyInfo2.Name);
}
// Test 3: object has all inherited properties but propertyInfoList doesn't have inherited properties.
object typeInstance = Activator.CreateInstance(type);
PropertyInfo[] propertyInfoList3 = typeInstance.GetType().GetProperties(bindingFlags);
List<string> propertyNameList3 = new List<string>();
foreach (PropertyInfo propertyInfo3 in propertyInfoList3)
{
propertyNameList3.Add(propertyInfo3.Name);
}
}
In Test 3 all parent class properties are visible when I inspect it.
But typeInstance.GetType().GetProperties(bindingFlags) doesn't return all parent class properties.

I think you have to remove the flag BindingFlags.DeclaredOnly because the purpose of that flag is exactly to remove the inherited properties from your result.

Related

Dynamically paging EF Core results

I have a UI grid which permits sorting by column :
Id
Organisation Name
Organisation type
Departments
1
first
some type
def
2
second
another type
abc, def
2
third
some type
xyz
See the entities below:
public class Organisation
{
public int Code { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public List<Department> Departments { get; set; }
}
public class Department
{
public int Code { get; set; }
public string Name { get; set; }
}
I want to be able to sort the table values by Departments which is a comma separated values that comes from Organization.Departments.Select(p=> p.Name);
I would like to make the sorting as an IQueryable and avoid bringing all the data in memory because After sorting I will apply the pagination and I don't want to bring all the DB records in memory.
I'm using the following extension method for sorting, but it is not working for nested collections:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
{
var type = typeof(T);
var property = type.GetProperty(sortProperty, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property == null)
throw new OperationFailedException($"Sorting by {sortProperty}");
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { type, property.PropertyType };
var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
This method works fine for properties that are at object level.
IQueryable I'm using later for sorting looks something like this:
var iQueryableToBeSorted = _dbContext.Organization.Include(p=>p.Departments).AsQueryable();

Query Cosmos DB to get a list of different derived types using the .Net SDK Microsoft.Azure.Cosmos

We have an interface and a base class with multiple derived types.
public interface IEvent
{
[JsonProperty("id")]
public string Id { get; set; }
string Type { get; }
}
public abstract class EventBase: IEvent
{
public string Id { get; set; }
public abstract string Type { get; }
}
public class UserCreated : EventBase
{
public override string Type { get; } = typeof(UserCreated).AssemblyQualifiedName;
}
public class UserUpdated : EventBase
{
public override string Type { get; } = typeof(UserUpdated).AssemblyQualifiedName;
}
We are storing these events of different derived types in the same container in Cosmos DB using v3 of .Net SDK Microsoft.Azure.Cosmos. We then want to read all the events and have them deserialized to the correct type.
public class CosmosDbTests
{
[Fact]
public async Task TestFetchingDerivedTypes()
{
var endpoint = "";
var authKey = "";
var databaseId ="";
var containerId="";
var client = new CosmosClient(endpoint, authKey);
var container = client.GetContainer(databaseId, containerId);
await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });
var queryable = container.GetItemLinqQueryable<IEvent>();
var query = queryable.ToFeedIterator();
var list = new List<IEvent>();
while (query.HasMoreResults)
{
list.AddRange(await query.ReadNextAsync());
}
Assert.NotEmpty(list);
}
}
Doesn't seem to be any option to tell GetItemLinqQueryable how to handle types. Is there any other method or approach to support multiple derived types in one query?
It's ok to put the events in some kind of wrapper entity if that would help, but they aren't allowed to be stored as an serialized sting inside a property.
The comment from Stephen Clearly pointed me in the right direction and with the help of this blog https://thomaslevesque.com/2019/10/15/handling-type-hierarchies-in-cosmos-db-part-2/ I ended up with a solution similar to the following example were we have a custom CosmosSerializer that uses a custom JsonConverter that reads the Type property.
public interface IEvent
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("$type")]
string Type { get; }
}
public abstract class EventBase: IEvent
{
public string Id { get; set; }
public string Type => GetType().AssemblyQualifiedName;
}
public class UserCreated : EventBase
{
}
public class UserUpdated : EventBase
{
}
EventJsonConverter reads the Type property.
public class EventJsonConverter : JsonConverter
{
// This converter handles only deserialization, not serialization.
public override bool CanRead => true;
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
// Only if the target type is the abstract base class
return objectType == typeof(IEvent);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// First, just read the JSON as a JObject
var obj = JObject.Load(reader);
// Then look at the $type property:
var typeName = obj["$type"]?.Value<string>();
return typeName == null ? null : obj.ToObject(Type.GetType(typeName), serializer);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("This converter handles only deserialization, not serialization.");
}
}
The NewtonsoftJsonCosmosSerializer takes a JsonSerializerSettings that it uses for serialization.
public class NewtonsoftJsonCosmosSerializer : CosmosSerializer
{
private static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
private readonly JsonSerializer _serializer;
public NewtonsoftJsonCosmosSerializer(JsonSerializerSettings settings)
{
_serializer = JsonSerializer.Create(settings);
}
public override T FromStream<T>(Stream stream)
{
if (typeof(Stream).IsAssignableFrom(typeof(T)))
{
return (T)(object)stream;
}
using var sr = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(sr);
return _serializer.Deserialize<T>(jsonTextReader);
}
public override Stream ToStream<T>(T input)
{
var streamPayload = new MemoryStream();
using var streamWriter = new StreamWriter(streamPayload, encoding: DefaultEncoding, bufferSize: 1024, leaveOpen: true);
using JsonWriter writer = new JsonTextWriter(streamWriter);
writer.Formatting = _serializer.Formatting;
_serializer.Serialize(writer, input);
writer.Flush();
streamWriter.Flush();
streamPayload.Position = 0;
return streamPayload;
}
}
The CosmosClient is now created with our own NewtonsoftJsonCosmosSerializer and EventJsonConverter.
public class CosmosDbTests
{
[Fact]
public async Task TestFetchingDerivedTypes()
{
var endpoint = "";
var authKey = "";
var databaseId ="";
var containerId="";
var client = new CosmosClient(endpoint, authKey, new CosmosClientOptions
{
Serializer = new NewtonsoftJsonCosmosSerializer(new JsonSerializerSettings
{
Converters = { new EventJsonConverter() }
})
});
var container = client.GetContainer(databaseId, containerId);
await container.CreateItemAsync(new UserCreated{ Id = Guid.NewGuid().ToString() });
await container.CreateItemAsync(new UserUpdated{ Id = Guid.NewGuid().ToString() });
var queryable = container.GetItemLinqQueryable<IEvent>();
var query = queryable.ToFeedIterator();
var list = new List<IEvent>();
while (query.HasMoreResults)
{
list.AddRange(await query.ReadNextAsync());
}
Assert.NotEmpty(list);
}
}

making cascading dropdownlist

I am trying to make cascading dropdown list in ASP.NET MVC4, both values for my dropdown list's comes from methods, so am in trouble how to pass value form one dropdown list to another.
Here's how I get values for the first dropdown list:
var CampaignInfo1 = CampaignManagementService.GetAdvertisers((string)Session["ticket"]);
List<CampaignList1> items1 = new List<CampaignList1>();
foreach (var element in CampaignInfo1)
{
items1.Add(new CampaignList1() { ID1 = element.Key, Name1 = element.Value });
}
var listOfCamp1 = new SelectList(items1, "ID1", "Name1", 1);
ViewData["list1"] = listOfCamp1;
And dropdown list in view:
#Html.DropDownList("list1", ViewData["list1"] as SelectList, "-- Select Client -1-")
The second dropdown list value am getting almost same method:
var CampaignInf = CampaignManagementService.GetCampaigns((string)Session["ticket"], IDFromfirstDDL);
List<AlreadyCreatedCampaignList> itemas = new List<AlreadyCreatedCampaignList>();
foreach (var element in CampaignInf)
{
itemas.Add(new AlreadyCreatedCampaignList() { campID = element.Key, campName = element.Value });
}
var listOfCam = new SelectList(itemas, "campID", "campName", 1);
ViewData["clist"] = listOfCam;
But there is a problem that in method GetCampaigns I have to pass the id(IDFromfirstDDL) which I get from first DDL, and only then method return values which are for that id.
The problem is that I don't know how to pass that selected value from first DDL to second, without any form submit, because I need that second DDL changes his values immediately after first DDL changes.
i made it combining http://kruisit.nl/articles/asp.net-mvc-linked-dropdown/ and http://www.appelsiini.net/projects/chained jquery chained selector this article
currently using in my site
public static class LinkedDropdownHelper
{
#region Methods
public static MvcHtmlString LinkedDropdownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string parent, IEnumerable<LinkedSelectListItem> selectList ,bool removedefault=false)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression( expression,htmlHelper.ViewData );
var selectedvalue = metadata.Model;
string propertyName = metadata.PropertyName;
TagBuilder select = new TagBuilder("select");
select.Attributes.Add("id", propertyName);
select.Attributes.Add("name", propertyName);
//select.Attributes.Add("class", "linked-dropdown");
select.Attributes.Add("class", parent);
foreach (var item in selectList)
{
if (removedefault && item.Value == "-1")
{
//skip default
}
else
{
TagBuilder option = new TagBuilder("option");
option.InnerHtml = item.Text;
option.Attributes.Add("value", item.Value);
option.Attributes.Add("class", item.LinkValue);
if (item.Selected)
{
option.Attributes.Add("selected", "selected");
}
select.InnerHtml += option.ToString(TagRenderMode.Normal);
}
}
//below code was changed by abdurrauf to support jquery chains
string script = #"<script type='text/javascript'>$(document).bind('ready', function(){
$('#" + propertyName + "').chained('#" + parent + "');"+
#"$('select[name=""" + propertyName + #"""]').val("""+selectedvalue+#""");" +
"});</script>";
return MvcHtmlString.Create(script + select.ToString(TagRenderMode.Normal));
}
#endregion Methods
}
public class LinkedSelectList : IEnumerable<LinkedSelectListItem>
{
#region Constructors
public LinkedSelectList(IEnumerable items, string dataValueField, string dataTextField, string dataLinkedValueField, IEnumerable selectedValues)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
Items = items;
DataValueField = dataValueField;
DataTextField = dataTextField;
DataLinkedValueField = dataLinkedValueField;
SelectedValues = selectedValues;
}
#endregion Constructors
#region Properties
public string DataLinkedValueField
{
get; private set;
}
public string DataTextField
{
get; private set;
}
public string DataValueField
{
get; private set;
}
public IEnumerable Items
{
get; private set;
}
public IEnumerable SelectedValues
{
get; private set;
}
#endregion Properties
#region Methods
public virtual IEnumerator<LinkedSelectListItem> GetEnumerator()
{
return GetListItems().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
internal IList<LinkedSelectListItem> GetListItems()
{
return GetListItemsWithValueField();
}
private static string Eval(object container, string expression)
{
object value = container;
if (!String.IsNullOrEmpty(expression))
{
value = DataBinder.Eval(container, expression);
}
return Convert.ToString(value, CultureInfo.CurrentCulture);
}
private IList<LinkedSelectListItem> GetListItemsWithValueField()
{
HashSet<string> selectedValues = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (SelectedValues != null)
{
selectedValues.UnionWith(from object value in SelectedValues select Convert.ToString(value, CultureInfo.CurrentCulture));
}
var listItems = from object item in Items
let value = Eval(item, DataValueField)
select new LinkedSelectListItem
{
Value = value,
Text = Eval(item, DataTextField),
LinkValue = Eval(item, DataLinkedValueField),
Selected = selectedValues.Contains(value)
};
return listItems.ToList();
}
#endregion Methods
}
public class LinkedSelectListItem
{
#region Properties
public string LinkValue
{
get; set;
}
public bool Selected
{
get; set;
}
public string Text
{
get; set;
}
public string Value
{
get; set;
}
#endregion Properties
}

Steps to map classes using ValueInjector

Quickly getting to the problem the mapping does not occur for the following code. Could someone explain why? or what i should do for the mapping to occur?
var parent = new Parent();
parent.ChildOne.Add(new ChildOne() { Name = "Child One" });
parent.ChildTwo.Add(new ChildTwo() { Name = "Child Two" });
AnotherParent anotherParent = new AnotherParent();
anotherParent.InjectFrom<LoopValueInjection>(parent);
Required Class are below
Anothher child one
public class AnotherChildOne
{
public string Name { get; set; }
}
Another child two
public class AnotherChildTwo
{
public string Name { get; set; }
}
Another Parent
public class AnotherParent
{
public ICollection<AnotherChildOne> ChildOne { get; set; }
public ICollection<AnotherChildTwo> ChildTwo { get; set; }
public AnotherParent()
{
ChildOne = new Collection<AnotherChildOne>();
ChildTwo = new Collection<AnotherChildTwo>();
}
}
Child Two
public class ChildTwo
{
public string Name { get; set; }
}
Child One
public class ChildOne
{
public string Name { get; set; }
}
Parent
public class Parent
{
public ICollection<ChildOne> ChildOne { get; set; }
public ICollection<ChildTwo> ChildTwo { get; set; }
public Parent()
{
ChildOne = new Collection<ChildOne>();
ChildTwo = new Collection<ChildTwo>();
}
}
I believe by default Value Injector will only inject the properties with the same name of the same type. You can get around this using a tweak to the CloneInjection sample from the Value Injector documentation as described here with this code:
public class CloneInjection : ConventionInjection
{
protected override bool Match(ConventionInfo c)
{
return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
}
protected override object SetValue(ConventionInfo c)
{
//for value types and string just return the value as is
if (c.SourceProp.Type.IsValueType || c.SourceProp.Type == typeof(string)
|| c.TargetProp.Type.IsValueType || c.TargetProp.Type == typeof(string))
return c.SourceProp.Value;
//handle arrays
if (c.SourceProp.Type.IsArray)
{
var arr = c.SourceProp.Value as Array;
var clone = Activator.CreateInstance(c.TargetProp.Type, arr.Length) as Array;
for (int index = 0; index < arr.Length; index++)
{
var a = arr.GetValue(index);
if (a.GetType().IsValueType || a.GetType() == typeof(string)) continue;
clone.SetValue(Activator.CreateInstance(c.TargetProp.Type.GetElementType()).InjectFrom<CloneInjection>(a), index);
}
return clone;
}
if (c.SourceProp.Type.IsGenericType)
{
//handle IEnumerable<> also ICollection<> IList<> List<>
if (c.SourceProp.Type.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
var t = c.TargetProp.Type.GetGenericArguments()[0];
if (t.IsValueType || t == typeof(string)) return c.SourceProp.Value;
var tlist = typeof(List<>).MakeGenericType(t);
var list = Activator.CreateInstance(tlist);
var addMethod = tlist.GetMethod("Add");
foreach (var o in c.SourceProp.Value as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(o);
addMethod.Invoke(list, new[] { e }); // in 4.0 you can use dynamic and just do list.Add(e);
}
return list;
}
//unhandled generic type, you could also return null or throw
return c.SourceProp.Value;
}
//for simple object types create a new instace and apply the clone injection on it
return Activator.CreateInstance(c.TargetProp.Type)
.InjectFrom<CloneInjection>(c.SourceProp.Value);
}
}
If you include the above CloneInjection code you will want to do this:
anotherParent.InjectFrom<CloneInjection>(parent);
instead of:
anotherParent.InjectFrom<LoopValueInjection>(parent);

Bind a complex class on MVC asp.net

I have the following classes:
public class Movie
{
string Name get; set;
string Director get; set;
IList<Tag> Tags get; set;
}
public class Tag
{
string TagName get; set;
}
On the Action of my controller I bind like this: public ActionResult Create([ModelBinder(typeof(MovieBinder))]Movie mo)
on theMovieBinder I convert the string to List<tag>. That when I debug works.
on the Movie binder I have the following code:
if (propertyDescriptor.Name == "Tags")
{
var values = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
if (values != null)
{
var p = values.AttemptedValue.Replace(" ", "");
var arrayValues = p.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var list = new List<Tag>();
foreach (string item in arrayValues)
{
list.Add(new Tag() { TagName = item });
}
value = list;
}
}
But I get the following Error in the modelstate:
Exception = {"The parameter conversion from type 'System.String' to type 'Models.Tag' failed because no type converter can convert between these types."}
I create a Tag binder, but it does not work, any ideas?
Thanks!
You could adapt the model binder I suggested here to this new situation where you have introduced the Tag class:
public class MovieModelBinder : DefaultModelBinder
{
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
if (propertyDescriptor.Name == "Tags")
{
var values = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
if (values != null)
{
return values.AttemptedValue.Split(',').Select(x => new Tag
{
TagName = x
}).ToList();
}
}
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
}

Resources