i am working on source generator and i need to read properties data type as string
Is there any solution to get data type name such as int, string, bool, etc...?
EDIT:
var typeName = property.Type.ToString(); throws exception
EDIT 2:
public void Execute(GeneratorExecutionContext context)
{
var model = new TemplateModel();
var syntaxReceiver = (SolnetParsableClassExplorer)context.SyntaxReceiver;
syntaxReceiver?.SolnetDtos.ForEach(dto =>
{
var typeNodeSymbol = context.Compilation
.GetSemanticModel(dto.SyntaxTree)
.GetDeclaredSymbol(dto);
var dataClass = new DataClassModel();
foreach (var member in dto.DescendantNodes())
{
if (member is PropertyDeclarationSyntax property)
{
dataClass.AddProperty(property);
}
}
dataClass.ClassName = dto.Identifier.ValueText;
model.DataClassModels.Add(dataClass);
});
...
}
internal class SolnetParsableClassExplorer : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> SolnetDtos { get; } = new();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classSyntax &&
classSyntax.HaveAttribute("SolnetDto"))
{
SolnetDtos.Add(classSyntax);
}
}
}
internal class DataClassModel
{
public List<DataField> Properties = new List<DataField>();
public string Namespace { get; set; }
public string ClassName { get; set; }
public int Size => Properties.Sum(p => p.Size);
public void AddProperty(PropertyDeclarationSyntax property)
{
// Here generator throw exception
var typeName = property.Type.ToString();
Properties.Add(
new DataField
{
PropertyName = property.Identifier.ValueText,
DataType = typeName,
Size = GetPropertySize(compilation, property, typeName),
WriteMethod = GetWriteMethod(typeName),
ReadMethod = GetReadMethod(typeName),
Offset = Size
});
}
I hope I understood your question correctly. To retrieve the string representation, you could use the TypeSyntax.ToString()
var strType = propertyDeclarationSyntax.Type.ToString();
For example, for the following
public string Bar1 { get; set; }
public int Bar2 { get; set; }
public long Bar3 { get; set; }
Result
Property Bar1 : string
Property Bar2 : int
Property Bar3 : long
Related
Some of my actions accept models like:
public class PaymentRequest
{
public decimal Amount { get; set; }
public bool? SaveCard { get; set; }
public int? SmsCode { get; set; }
public BankCardDetails Card { get; set; }
}
public class BankCardDetails
{
public string Number { get; set; }
public string HolderName { get; set; }
public string ExpiryDate { get; set; }
public string ValidationCode { get; set; }
}
And the action method looks like:
[HttpPost]
[Route("api/v1/payment/pay")]
public Task<BankCardActionResponse> Pay([FromBody] PaymentRequest request)
{
if (request == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
return _paymentService.PayAsync(DataUserHelper.PhoneNumber, request);
}
I use Nlog. I think it's clear this is a bad idea to log all this bank data. My log config file contained the following line:
<attribute name="user-requestBody" layout="${aspnet-request-posted-body}"/>
I logged the request. I decided to refactor that and planned the following strategy. Actions that contain sensitive data into their requests I will mark with an attribute like
[RequestMethodFormatter(typeof(PaymentRequest))]
then take a look at my custom renderer:
[LayoutRenderer("http-request")]
public class NLogHttpRequestLayoutRenderer : AspNetRequestPostedBody
{
protected override void DoAppend(StringBuilder builder, LogEventInfo logEvent)
{
base.DoAppend(builder, logEvent);
var body = builder.ToString();
// Get attribute of the called action.
var type = ... // How can I get "PaymentRequest" from the [RequestMethodFormatter(typeof(PaymentRequest))]
var res = MaskHelper.GetMaskedJsonString(body, type);
// ... and so on
}
}
I think you understand the idea. I need the type from the method's RequestMethodFormatter attribute. Is it even possible to get it into the renderer? I need it because I'm going to deserialize request JSON into particular models (it's gonna be into the MaskHelper.GetMaskedJsonString), work with the models masking the data, serialize it back into JSON.
So, did I choose a wrong approach? Or it's possible to get the type from the attribute into the renderer?
After some research, I ended up with the following solution:
namespace ConsoleApp7
{
internal class Program
{
private static void Main()
{
var sourceJson = GetSourceJson();
var userInfo = JsonConvert.DeserializeObject(sourceJson, typeof(User));
Console.WriteLine("----- Serialize without Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo));
Console.WriteLine("----- Serialize with Resolver-----");
Console.WriteLine(JsonConvert.SerializeObject(userInfo, new JsonSerializerSettings
{
ContractResolver = new MaskPropertyResolver()
}));
}
private static string GetSourceJson()
{
var guid = Guid.Parse("3e92f0c4-55dc-474b-ae21-8b3dac1a0942");
return JsonConvert.SerializeObject(new User
{
UserId = guid,
Age = 19,
Name = "John",
BirthDate = new DateTime(1990, 5, 12),
Hobbies = new[]
{
new Hobby
{
Name = "Football",
Rating = 5,
DurationYears = 3,
},
new Hobby
{
Name = "Basketball",
Rating = 7,
DurationYears = 4,
}
}
});
}
}
public class User
{
[MaskGuidValue]
public Guid UserId { get; set; }
[MaskStringValue("***")] public string Name { get; set; }
public int Age { get; set; }
[MaskDateTimeValue]
public DateTime BirthDate { get; set; }
public Hobby[] Hobbies { get; set; }
}
public class Hobby
{
[MaskStringValue("----")]
public string Name { get; set; }
[MaskIntValue(replacement: 11111)]
public int Rating { get; set; }
public int DurationYears { get; set; }
}
public class MaskPropertyResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = base.CreateProperties(type, memberSerialization);
var allowedPropertyTypes = new Type[]
{
typeof(Guid),
typeof(DateTime),
typeof(string),
typeof(int),
};
foreach (var prop in props.Where(p => allowedPropertyTypes.Contains(p.PropertyType)))
{
if (prop.UnderlyingName == null)
continue;
var propertyInfo = type.GetProperty(prop.UnderlyingName);
var attribute =
propertyInfo?.GetCustomAttributes().FirstOrDefault(x => x is IMaskAttribute) as IMaskAttribute;
if (attribute == null)
{
continue;
}
if (attribute.Type != propertyInfo.PropertyType)
{
// Log this case, cause somebody used wrong attribute
continue;
}
prop.ValueProvider = new MaskValueProvider(propertyInfo, attribute.Replacement, attribute.Type);
}
return props;
}
private class MaskValueProvider : IValueProvider
{
private readonly PropertyInfo _targetProperty;
private readonly object _replacement;
private readonly Type _type;
public MaskValueProvider(PropertyInfo targetProperty, object replacement, Type type)
{
_targetProperty = targetProperty;
_replacement = replacement;
_type = type;
}
public object GetValue(object target)
{
return _replacement;
}
public void SetValue(object target, object value)
{
_targetProperty.SetValue(target, value);
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskStringValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(string);
public object Replacement { get; }
public MaskStringValueAttribute(string replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskIntValueAttribute : Attribute, IMaskAttribute
{
public object Replacement { get; }
public Type Type => typeof(int);
public MaskIntValueAttribute(int replacement)
{
Replacement = replacement;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskGuidValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(Guid);
public object Replacement => Guid.Empty;
}
[AttributeUsage(AttributeTargets.Property)]
public class MaskDateTimeValueAttribute : Attribute, IMaskAttribute
{
public Type Type => typeof(DateTime);
public object Replacement => new DateTime(1970, 1, 1);
}
public interface IMaskAttribute
{
Type Type { get; }
object Replacement { get; }
}
}
I hope somebody will find it helpful.
You can try nuget package https://www.nuget.org/packages/Slin.Masking and https://www.nuget.org/packages/Slin.Masking.NLog.
It can easily be integrated with DotNet projects with slight changes, and you can define your rules for it. But the document needs some improvement.
As a suggestion, you can use two files:
masking.json (can be a generic one, that shared across all projects)
masking.custom.json (can be used with particular rules for specific projects)
I'd like an extension method to create an object based on another but keep only the primitive properties. This object will be dumped into a log file in JSON format for logging.
Based on the classes shown below, in this sample, the created object should keep only these properties :
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
I am using .NET Framework 4.7
How can I do this?
// To use like this
var order = new Order();
var forLog = order.RemovePrimitives();
// Sample of classes
public class Order
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public List<Item> Items { get; set; }
public Address Address { get; set; }
}
public class Item{}
public class Address{}
public static class Extensions
{
public static string RemovePrimitives(this object obj)
{
// I need to create an anonymous, named 'TheNewObjectHere' object but only with primitives
// I will dump the object to push to a log file. I need only primitives
return JsonConvert.SerializeObject(TheNewObjectHere, Formatting.Indented);
}
}
Thanks
try this
public static class Extensions
{
public static string RemovePrimitives(this object obj)
{
var jsonObj = JObject.FromObject(obj);
var propToRemove = jsonObj.Properties().Where(i => !i.Value.GetType().ToString()
.Contains("JValue")).ToList();
foreach (var prop in propToRemove) prop.Remove();
return jsonObj.ToString();
}
}
You can use reflection to get primitive properties and then use JObject to build a JSON object dynamically.
public static readonly Type[] AdditionalPrimities = new[] { typeof(decimal), typeof(string) };
public static string RemovePrimitives<T>(this T obj)
{
var jObj = new JObject();
var props = GetPrimitiveProperties(obj);
foreach (var item in props)
{
var value = item.GetValue(obj);
if (value != null)
{
jObj.Add(item.Name, new JValue(value));
}
}
return jObj.ToString(Newtonsoft.Json.Formatting.Indented);
}
public static PropertyInfo[] GetPrimitiveProperties<T>()
{
var properties = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(r => r.PropertyType.IsPrimitive || (r.PropertyType.IsGenericType && Nullable.GetUnderlyingType(r.PropertyType) != null) || AdditionalPrimities.Contains(r.PropertyType))
.Select(r => r)
.ToArray();
return properties;
}
public static void Main()
{
var order = new Order { FirstName = "abc", LastName = "cde", Address = new Address(), Age2 = 3, Age = 1 };
var final = order.RemovePrimitives();
Console.WriteLine(final);
}
Fiddle
I am writing a method for extracting all properties from an object (including properties of its own) with custom attribute . For example
public class SomeModel
{
[Custom]
public string Name { get; set; }
public string TestData { get; set; }
[Custom]
public string Surname { get; set; }
public InnerModel InnerModel { get; set; }
}
And Inner Model :
public class InnerModel
{
public string Id { get; set; } = "TestID";
[Custom]
public string Year { get; set; }
public ThirdObject HidedObject { get; set; }
}
And the third one :
public class ThirdObject
{
[Custom]
public string HidedName { get; set; }
}
I need to find all properties with "Custom" attribute .
Testing :
SomeModel model = new SomeModel()
{
Name = "farid",
Surname = "Ismayilzada",
TestData = "Test" ,
InnerModel = new InnerModel() { Year ="2022" , HidedObject= New ThirdObject{ HidedName="Secret"}}
};
I need to write the method
GetMyProperties(model) => List<PropInf>()
[PropertyName= Name,Value=Farid ,Route="Name" ]
[PropertyName= Surname,Value=Ismayilzada,Route="Surname" ]
[PropertyName= Year,Value=2022,Route="InnerModel.Year" ]
[PropertyName= HidedName,Value=Secret,Route="InnerModel.HidedObject.HidedName" ]
How to get this information ?
You can write a method like this :
private static IEnumerable<PropInfo> GetPropertiesInfo(object obj, string route = "")
{
List<PropInfo> results = new List<PropInfo>();
// You can filter wich property you want https://learn.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo?view=net-6.0
var objectProperties = obj.GetType().GetProperties().Where(p => p.CanRead);
foreach (var property in objectProperties)
{
var value = property.GetValue(obj);
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
results.AddRange(GetPropertiesInfo(value, route + property.Name + "."));
}
else
{
// Check if the property has the Custom Attribute
var customAttributes = property.GetCustomAttributes<CustomAttribute>();
if (!customAttributes.Any())
continue;
// You can set a method in your Attribute : customAttributes.First().CheckIfNeedToStoreProperty(obj);
results.Add(new PropInfo()
{
PropertyName = property.Name,
Value = value,
Route = route + property.Name
});
}
}
return results;
}
public class PropInfo
{
public string PropertyName { get; set; }
public object Value { get; set; }
public string Route { get; set; }
}
public class CustomAttribute : Attribute
{
public bool CheckIfNeedToStoreProperty(object obj)
{
return true;
}
}
I have this json string and i want to get the 4th line (iValue, sValue) of every record.
My problem here is the keys vary for every record (based on the data type of the value).
Is there any way to do this on C#?
Here is an example:
{ "data": [
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "CID",
"iValue": "13"
},
{
"pKey": "0",
"Entity": "tableName",
"Attribute": "username",
"sValue": "test_user1"
}] }
Here is kind of a big implementation, you will have to implement this for each iValue, fValue, etc however, it speeds up the implementation and usage. First of, here is the usage:
string rawJson = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var values = JsonConvert.DeserializeObject<TakeData>(rawJson).Data.Select(v => v.PureData);
Now values contains the list. Here is the usage for accessing each:
foreach (var val in values)
{
if (val is IntData i)
{
int myInt = i.iValue;
// use the rest of the properties
}
else if (val is StrData s)
{
string myStr = s.sValue;
// use the rest of the properties
}
}
And here is the implementation:
class TakeData
{
public List<TakeItAll> Data { get; set; }
}
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
PureData = new IntData { pKey = pKey, Entity = Entity, Attribute = Attribute, iValue = iValue };
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
PureData = new StrData { pKey = pKey, Entity = Entity, Attribute = Attribute, sValue = sValue };
}
}
public IPureData PureData { get; private set; }
}
interface IPureData
{
int pKey { get; set; }
string Entity { get; set; }
string Attribute { get; set; }
}
class IntData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public int iValue { get; set; }
}
class StrData : IPureData
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string sValue { get; set; }
}
Of course you can use some alternatives as well. Such as using an enum in TakeItAll to keep track of the data type (or a type variable) instead of so many classes. This way However the size of the values object would be larger.
class TakeItAll
{
public int pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
private int _iValue;
public int iValue
{
get => _iValue;
set
{
_iValue = value;
ValType = typeof(string);
}
}
private string _sValue;
public string sValue
{
get => _sValue;
set
{
_sValue = value;
ValType = typeof(int);
}
}
public Type ValType { get; private set; }
}
I would deserialize this into an object supporting both types of properties and then by code try parsing either the integer or the string if the integer fails.
If the Attribute value gives you a clue as to which one to look for, you could also use that to prevent having to try parsing the integer every time.
I would not rely on the property being the "fourth" property every time, as I'm assuming this would be external data, where you may not be able to control whether these properties come out in the exact same order every time (now and in the future).
If you don't know the data type then you could use an object to handle it.
It's a good idea to deserialize JSON string to concrete class to avoid string manipulation mistake.
public class Datum
{
public object pKey { get; set; }
public string Entity { get; set; }
public string Attribute { get; set; }
public string iValue { get; set; }
public string sValue { get; set; }
}
public class DataCollection
{
public List<Datum> data { get; set; }
}
public void Test()
{
var str = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var list = JsonConvert.DeserializeObject<DataCollection>(str);
var keys = list.data.Select(x => x.pKey).ToList();
}
Another option is to deserialize to dynamic and inspect that:
var json = "{\"data\":[{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"CID\",\"iValue\":\"13\"},{\"pKey\":\"0\",\"Entity\":\"tableName\",\"Attribute\":\"username\",\"sValue\":\"test_user1\"}]}";
var result = JsonConvert.DeserializeAnonymousType<dynamic>(json, null);
if (result.data != null)
{
for (var i = 0; i < result.data.Count; i++)
{
if (result.data[i]["iValue"] != null)
// Parse iValue
if (result.data[i]["sValue"] != null)
// Parse sValue
}
}
You could load the Json in a ExpandoObject
var expConverter = new ExpandoObjectConverter();
dynamic objList = JsonConvert.DeserializeObject<List<ExpandoObject>>(json, expConverter);
JSON array to ExpandoObject via JSON.NET
Then once you have loaded it in as a List<ExpandoObject> you may itterate over it as a dictionary.
foreach(var obj in objList)
{
//convert the object to a Dictionary and select the 4th element.
var yourresult = (obj as IDictionary<string, object>).ElementAt(3);
}
My two cents:
Get each object of the array as a KeyValuePair
var json = "your json here";
var root = JsonConvert.DeserializeObject<Root>(json);
foreach (var element in root.Data)
{
//===================================================> Here using object because your value type change. You can change it to string if your value is always wrapped in a string (like "13")
var keyValuePair = element.ToObject<Dictionary<string, object>>();
//here, for each object of the 'data' array, you can check if the desidered property exists
if (keyValuePair.ContainsKey("iValue"))
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.ContainsKey("sValue"))
{
var propertyValue = keyValuePair["sValue"];
}
// Or you can check the property name in the desidered position
if (keyValuePair.Keys.ElementAt(3) == "iValue")
{
var propertyValue = keyValuePair["iValue"];
}
else if (keyValuePair.Keys.ElementAt(3) == "sValue")
{
var propertyValue = keyValuePair["sValue"];
}
}
Where Root is
class Root
{
[JsonProperty("data")]
public List<JObject> Data { get; set; }
}
With this solution you can always know which property (iValue or sValue) is specified. On the contrary, if you use a model class which has both property names, you wouldn't know which property is specified when the value is null (unless you use additional properties/classes and a custom JsonConverter).
Edit
As Panagiotis Kanavos reminded me, the JObject class implements IDictionary<string, JToken>. So, inside your foreach you could use:
if (element["iValue"] != null)
{
var propertyValue = element["iValue"].Value<string>();
}
if (element["sValue"] != null)
{
var propertyValue = element["sValue"].Value<string>();
}
// Or you can check the property name in the desidered position
var propName = element.Properties().ElementAt(3).Name;
if (propName == "iValue")
{
var propertyValue = keyValuePair["iValue"].Value<string>();
}
else if (propName == "sValue")
{
var propertyValue = keyValuePair["sValue"].Value<string>();
}
Of course you can optimize this code and check for nulls.
I want to split the column creation of a datagrid partially in c# coee and partially in xaml code. my datagrid holds columns starting from 1 - 100 ,which are created using a list in c#.
Now i want to add columns in datagrid whose source is an object field of a class which have 30 properties. Please refer the code.
public partial class MainWindow : Window
{
private List<Test> listTest;
Test obj;
public MainWindow()
{
InitializeComponent();
}
public List<Test> ListTest
{
get
{
return listTest;
}
set
{
listTest = value;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
listTest = new List<Test>();
for (int i = 0; i < 10; i++)
{
obj = new Test();
listTest.Add(obj);
}
this.MyDatagrid.ItemsSource = ListTest; //creating columns in datagrid by list
}
}
public class Test
{
public string m_field1_Test{get;set;}
public string m_field2_Test { get; set; }
public string m_field3_Test { get; set; }
public string m_field4_Test { get; set; }
public string m_field5_Test { get; set; }
public string m_field6_Test { get; set; }
public string m_field7_Test { get; set; }
public string m_field8_Test { get; set; }
public string m_field9_Test { get; set; }
public string m_field10_Test { get; set; }
public Test1 test1obj { get; set; }
public Test()
{
m_field1_Test = "field1";
m_field2_Test = "field2";
m_field3_Test = "field3";
m_field4_Test = "field4";
m_field5_Test = "field5";
m_field6_Test = "field6";
m_field7_Test = "field7";
m_field8_Test = "field8";
m_field9_Test = "field9";
m_field10_Test = "field10";
test1obj = new Test1();
}
}
public class Test1
{
public string m_field1_Test1 { get; set; }
public string m_field2_Test1 { get; set; }
public string m_field3_Test1 { get; set; }
public string m_field4_Test1 { get; set; }
public string m_field5_Test1 { get; set; }
public string m_field6_Test1 { get; set; }
public string m_field7_Test1 { get; set; }
public string m_field8_Test1 { get; set; }
public string m_field9_Test1 { get; set; }
public string m_field10_Test1 { get; set; }
public string m_field11_Test1 { get; set; }
public string m_field12_Test1 { get; set; }
public string m_field13_Test1 { get; set; }
public string m_field14_Test1 { get; set; }
public string m_field15_Test1 { get; set; }
public string m_field16_Test1 { get; set; }
public string m_field17_Test1 { get; set; }
public string m_field18_Test1 { get; set; }
public string m_field19_Test1 { get; set; }
public string m_field20_Test1 { get; set; }
public string m_field21_Test1 { get; set; }
public string m_field22_Test1 { get; set; }
public string m_field23_Test1 { get; set; }
public string m_field24_Test1 { get; set; }
public string m_field25_Test1 { get; set; }
public string m_field26_Test1 { get; set; }
public string m_field27_Test1 { get; set; }
public string m_field28_Test1 { get; set; }
public string m_field29_Test1 { get; set; }
public string m_field30_Test1 { get; set; }
public Test1()
{
m_field1_Test1 = "field1";
m_field2_Test1 = "field2";
m_field3_Test1 = "field3";
m_field4_Test1 = "field4";
m_field5_Test1 = "field5";
m_field6_Test1 = "field6";
m_field7_Test1 = "field7";
m_field8_Test1 = "field8";
m_field9_Test1 = "field9";
m_field10_Test1 = "field10";
m_field11_Test1 = "field11";
m_field12_Test1 = "field12";
m_field13_Test1 = "field13";
m_field14_Test1 = "field14";
m_field15_Test1 = "field15";
m_field16_Test1 = "field16";
m_field17_Test1 = "field17";
m_field18_Test1 = "field18";
m_field19_Test1 = "field19";
m_field20_Test1 = "field20";
m_field21_Test1 = "field21";
m_field22_Test1 = "field22";
m_field23_Test1 = "field23";
m_field24_Test1 = "field24";
m_field25_Test1 = "field25";
m_field26_Test1 = "field26";
m_field27_Test1 = "field27";
m_field28_Test1 = "field28";
m_field29_Test1 = "field29";
m_field30_Test1 = "field30";
}
}
Now i want to display these 30 fields of Test1 class as the columns of datagrid. I already know that this can be done in c# code using LINQ. But in that case i have to create a new list which will have all items of list as well as fields of object. I want to know a way out by which i dnt have to manually do that.
Can somebody know the solution...
Use Databinding to bind your List to your DataGrid.
I'll also recommend the MVVM design pattern to make your app design simplier.
To programatically add a column:
DataGridTextColumn textColumn = new DataGridTextColumn();
textColumn.Header = "First Name";
textColumn.Binding = new Binding("FirstName");
dataGrid.Columns.Add(textColumn);
Check out this post on the WPF DataGrid discussion board for more information.
The simple solution called Reflection!
Lets say you have a class called Bar:
public class Bar
{
string bar1 = "bar1", bar2 = "bar2", bar3 = "bar3";
public string Bar1
{
get { return bar1; }
set { bar1 = value; }
}
public string Bar2
{
get { return bar2; }
set { bar2 = value; }
}
public string Bar3
{
get { return bar3; }
set { bar3 = value; }
}
}
and a class called Foo which has an instance of Bar:
public class Foo
{
string foo1 = "foo1", foo2 = "foo2", foo3 = "foo3";
Bar bar1 = new Bar();
public Bar Bar1
{
get { return bar1; }
set { bar1 = value; }
}
public string Foo1
{
get { return foo1; }
set { foo1 = value; }
}
public string Foo2
{
get { return foo2; }
set { foo2 = value; }
}
public string Foo3
{
get { return foo3; }
set { foo3 = value; }
}
}
and on your Form1 you have a datagridview (i dont post the code of the designer), then you can do it with reflection (the code is unslightly written in Form1)
public partial class Form1 : Form
{
List<Foo> foo = new List<Foo>();
public List<Foo> Foo
{
get { return foo; }
set { foo = value; }
}
public Form1()
{
InitializeComponent();
foo.Add(new WindowsFormsApplication1Foo());
foo.Add(new WindowsFormsApplication1.Foo());
BindingSource source = new BindingSource();
source.DataSource = Foo;
dataGridView1.DataSourceChanged += new EventHandler(dataGridView1_DataSourceChanged);
dataGridView1.DataSource = Foo.ToArray();
}
bool sourceChange = true;
void dataGridView1_DataSourceChanged(object sender, EventArgs e)
{
if (sender is DataGridView)
{
DataGridView dgv = (DataGridView)sender;
if (sourceChange)
{
sourceChange = false;
object source = dgv.DataSource;
Type sourceType = source.GetType();
if (sourceType.IsArray)
{
Array arr = (Array)source;
if (arr.Length > 0)
{
Type elementType = sourceType.GetElementType();
Type myType = CreateCustomType(elementType);
List<object> list = new List<object>();
IEnumerator enumerator = arr.GetEnumerator();
while (enumerator.MoveNext())
{
object myNewTypeInstance=null;
CopyData(enumerator.Current, myType,ref myNewTypeInstance);
list.Add(myNewTypeInstance);
}
dgv.DataSource = list;
}
}
}
}
}
private void CopyData(object oldObj, Type newType,ref object newObj)
{
if(newObj==null)
newObj = Activator.CreateInstance(newType);
Type oldType = oldObj.GetType();
foreach (var item in oldType.GetProperties())
{
string name = item.Name;
object value = oldType.GetProperty(item.Name).GetValue(oldObj,null);
if (item.PropertyType.Namespace != "System")
{
CopyData(value, newType,ref newObj);
}
else
{
newType.GetProperty(name).SetValue(newObj, value, null);
}
}
}
private Type CreateCustomType(Type t)
{
AppDomain myDomain = Thread.GetDomain();
AssemblyName myAsmName = new AssemblyName() { Name = "MyDynamicAssembly" };
AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder myModBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name, myAsmName.Name + ".dll");
TypeBuilder myTypeBuilder = myModBuilder.DefineType("MyDynamicClass", TypeAttributes.Public);
DefineProperties(myTypeBuilder, t);
return myTypeBuilder.CreateType();
}
private void DefineProperties(TypeBuilder tBuilder, Type t)
{
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
foreach (var item in t.GetProperties())
{
Type _t = item.PropertyType;
if (_t.Namespace != "System")
{
DefineProperties(tBuilder, _t);
}
else
{
FieldBuilder customerNameBldr = tBuilder.DefineField("_" + item.Name, item.PropertyType, FieldAttributes.Private);
PropertyBuilder custNamePropBldr = tBuilder.DefineProperty(item.Name, System.Reflection.PropertyAttributes.HasDefault, item.PropertyType, null);
MethodBuilder custNameGetPropMthdBldr = tBuilder.DefineMethod("get_" + item.Name, getSetAttr, item.PropertyType, Type.EmptyTypes);
ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();
custNameGetIL.Emit(OpCodes.Ldarg_0);
custNameGetIL.Emit(OpCodes.Ldfld, customerNameBldr);
custNameGetIL.Emit(OpCodes.Ret);
MethodBuilder custNameSetPropMthdBldr = tBuilder.DefineMethod("set_" + item.Name, getSetAttr,null, new Type[] { item.PropertyType });
ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();
custNameSetIL.Emit(OpCodes.Ldarg_0);
custNameSetIL.Emit(OpCodes.Ldarg_1);
custNameSetIL.Emit(OpCodes.Stfld, customerNameBldr);
custNameSetIL.Emit(OpCodes.Ret);
custNamePropBldr.SetGetMethod(custNameGetPropMthdBldr);
custNamePropBldr.SetSetMethod(custNameSetPropMthdBldr);
}
}
}
}
Sure, you can write it more generic, but it should show you how you could create a "dynamic type" in C#.
Additionally I copy the data of the old structure into the new one and change the datasource to the new list of my dynamic type. Thats all ;)
If you have further questions, i'll try to help you.
The result: