Cast one object to another - c#

I have got two user defined CLR objects which are identical and only differ in name, for example this code:
public class Person
{
public string Name { get; set; }
public string Address { get; set; }
public string LastName { get; set; }
public string ETC { get; set; }
}
public class PersonData
{
public string Name { get; set; }
public string Address { get; set; }
public string LastName { get; set; }
public string ETC { get; set; }
}
I know that this can be done by creating an object from either of these CLR's and than pass all properties one by one to it.
But is there another way? I got a few CLR's that are pretty big, 15+ properties.
[Edit]
Some more context. The classes where already there. I generated a model from the database using EntityFramework. The database has almost the exact same structure as the classes.
Also, it's a lot of code that was already there. These classes also inherit from several interfaces etc. Refactoring now is not an option, so i'm looking for an easy fix for now.

Assumming you don't want both classes to inherit from an interface you can try Automapper it's a library for automatically mapping similar classes.

If you are the author of those classes, you could have them implement the same interface, or even better, inherit from the same class. That way you don't have to copy values from one to another - just use a reference to their parent type. Remember, having couples of unrelated types which have exactly the same members is a sure sign that you need to rethink your design ASAP.

And, just for the sake of completeness, here's what it looks like with reflection:
var a = new Person( ) { LastName = "lastn", Name = "name", Address = "addr", ETC = "etc" };
var b = new PersonData( );
var infoPerson = typeof( PersonData ).GetProperties( );
foreach ( PropertyInfo pi in typeof( Person ).GetProperties( ) ) {
object value = pi.GetValue( a, null );
infoPerson.Single( p => p.Name == pi.Name )
.SetValue( b, value, null );
}

Related

C# 8 Non-Null Reference Types for Database models with foreign key

I'am trying to implement non-null-reference-types in my project https://dev.to/integerman/safer-code-with-c-8-non-null-reference-types-4f2c. I like it, but have a question regarding database models and constraints.
Is there a way to say that value X is never NULL because it's not nullable in the database?
For example:
public class Person
{
public int Id { get; set; };
public string Name { get; set; } = "No name set";
public IEnumerable<Dog> Dogs { get; set; } = new List<Dog>();
}
Person.Name is nullable=false in the database. Is there a way I can say that this property is never null? Now I have to set a default value.
public class Dog
{
public int Id { get; set; }
public string Name { get; set; } = "Noname";
public int PersonId {get; set; }
public Person Person { get; set; }
}
Here I wonder the same about Person from Dog. This is a foreign key constraint in the database, A Dog can't exist without an Person(owner). Is there a way to say that; I know that this value is never null trust me or something like that ?
[Update]
Is there a way to say that value X is never NULL because it's not nullable in the database?
Yes, see Pavel Anikhouski answer
Should you do it:
No, see TomTom s answer
The best solution I think is #canton7 last comment. He links to this; https://learn.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#non-nullable-properties-and-initialization
Another reason:
public IEnumerable<Dog> Dogs { get; set; } = new List<Dog>();
If you are trying to get a Person like this:
var person = _dbcontext.persons.Single(x => x.Name == "John").ToList();
Console.log($"John has {person.Dogs.Count} dogs")
This will output 0 everytime, because we forgot to .Include(x => x.dogs).
So it's wrong.
No, and it makes no sense. Particularly in the area of database entities - you MUST allow null because otherwise you can not load the object without automatically loading the related object. Which ultimately will force you to load a lot of data that you may not want for a simple query.
Dogs CAN exist without Person. Not on db level, but I can ask for a list of all dogs and not be interested in the owners at this point.
You can use null forgiving operator ! for that
public Person Person { get; set; } = default!;
As it pointed in comments, you should carefully use it, since it works with any type (value or reference one) and you can use null! as well

C# - Make copy of a big class (model) by value not reference

I do project by asp.net core mvc. when I do copy of the model and change its values, the values of the original model is changing too because it made copy of the model by reference, so the value place in data is same.
I need way that I can do copy for values of the model doesn't connect with original model.
Your question could have many answers depending on the encapsulation of the object you are copying. I will assume you are operating on a low level entity object rather than an object that is supposed to encapsulate it. If this assumption is incorrect and it is a higher level object that encapsulates entity operations I will gently remind you of good programming practices: Martin Fowler - TellDontAsk.
For the answer I will use the class below to illustrate:
public class Student
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
I assume what is happening is something similar to the following:
Student john = new Student();
Student jane = john;
jane.FirstName = "Jane"; // now john.FirstName == "Jane"
What you are going to need to do is clone the object to a new object instance. There are various ways to do that.
Option #1:
// Create a new entity object manually assigning each value
// from the first object to the value in the new object.
var clonedStudent = new Student
{
Id = john.Id, // Copies value not reference
LastName = john.LastName, // string is immutable this OK
FirstName = john.FirstName, // string is immutable this OK
// DateTime is a struct I think so it should pass value
EnrollmentDate = john.EnrollmentDate // Verify my assumption
};
Option #2:
// Make Student class partial and extend it with clone method.
// This is helpful for generated entities not using the code-first approach.
public partial class Student
{
public int Id { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
public partial class Student
{
public Student Clone()
{
return new Student
{
Id = Id, // Copies value not reference
LastName = LastName, // string is immutable this OK
FirstName = FirstName, // string is immutable this OK
// DateTime is a struct I think so it should pass value
EnrollmentDate = EnrollmentDate, // Verify my assumption
};
}
}
To use it you would write:
Student clonedStudent = john.Clone();
Option #3: You could use a NuGet package that does the cloning for you. There are various ones that do that. A quick google search pulled up this one for me. DeepCloner
If you are copying objects from one type to another you might want to use AutoMapper.
NOTE: Also, based on your question a good knowledge of how entity framework handles changes might be useful.
Tracking vs. No-Tracking Queries
Hopefully that helps.
Happy coding!!!
option 1 use AutoMapper
option 2 create Copy using Reflection
public class PropertyCopier<TParent, TChild> where TParent : class
where TChild : class
{
public static void Copy(TParent parent, TChild child)
{
var parentProperties = parent.GetType().GetProperties();
var childProperties = child.GetType().GetProperties();
foreach (var parentProperty in parentProperties)
{
foreach (var childProperty in childProperties)
{
if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType)
{
childProperty.SetValue(child, parentProperty.GetValue(parent));
break;
}
}
}
}
}

neo4jclient to POCO objectGraph

I'm starting to play with neo4jclient, and although I have found the wiki pages which show pulling out nodes etc. I'm a little confused how to take a slighlty more complex structure from the graphDB and reconstruct it into my POCO objects.
As an example, say I have the following graph:
And I have the following classes:
public class Person
{
public string name { get; set; }
public List<Sport> watches { get; set; }
public List<Sport> plays { get; set; }
}
public class Sport
{
public string name { get; set; }
public GoverningBody governingBody { get; set; }
}
public class GoverningBody
{
public string name { get; set; }
}
Could somebody give me the c# code I would need to use to pull out "David", along with the sports he plays and the governing body for that sport. The end goal would be that the Person, Sport(s) and GoverningBody objects would all be populated so that I can use them as normal within the C# code.
Thanks
David
This is a very quick solution - you can create (in effect) an anonymous type in the With statement that you can parse into a result, for example, with the addition of a SportAndGovern class:
public class SportAndGovern
{
public Sport Sport { get; set; }
public GoverningBody Govern { get; set; }
}
You can execute this Cypher (I've not used parameterised stuff, you should) to get a person with a list of the sports they play - you do end up with duplicated Governing Bodies coming back, i.e. one for each Sport the person watches.
var query = Client.Cypher
.Match("(p:Person {Name:'David'})")
.OptionalMatch("(p)-[:PLAYS]->(s:Sport)<-[:GOVERNS]-(g:GoverningBody)")
.With("p, Collect(Distinct {Sport: s, Govern: g}) as sportAndGovern")
.Return((p, sportAndGovern) => new
{
Person = p.As<Person>(),
SportAndGovern = Return.As<IEnumerable<SportAndGovern>>("sportAndGovern")
});
This code should get you started
var userQuery = client.Cypher
.Match("(n:Person { name: 'David'})-[:PLAYS]->(s:Sport)")
.Return((n, s) => new
{
Peep = n.As<Person>(),
Sports = s.CollectAsDistinct<Sport>()
})
.Results
.FirstOrDefault();
var david = userQuery.Peep;
david.plays = userQuery.Sports.ToList();
SO looking at this in a little detail there are some points to note.
Firstly, client refers to an instance of Neo4jClient and assumes that you have previously initialised this.
Secondly, the query assumes that you only have one Person node where the name property has a value of "David".
The Return clause projects the query results into an Anonymous type. It uses the CollectAsDistinct method to return an IEnumerable<Sport> collection. This translates into COLLECT(distinct s) in Cypher to collect the Sport nodes.
Finally, it then users the anonymous type to build up a Person object to return.

Convert an Entity to a extended class based on original class?

I have a Employee class which is an Entity Framework code first class representing an Employee. I would like to create a view model based on the original Employee class and then populate that class from a linq query to my EF context.
public class EmployeeVM : Employee
{
public List<DepartmentSelect> Departments { get; set; }
}
EmployeeVM employee = context.Employees.Find(id);
I get the error "cannot implicitly convert type Employee to EmployeeVM."
Is there a simple way to do this rather then creating a new object and foreaching every parameter into the equivalent in the new class?
be nice if coding had an easy button, but it is what it is.. You could write the code one time and reuse it if you want. You could use AutoMapper and deal with some of the headaches that come with that. Your best bet would just be to write it yourself and maybe catch some errors if your context changes..
Without a mapper you could just add a static func to your viewmodel that will take an Employee object and create an EmployeeVM and use this in your context queries.
public class EmployeeVM
{
public EmployeeVM()
{
Departments = new List<DepartmentSelect>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Dob { get; set; }
public List<DepartmentSelect> Departments { get; set; }
public static Func<Employee, EmployeeVM> FromEntity = item => new EmployeeVM() {
Id = item.Id,
FirstName = item.FirstName,
LastName = item.LastName,
Dob = item.Dob
};
}
// get single EmployeeVM
var eVm = EmployeeVM.FromEntity(context.Employees.Find(id));
// get List<EmployeeVM
var eVmList = context.Employees.Select(EmployeeVM.FromEntity).ToList();
This isnt recommended but if Employee is a partial class you could always just extend it by adding another partial class in the same namespace.
public partial class Employee
{
//Add Extra Properties
public List<DepartmentSelect> Departments { get; set; }
}
you maybe want to use Automapper. http://automapper.org/
Tools like AutoMapper are designed to ease the burden of having a bunch of property-mapping code. You can also just serialize the first object and deserialize it into the second one.
I should probably mention, though, that this is probably a misuse of inheritance. Is there a reason you can't just put your Employee entity directly on your EmployeeVm as a property? Beyond that, Arash is right in pointing out that ViewModels should generally not be tightly coupled to your data model.

Can two Lists be joined if types are different like List<T> and List<G>?

I have two different classes like below:
public class ProductDto : IDto
{
public int Parentproductid { get; set; }
public string Parentproductnumber { get; set; }
public int Supplierid { get; set; }
//More properties
}
public class OrderItemsDto : IDto
{
public int Poid { get; set; }
public System.DateTime Createddate { get; set; }
public int Parentproductid { get; set; }
public int Variationid { get; set; }
public System.DateTime Deliverydate { get; set; }
//More properties
}
What I need to do is basically to join List<OrderItemsDto> and List<ProductDto> on parentproductid (like if they were database tables) and produce another list.
I have tried using Union like below:
List<ProductDto> productParents = productManager.getList();
List<OrderItemsDto> orderItemsList = ordermanager.getList();
gvPurchaseOrderItems.DataSource = orderItemsList.Select(o => o.Parentproductid).Union(productParents.Select(pp => pp.parentproductid));
But this only gives me a result of the List<int> parentproductids found in both of the lists where I need something that has properties ( columns in above case ) from both classes. And I couldn't find how to select multiple properties with the Select extension method ( I am kind of a beginner )
I know I can create a new class and map the properties manually, but I really am curious about how to do this with lambda expressions or linq. Is this possible, I would really appreciate if you could point me a direction. Thanks.
You can use Select to create an anonymous type for your query:
orderItemsList.Select(o => new { o.Parentproductid, o.Variationid, /* etc */ })
So, in your case:
gvPurchaseOrderItems.DataSource = orderItemsList
.Select(o => new { o.Parentproductid, o.Variationid, /* etc */ })
.Union(productParents
.Select(pp => new { pp.Parentproductid, pp.Variationid, /* etc */ }));
As both ProductDto and OrderItemsDto implements IDto interface, no need for anonymous types as you tried, this should work:
gvPurchaseOrderItems.DataSource = orderItemsList.Cast<IDto>().Union(productParents);
All the common properties (i.e. defined in the interface I assume) will be there, along with the type-specific ones using another casting.
According to your comment (and the fact that it's DTOs, silly me), you should definitely go for Dave Bish's answer.
If you want to join two lists on Parentproductid into one list, try:
var result = from pm in productManager
join om in ordermanager on pm.Parentproductid equals om.Parentproductid
select new { pm.Parentproductid, pm.Parentproductnumber,
pm.Supplierid, om.Poid /* more properties */ };

Categories

Resources