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

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;
}
}
}
}
}

Related

Are DBSet<> object's ids set when assigning one to the other?

I have two entities with relation one-many in DB and corresponding classes generated by reverse engineering the database.
public partial class Assets
{
public int AssetsId { get; set; }
public string Description { get; set; }
public int? PersonId { get; set; }
public virtual Persons Person { get; set; }
}
public partial class Persons
{
public Persons()
{
Assets = new HashSet<Assets>();
}
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public virtual ICollection<Assets> Assets { get; set; }
}
When I assign one entity to the other do the corresponding ID's are set automatically (i am curious if they are set before SaveChanges is called, so i could use it without committing all current changes).
static void Main(string[] args)
{
Context c = new Context();
var a = new Assets();
a.AssetsId = 1;
var p = new Persons();
p.PersonId = 2;
c.Add(a);
c.Add(p);
a.Person = p; //does it set a.PersonId = 2
p.Assets.Add(a); //does it set a.PersonId = 2
c.SaveChanges();
}
(You can just check the answer to your question in the debugger or with a simple test using your provided code.)
When I assign one entity to the other do the corresponding ID's are set automatically [before SaveChanges is called later?]
No, they are not. The reason is, that your navigation properties here are really just POCO properties (meaning simple .NET properties). The Assets.Persons property is really just a property of type Persons and the Persons.Assets property contains really just the HashSet<Assets> instance that you assigned in the constructor. There is no magic here.
The IDs are being synchronized automatically during change tracking, which happens for example when SaveChanges() is called, but can also manually be triggered by calling context.ChangeTracker.DetectChanges().
As a side note, be aware that you should not set IDs in your code explicitly, if you are using identity/auto increment columns for primary keys, because this can lead to exceptions when saving (depending on the database server being used), due to EF Core trying to insert the explicitly assigned ID to the identity/auto increment column (e.g. this is not allowed by default for SQL Server).

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.

Why does my ASP.NET MVC 4 application create new entities instead of updating the old ones?

EDIT: The solution I selected probably wasn't the best, but it definitely worked. I'll be going through my code over the next week (once this project is done) and I'll update my question when I understand what went wrong.
I'm using the ASP.NET MVC 4 framework with Entity 5. Here's some code:
The class to be instantiated and saved (fresh) in the database:
public class ClassCancellation
{
[Key]
public int Id { get; set; }
public Faculty Professor { get; set; }
public DateTime CancelledOn { get; set; }
public Course Course { get; set; }
[Required]
public ClassDate ClassCancelled { get; set; }
public Message CancellationMessage { get; set; }
[Required]
public List<Student> Students { get; set; }
}
It's mapped from the viewmodel called CancellationFull (with AutoMapper):
public class CancellationForList
{
[Key]
public int Id { get; set; }
public CourseForList Course { get; set; }
public ClassDateForList ClassCancelled { get; set; }
}
public class CancellationFull : CancellationForList
{
public CancellationFull()
{
this.Students = new List<StudentForList>();
}
public FacultyForList Professor { get; set; }
public MessageForList CancellationMessage { get; set; }
public DateTime CancelledOn { get; set; }
public List<StudentForList> Students { get; set; }
}
This is the repo method that turns a CancellationFull into a ClassCancellation and then saves it to the database:
public CancellationFull createClassCancellation(CancellationFull c)
{
ClassCancellation newCancellation = Mapper.Map<ClassCancellation>(c);
dc.ClassCancellations.Add(newCancellation);
dc.SaveChanges();
return Mapper.Map<CancellationFull>(dc.ClassCancellations.FirstOrDefault(cc => cc.Id == newCancellation.Id));
}
Why, for the love of god why, does the database create new objects for Faculty and Course when the Id (primary key) of each's existing entity counterpart is provided? It might also be doing the same with Student objects but I haven't looked that closely.
Before the ClassCancellation instance is saved to the database the debugger shows that it's attributes Professor of type Faculty and Course of type Course have the correct primary key - that is, the primary key of the already existing entities of those types that I'm trying to update with a reference to the new ClassCancellation object.
Driving me nuts. Feel free to ask for clarification!
EDIT:
Here's the logic where the CancellationFull viewmodel is constructed from form data and viewmodels about existing objects retrieved from their respective repos:
newCancellation = new CancellationFull();
newCancellation.CancelledOn = DateTime.Now;
newCancellation.ClassCancelled = repoClass.getClassDateForListById(Int32.Parse(classIds[i]));
newCancellation.Course = repoCourse.getForList(newCancellation.ClassCancelled.Course.Id);
newCancellation.CancellationMessage = repoMessage.getMessageForList(newMessage.Id);
newCancellation.Professor = repoFac.getFacultyForList((int)Session["facId"]);
var students = repoStudent.getStudentsForListByCourse(newCancellation.Course.Id);
foreach ( var student in students )
{
newCancellation.Students.Add(student);
}
repoCancellation.createClassCancellation(newCancellation);
Here's an example of one of those repo methods (the rest are very similar):
public CourseForList getForList(int? id)
{
return Mapper.Map<CourseForList>(dc.Courses.FirstOrDefault(c => c.Id == id));
}
What I find the easiest solution is when updating a model, clear any related entities, then re add them.
ie:
newCancellation.Students.Clear();
foreach ( var student in students )
{
newCancellation.Students.Add(student);
}
Try using Attach() instead of Add()
dc.ClassCancellations.Attach(newCancellation);
dc.SaveChanges();
Add() is used for new objects that do not already exist in the database. Attach() is used for creating relationships to entities that already exist in the database.
EDIT
Without seeing your code, the best solution I can recommend to attach is to create a 'stub' instance and then attach that to your newCancellation:
var existingCourse = new Course{ Id = newCancellation.ClassCancelled.Course.Id };
db.Courses.Attach(existingCourse);
newCancellation.Course = existingCourse;
The problem is that you have multiple contexts, or units of work. When you add the newCancellation to the dc context, it also adds any related entity in the object graph that is not tracked in the dc context. I think your best option is:
dc.ClassCancellations.Add(newCancellation);
dc.Entry(newCancellation.Course).State = EntityState.Unchanged;
dc.Entry(newCancellation.Faculty).State = EntityState.Unchanged;
See Julie Lerman's article on this issue for an explanation and other options.
In my opinion, EF should recognize entities that have autonumbered keys and not insert them if the key is assigned.

Cast one object to another

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 );
}

Categories

Resources