I have high and low but still cannot find the solution to my problem so I gonna post it here.
this is my ViewModel
public class CustomerDetail
{
FirstName {get;set;}
LastName {get;set;}
ID {get;set;}
Email {get;set;}
}
This is how I pass my model to another method in another folder (project), I am actually passing the model to a reportViewer to export as pdf
fileExporter.Initialize(CustomerDetail).Export();
FileExportBase
public abstract class FileExportBase
{
protected string someotherobject {get;set;}
protected string someotherobject1 {get;set;}
protected CustomerDetail customerProfile { get; set; }
public abstract FileExportBase Initialize(params object[] args);
public abstract OperationResult Export();
public class CustomerDetail
{
FirstName {get;set;}
LastName {get;set;}
ID {get;set;}
Email {get;set;}
}
}
This is how I retrieve my data
public override FileExportBase Initialize(params object[] args)
{
someotherobject = args[0].ToString();
someotherobject1 = args[1].ToString();
//This is where I am facing the problem
customerProfile = (CustomerDetail)args[2];
return this;
}
So What should I do so I have to cast the object back to the model type that I initially declared it before passing it as object?
In your circumstance, you would more than likely want to do:
var customer = new CustomerDetail();
customer = args[2];
Now this code has an assortment of problems, for instance you're relying on an index, which may change, thus breaking your code. You may want to potentially refactor, in a manner that accepts your CustomerDetail directly.
public class Initialize<T>(T model, params string[] parameters) where T : class, new()
{
// Do Something
}
With the use of generic's, you can now simply do:
var customer = new T();
customer = model;
This alleviates the risk with the index, while being flexible. Then with a small bit of reflection you can easily ascertain the Properties and Field Index's within your T.
Not sure if this helps you, but based on what I see and understand of your problem this may be valid food for thought. If you add some more clarification, I may be able to better assist.
Related
I have a class below:
public class Employee
{
private IEmployeeDataAccessor _employeeDataAccessor;
public Employee(IEmployeeDataAccessor emda )
{
_employeeDataAccessor = emda ;
}
public int UserId { get; private set; }
public string Username { get; set; }
}
IEmployeeDataAccessor is addscoped in startup.cs.
In dapper I have:
return UserDb.Query<Employee>("sqlcmd for Selecting Employee here").SingleOrDefault();
I get the following error 'A parameterless default constructor or one matching signature is required'
There are no issues if my constructor is parameterless, though.
Does anyone know why?
I think that question is answered in several comments and answer, but I will try to put everything together.
Source of problem: Dapper can't create object of your Employee class, because the only constructor that class has is public Employee(IEmployeeDataAccessor emda) and Dapper doesn't know where to get object implementing IEmployeeDataAccessor interface. And this is no surprise, because this is not a column of the table. Dapper is a mapper of the SQL results to the value objects. And you should not have any additional logic in such value objects.
Solution: problem comes from bad design, you should refactor Employee class. It should not contain any data access logic. It should be just a value class. You can do it as #Asherguru suggested or you can implement it as a record class:
public record Employee
(
int UserId,
string Username
);
Then return UserDb.Query<Employee>("sqlcmd for Selecting Employee here").SingleOrDefault(); should work.
And this line, as far as I understood, should go to class implementing IEmployeeDataAccessor i.e. this is a data access logic to get Employee value objects.
Employee is model. So you can use properties only. Do not add any others.
Remove
private IEmployeeDataAccessor _employeeDataAccessor;
public Employee(IEmployeeDataAccessor emda )
{
_employeeDataAccessor = emda ;
}
Use this instead:
public class Employee
{
public int UserId { get; private set; }
public string Username { get; set; }
}
I want to create a method that displays the information contained in an object, that will work dynamically, with any object. I'm having trouble handling properties that are other custom classes. In the example below the Person has Phones and Occupations which both are other classes. When the data is displayed, the value on the screen currently is:
TestReflection.Person
Name: Mary
Phones: TestReflection.Phones
Occupations: TestReflection.Occupations
It just displays the name of class, like TestReflection.Phones, rather than the data inside that object.
How can I change this code to show information like this instead?
TestReflection.Person
Name: Mary
Phones:
TestReflection.Phones
Type: 1
Number: 555XYZ
Occupations:
TestReflection.Occupations
Type: 5
Description: Secretary
Here is my code:
class Program
{
static void Main(string[] args)
{
List<Person> listPeson = new List<Person>();
var person1 = new Person();
person1.Name = "Mary";
person1.Phones = new Phones { new Phone { Type = 1, Number = "555XYZ" } };
person1.Occupations = new Occupations {new Occupation { Type = 5, Description = "Secretary" }};
listPeson.Add(person1);
DynamicExport(listPeson);
Console.ReadLine();
}
public static void DynamicExport<T>(List<T> listReg)
{
for (int i = 0; i < listReg.Count; i++)
{
Console.WriteLine(listReg[i].GetType());
foreach (var item in listReg[i].GetType().GetProperties())
{
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
}
}
}
}
class Person
{
public string Name { get; set; }
public Phones Phones { get; set; }
public Occupations Occupations { get; set; }
}
class Phones : List<Phone> { }
class Phone
{
public int Type { get; set; }
public string Number { get; set; }
}
class Occupations : List<Occupation> { }
class Occupation
{
public int Type { get; set; }
public string Description { get; set; }
}
I made some edits to your question - I hope I understood you correctly.
If you want to export data
If your question is really about displaying data, then there are better ways to do it than creating your own export method. The format you are trying to display looks similar to YAML. There's also JSON and XML. Using one of these libraries is probably better than writing your own method:
YamlDotNet NuGet package
Json.NET NuGet Package
System.Xml.Serialization.XmlSerializer class
If you want to learn more about reflection
Maybe you're interested in learning more about reflection, and the export is just an example to play around with it. In that case, let's look at this line:
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
$"{item.GetValue(listReg[i], null)}" ends up calling person1.Phones.ToString(). The default behavior of ToString just displays the type name. You could override that behavior, like this:
class Phones : List<Phone>
{
public override string ToString()
{
return Program.DynamicExportToString(this);
// ... where DynamicExportToString is a modified version of DynamicExport that
// builds and returns a string rather than sending it directly to the Console.
}
}
Maybe you want to be able to handle any class, even when you cannot override ToString in all of the classes you might export. Then you will need to put some additional logic in the DynamicExport method, because...
$"{item.Name}: {item.GetValue(listReg[i], null)}"
... doesn't work for every situation. We need to display different things depending on the type of the property.
Consider how you want to handle null values. Maybe something like $"{item.Name}: <null>"
Use your existing $"..." code if the type is...
a primitive type.
DateTime
String
... or a Nullable<> of one of those types.
If the type implements IEnumerable, loop over the contents of the collection and recursively call your export code for each element.
It's important to check for this interface after you've checked if the type is a String, because String implements IEnumerable.
Otherwise, recursively call your export code on this value.
When you call your export code recursively, it would be wise to guard against infinite loops. If the object you're trying to export contains a circular reference - you could quickly wind up with a StackOverflowException. To avoid this, maintain a stack of objects that have already been visited.
I think the above advice is generally applicable whenever you're using reflection to traverse an object graph - whether it's for serialization or any other purpose.
I hope this helps!
I'm trying to create a collection (list<> or IEnumerable<>) of a custom objet "InventorAttribue" that has 2 properties; Name and Value.
The "Value" property can be of various type so I thought of coding this object like this:
public class InventorAttribute<T> {
public InventorAttribute (string name, T value) {
Name = name;
Value = value;
}
public string Name { get; set; }
public T Value { get; set; }
}
Further I plan to use an "AttiributeSet" class to represent the final Autodesk Inventor AttributeSet to be stored in an Inventor's object. Here is the class and where my question stands, because of course, this code does not work as the type 'T' cannot be found (!)
public class AttributeSet
{
public AttributeSet(string category, string name {
Name = name;
Attributes = new List<InventorAttribute<T>>();
}
public string Category { get; set; }
public string Name { get; set; }
public List<InventorAttribute<T>> Attributes { get; set; }
public void AddAttribute(string name, T value){
Attributes.Add(new InventorAttribute<T>(name,value));
}
}
Question:
How can I manage to write this code, and being able to pass the "InventorAttribute.Value" type only at run time through the "AddAttribute" method.
Thanks in advance for greatly appreciated help.
Your AttributeSet class should be also parametrized:
public class AttributeSet<T>
NOTE: you cannot store InventorAttribute<T> parametrized with different T types in Attributes collection. Even if you could do that, how would you consume such collection? You will need to cast Value for each attribute to appropriate type. You will not have any benefits of having generic class here. So create non-generic InventorAttribute which will store values in property of object type.
You're probably imagining some form of inheritance. It doesn't exist here.
An InventorAttribute<string> is not a subclass of InventorAttribute<T>. Nor is it a subclass of InventorAttribute<object> (I mention this since it's usually people's next attempt to define the collection's item type). Each constructed generic type is effectively independent1.
If applicable, you may be able to introduce a new base class:
public abstract class InventorAttribute {
public string Name { get; set; }
public InventorAttribute (string name) {
Name = name;
}
}
public class InventorAttribute<T> : InventorAttribute {
public InventorAttribute (string name, T value) : base(name) {
Value = value;
}
public T Value { get; set; }
}
And you can now declare your collection to be of non-generic type InventorAttribute. But now you cannot access the Values until you cast to the more specific type.
1So far as the type system is concerned. As an implementation detail, the system is able to cleverly JIT only a single version of each method body that is applicable for all reference types. But that doesn't have any visible impact in the type system.
I have a client that sends SomeComplexObject to a webservice.
I want the webservice to be unaware of the structure of the data,
so want the data to be deserialized to a dynamic that is passed then to a method that knows how to deal with it.
I use System.Web.Helpers.Json.Encode(), Decode() methods
and I have a problem when SomeComplexObject contains a collection.
It is deserialized to a DynamicJsonArray but it is somehow not accessible
for the consumer of the data.
These are Model types.
public class Aaa
{
public Bbb B { get; set; }
public List<Ccc> Cccs { get; set; }
}
public class Bbb
{
public long Key { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Ccc
{
public string Name { get; set; }
public int Age { get; set; }
}
Let us say that I have a myAaa object of type Aaa with a property of type Bbb
and a list of 42 Ccc objects.
Now:
var MyAaaSerialized = System.Web.Helpers.Json.Encode(MyAaa);
Then I send it and then:
var MyAaaDeserialized = System.Web.Helpers.Json.Decode(MyAaaSerialized);
And finally:
This is the code of the consumer. Email and emailService are Postal classes.
dynamic email = new Email(template);
email.Data = MyAaaDeserialized;
email.User = user;
this.emailService.Send(email);
I can see in the sent email that properties of B object are accessible.
However, properties of members of Cccs list are not.
Of course with email.Data = MyAaa; everything works OK.
Is there any dead simple way to serialize/deserialize a complex object that contains a collection?
Thanks in advance,
If I haven't misunderstood your question, it can be resolved by using the Decode overload that uses generics as below. Just specifying the type (Aaa in this case) worked for me in the code below and it printed all ccc properties correctly:
var myAaa = new Aaa()
{
B = new Bbb() { Name = "someone", DateOfBirth = DateTime.Today.AddYears(-20) },
Cccs = Enumerable.Repeat<Ccc>(new Ccc() { Age = 20, Name = "someone else" }, 42).ToList()
};
var MyAaaSerialized = System.Web.Helpers.Json.Encode(myAaa);
var MyAaaDeserialized = System.Web.Helpers.Json.Decode<Aaa>(MyAaaSerialized);
dynamic data = MyAaaDeserialized;
foreach (Ccc newCccs in data.Cccs)
{
Console.WriteLine($"{newCccs.Name}\t{newCccs.Age}");
}
Ok, I was wrong. System.Web.Helpers.Json.Encode() and Decode() work correctly. I`ll tell you what happened:
I used to use Postal mailsender locally, i.e. I used to take my super-complex POCO object and pass it as a dynamic to Postal Email class. And it worked.
Now I want some webservice to send emails so I need to pass serialized data to it. I have sent a test email and I`ve seen that values of some of the fields are not present in the email message, but 'DynamicJsonObject' is there instead. So I assumed (wrong), that the structure of deserialized object is somehow corrupted.
But the truth is different:
The missing data is of complex type, say
public class Money
{
public double Amount {get; set;}
public string Currency {get; set;}
public override ToString()
{...}
}
So when I asked in Razor email template for:
#item.Prices.SalePriceGross locally,
it somehow must have used the ToString() method.
And of course in a serialized / deserialized object there is no knowledge about the ToString() method.
I am going to need to expose a property with the string I want to display or (better) access Amount and Currency explicitly and process them in Razor email template.
Thank you for help.
I have a large collection of automatically generated objects. Although they are all of different, non-related classes, all of the objects share some basic properties (name, id, etc.). I do not control the generation of these objects, so unfortunately I cannot take the ideal approach of implementing an interface. I would like to create a method in which I pass an arbitrary one of these objects and do something using these common properties.
The general idea would be something like:
someObj a = new someObj();
a.name = "sara";
diffObj b = new diffObj();
b.name = "joe";
string phrase = string.Format("I am with {0} and {1}",
getName(a), getName(b));
private string getName(object anyObjWithName)
{
return anyObjWithName.name;
}
though naturally this does not work.
I thought a generic method might hold the answer, but the only way I can see to call it with the current type is using genericMethod.Invoke , which still carries the same issue of not being able to resolve the properties of the passed object in the method. This is unlike Calling generic method with a type argument known only at execution time or How to call generic method with a given Type object? where only the type, or properties of the type, are used in the method, as opposed to properties of the object.
I am aware that this would be (very) prone to error, but I can guarantee that all objects encountered will have the common properties being manipulated.
I can guarantee that all objects encountered will have the common properties being manipulated
If that's the case, you can use dynamic:
private string getName(dynamic anyObjWithName)
{
return anyObjWithName.name;
}
Be aware that using any object that does not have a name property will not fail until run-time.
If you want to add a little bit of safety you can catch the RuntimeBinderException that gets thrown if the property does not exist:
private string getName(dynamic anyObjWithName)
{
try {
return anyObjWithName.name;
}
catch(RuntimeBinderException) {
return "{unknown}";
}
}
If you're unhappy with the performance using dynamic as mentioned by D Stanley, you could always try FastMember.
All you need to know to start using it is pretty much shown in the first 2 code examples.
You are creating a Rube Goldberg device there. You should just have all your data objects classes implement a single interface, then you can work on that. Much simpler and less error prone than fiddling with reflection.
The very fact that a lot of objects have common properties but don't share the same ancestry, on in the very least a common interface, shows that something is wrong with your design. Do rethink it.
Multiple ways to accomplish this, simplest probably is to create Interface and declare common methods there, have your object implement it, then change "getName" method take interface object
private string getName(IMyInterface anyObjWithName)
{
return anyObjWithName.name;
}
The correct way to do this is with an interface, if you own the types that you're working with
public interface IEntity
{
int ID { get; set; }
string Name { get; set; }
}
public class TypeOne : IEntity
{
public int ID { get; set; }
public string Name { get; set }
public string BespokePropertyOne { get; set;}
}
public class TypeTwo : IEntity
{
public int ID { get; set; }
public string Name { get; set; }
public float BespokePropertyTwo { get; set; }
}
static void Main(string[] args)
{
List<IEntity> entities = new List<IEntity>();
entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" });
entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f });
foreach (IEntity entity in entities)
{
Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name);
}
}
This answer was written before the edit to the question stating that interfaces weren't possible in this case. Perhaps it can help someone else reading this question.
Interface:
interface Iname
{
string Name { get; set; }
}
Use interface:
class A : Iname
{
public string Name { get; set; }
}
class B : Iname
{
public string Name { get; set; }
}
The method:
string GetName(Iname o)
{
return o.Name;
}
Use:
A a = new A { Name = "First" };
B b = new B { Name = "Last" };
Text = GetName(a) + " " + GetName(b);