Auto populate properties in class - c#

Suppose that I have a class name User, is there any ways to populate the class properties and default value in Visual Studio as follow. I feel it is tedious to repeat typing the properties again and again.
Probably there is default shortcut to do this in Visual Studio or any extension that can do this?
Case 1
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
);
case 2
var user = new User();
user.UserID = "", // auto populate with type default value
user.Username = "", // auto populate with type default value
........ // auto populate all properties inside User class
........ // auto populate all properties inside User class

I'm not quite sure what are you trying to achieve here
var user = db.User.Where(x => x.UserID).Select(o => new User
{
UserId = o.UserId, // auto populate
Username = o.Username, // auto populate
........ // auto populate all properties inside User class
}
since both new User and db.User are the same type of class/entity. But let's say you have an entity model User and a DTO/View model called UserViewModel and you want automatically to map the values of User to UserViewModel you can use automapper https://docs.automapper.org/en/stable/Getting-started.html Example this is the Entity definition
public class User
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
And we have a UserViewModel to which you want to map the data from User
public class UserViewModel
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public int Age {get; set;}
}
With AutoMapper you can do this
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>();
}
and then you can use the mapper configuration like this
var user = db.User.Where(x => x.UserID).First();
var userViewModel = configuration.CreateMapper().Map<UserViewModel>(user);
This will automatically populate the property values of User to UserViewModel. Alternatively you can have a view model like this
public class UserViewModel
{
public int Id {get; set;}
public string FullName {get; set;}
public int Age {get; set;}
}
This time not all properties of User and UserViewModel match one to one and you will have to set a custom mapper configuration i.e.
var configuration = new MapperConfiguration(config =>
{
config.CreateMap<User, UserViewModel>()
.ForMember(uvm => uvm.FullName, o => o.MapFrom($"{u.FirstName} {u.LastName}") );
}
This way you are configuring the automatic User to UserViewModel mapping to map the FullName property value by concatenating FirstName and LastName from User

Well I have build a library that could solve this problem for you, called FastDeepCloner
public class User
{
public virtual string Name { get; set; } = "sdjh";
public virtual int PasswordLength { get; set; } = 6;
public Circular Test { get; set; } = new Circular();
}
public class CloneToTest
{
[FastDeepCloner.FastDeepClonerColumn("Name")]
[FastDeepCloner.FastDeepClonerColumn("LastName")] // Or
public string FullName { get; set; }
// You see here the type could be difrrent then the orginal type.
// FastDeepCloner will try to convert it, if it fail then a default value will be inserted insted
public string PasswordLength { get; set; }
// You could add a path insted, remember this only work on none list items.
[FastDeepClonerColumn("Test.myBar.Id")]
public int Id { get; set; }
public Circular Test { get; set; }
}
Now simple use
var user = new User() { Name = "alen toma" };
var cloneTo =new CloneToTest();
FastDeepCloner.DeepCloner.CloneTo(user, cloneTo);
Assert.AreEqual(user.Name, cloneTo.FullName);

Related

Get prev, current, next on MongoDB c#

I have a Collection Students which Stores registered students data in MongoDB. It has StudentName, SaveDate etc. fields. I am displaying the details to my website from my .net framework WebAPI.
What I want to do is Get StudentDetails with prev next student ID's by the order of SaveDate.
My current query gets Student details by student id from below query,
var collection = StudentDB.Collection<Student>("StudyMaterials");
var curfilter = Builders<Student>.Filter.Eq(x => x.studentID, studentID);
studentDetails= collection.Find(curfilter).FirstOrDefault();
This is my Student.cs class,
public class Student
{
[BsonRepresentation(BsonType.String)]
public Guid StudentID {get; set;}
public string StudentName {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid NextStudentID {get; set;}
[BsonRepresentation(BsonType.String)]
public Guid PrevStudentID {get; set;}
}
I want to use aggregation but don't know how to sort by multiple sort definitions on the aggregation framework.
First of all, I don't think you need to keep the students like that. I suggest the following:
public class Student
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public DateTime SaveDate { get; set; }
}
You need to make sure that the documents are sorted by SaveDate. For instance, when inserting new students into the database you can do it like this:
var student = new Student
{
Name = "Sara",
SaveDate = DateTime.UtcNow
};
context.Students.InsertOne(student);
Now, you can get the documents with Skip and Limit:
var skip = 0;
var limit = 1;
var context = new MongoContext();
var filter = Builders<Student>.Filter.Empty;
var result = context.Students.Find(filter).Skip(skip).Limit(limit).FirstOrDefault();
Increment skip to get next and so on.

Can't convert from model to viewmodel, using AutoMapper

I'm sure this is dead simple, but I need help ... I'm trying to display a single product in a view, and have this query:
var Product = await (from p in _context.Products
where p.Id == id
select p).FirstOrDefaultAsync();
Then I try to map the result to my viewmodel and return it to the view:
var VMProduct = _mapper.Map<ViewModelProduct, Product>(Product);
return View(VMProduct);
However, I get a build error on the mapping:
"Error CS1503 Argument 1: cannot convert from 'MyStore.Models.Product'
to MyStore.Models.ViewModels.ViewModelProduct'"
This is my entity model,
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public List<ProductInCategory> InCategories { get; set; }
}
and this is my viewmodel
public class ViewModelProduct
{
public int Id { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public decimal Price { get; set; }
public int SortOrder { get; set; }
public IEnumerable<ViewModelCategoryWithTitle> Categories { get; set; }
public ViewModelProduct(ProductInCategory pic)
{
Id = pic.Product.Id;
Title = pic.Product.Title;
Price = pic.Product.Price;
Info = pic.Product.Info;
SortOrder = pic.SortOrder;
}
public ViewModelProduct() { }
}
This is my mapping profile:
CreateMap<Product, ViewModelProduct>();
CreateMap<ViewModelProduct, Product>();
Edit:
After changing
var VMProduct = _mapper.Map<ViewModelProduct, Product>(Product);
to
var VMProduct = _mapper.Map<Product, ViewModelProduct>(Product);
and adding Mapper.AssertConfigurationIsValid();, I get one step further, and am informed that SortOrder, Categories and InCategories are unmapped.
I'm reluctant to change my viewmodel (too much). Can I make the mapping work with the current viewmodel?
Edit 2:
Apparently, now it works. The unmapped properties are still unmapped, but when I removed Mapper.AssertConfigurationIsValid();, the view rendered just fine.
Note that you can define for each member how it should be mapped. This is necessary if the destination member has a different name than the source member.
If source and destination have different (complex) types, add an additional mapping config between these types.
If the member is not mapped, but set somewhere else (e.g. in the controller), ignore it to prevent an error when checking the configuration with Mapper.AssertConfigurationIsValid().
CreateMap<Product, ViewModelProduct>()
// other members will be mapped by convention, because they have the same name
.ForMember(vm => vm.SortOrder, o => o.Ignore()) // to be set in controller
.ForMember(vm => vm.Categories, o => o.MapFrom(src => src.InCategories));
// needed to resolve InCategories -> Categories
CreateMap<ViewModelCategoryWithTitle, ProductInCategory>();
Also, most of the time it is sufficient to tell Automapper just the destination type you want, and let it resolve which mapping to apply:
var VMProduct = _mapper.Map<ViewModelProduct>(Product);

How to query multiple inherited sub-classes in a single query in Entity Framework

I have a big problem of querying diverse types of inherited subentities in a single query in Entity Framework. My essential aim is providing all of my data model structure in a single JSON string by eager loading. And the tricky point is "the inherited subclasses may contain another inherited subclass". The example seen below will clearly explain the situation.
Assume that I have a simple class structure like this:
public class Teacher
{
public int id { get; set; }
public string fullname{ get; set; }
//navigation properties
public virtual HashSet<Course> courses{ get; set; }
}
public class Course
{
public int id { get; set; }
public string coursename{ get; set; }
//foreign keys
public int TeacherId{ get; set; }
//navigation properties
public virtual Teacher teacher{ get; set; }
public virtual HashSet<Course> prerequisites{ get; set; }
}
Course has some subclasses GradedCourse and UngradedCourse
B1 or B2 may have a list of subentities consists of entities of types B1 or B2.
public class GradedCourse : Course
{
public string gradeType{ get; set; }
}
public class UngradedCourse: Course
{
public string successMetric { get; set; }
}
Now by this structure I want to provide a JSON structure from my WEBApi yielding list of Teacher objects including both GradedCourse and UngradedCourse with their subentities and specific fields. I have a query like this but it does not compile
db.Teachers.Select(t => new
{
t.id,
t.fullName
courses = t.courses.OfType<GradedCourses>()
.Select(g => new
{
id = g.id,
coursename = g.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
gradeType = g.gradeType
}
).Concat(t.courses.OfType<UngradedCourses>()
.Select(u => new
{
id = u.id,
coursename = u.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
successMetric= u.successMetric // subclass specific field
}
)
)
}
)
The problem is concating two different types of objects (they have different fields which is not possible for SQL UNION)
How can I handle this? Any help will open my mind. Thanks in advance for the professionals :)
It does not compile because the element type of 2 sets is not the same. So you just need to make them the same before being able to do anything:
db.Teachers.Select(t => new
{
t.id,
t.fullName
courses = t.courses.OfType<GradedCourses>()
.Select(g => new
{
id = g.id,
coursename = g.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
isGradedCourse = true,
gradeTypeOrMetric = g.gradeType
}).Concat(t.courses.OfType<UngradedCourses>()
.Select(u => new
{
id = u.id,
coursename = u.coursename,
prerequisites = g.prerequisites, // this is the list of other subentities
isGradedCourse = false,
gradeTypeOrMetric= u.successMetric // subclass specific field
}))
//finally select what of your choice
.Select(e => new {
id = e.id,
coursename = e.coursename,
prerequisites = e.prerequisites,
gradeType = e.isGradedCourse ? e.gradeTypeOrMetric : "",
successMetric = e.isGradedCourse ? "" : e.gradeTypeOrMetric
})
});
You still benefit the query being executed on server side without having to pull all teachers to local (and then being able to cast the entities - which is not supported in LinqToEntity query).

Does Entity Framework see property change inside object which is already in database?

I have two model classes:
public class Person {
public int Id { get; set; }
public string FirstName { get; set; }
public virtual List<Desert> Deserts {get;set;}
}
public class Desert{
public int Id { get; set; }
public string Name { get; set; }
public virtual List<Person> Persons{get;set}
}
If I add some persons to the database in Seed method and add their favourite deserts to them:
var deserts = new List<Desert>{new Desert{Name = "IceCream"}, new Desert{Name = "Cake"}}
var persons = new List<Person> {
new Person{FirstName = "James", Deserts = deserts},
new Person{FirstName = "Joanna"}
};
persons.ForEach(person => context.Persons.AddOrUpdate(person));
context.SaveChanges();
Then somewhere in controller invoke method which removes desert from person(connection/relationship between person and desert) :
//removes desert from a person
public void RemoveDesert(int personId, int desertToRemoveId) {
Person person = db.Persons.Find(personId);
foreach(Desert desert in person.Deserts){
if(desert.Id == desertToRemoveId){
person.Deserts.Remove(desert);
}
}
context.saveChanges();
}
Will Entity Framework see that property public virtual List<Desert> FavouriteDeserts {get;set;} changed and update it?
If not how to remove a desert which has Id=desertToRemoveId from a person which has Id=personId ans save changes in database? Can it be done without Lambda expressions?
Yes, it keeps track of child objects unless you tell it not to.
foreach(Desert desert in person.Deserts){
if(desert.Id == desertToRemoveId){
person.Deserts.Remove(desert);
}
}
I don't think C# will allow you to modify a collection while iterating it.
That said, you can always query the object you want to remove and then remove it:
var desertToRemove = person.Deserts.FirstOrDefault(x=> x.Id == desertToRemoveId);
if(desertToRemove != null) person.Deserts.Remove(desertToRemove );
Note that this however, only removes the relation (between person and desert), not the child entity. Read up on this subtle difference in this excellent answer here.
Also, you need to change your classes to use ICollection instead of List:
public virtual ICollection<Person> Persons {get; set; }
public virtual ICollection<Desert> Deserts {get; set;}

Navigation Property null when I do context.Users.Create(). Is that correct?

I'm developing my first class library that uses Entity Framework Code First as Data access layer.
I have this class:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<User> Friends { get; set; }
public virtual ICollection<User> FromWhomIsFriend { get; set; }
}
And now I testing my code with a Console application:
static void Main(string[] args)
{
Database.SetInitializer(
new DropCreateDatabaseAlways<AdnLineContext>());
insertUsersAndFriends();
}
private static void insertUsersAndFriends()
{
using (var context = new AdnLineContext())
{
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
var user2 = context.Users.Create();
user2.Name = "User2";
user2.Age = 26;
user2.City = "City2";
user2.Country = "Country2";
user2.Email = "email_2#email.com";
user2.InterestIn = "User2's interests";
var user3 = context.Users.Create();
user3.Name = "User3";
user3.Age = 27;
user3.City = "City3";
user3.Country = "Country3";
user3.Email = "email_3#email.com";
user3.InterestIn = "User3's interests";
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends.Add(user2);
user3.Friends.Add(user1);
context.SaveChanges();
}
}
I'm testing, so the database is empty.
This is my UserConfiguration class:
public UserConfiguration()
{
Property(d => d.Name).IsRequired();
Property(d => d.Age).IsRequired();
Property(d => d.City).IsRequired();
Property(d => d.Country).IsRequired();
Property(d => d.Email).IsRequired();
Property(d => d.InterestIn).IsRequired();
HasMany(d => d.MessagesSent).WithRequired(l => l.SentByUser).WillCascadeOnDelete(false);
HasMany(d => d.MessagesReceived).WithRequired(l => l.SentToUser).WillCascadeOnDelete(false);
HasMany(d => d.Friends).
WithMany(d => d.FromWhomIsFriend).
Map(c =>
{
c.ToTable("UserFriends");
c.MapLeftKey("UserId");
c.MapRightKey("FriendId");
});
HasMany(d => d.WantsToDo).
WithMany(a => a.Users).
Map(t =>
{
t.ToTable("UserActivities");
t.MapLeftKey("UserId");
t.MapRightKey("ActivityId");
});
}
But I get a null pointer exception here user1.Friends.Add(user2); because Friends is null.
What am I doing wrong? How can I fix this problem?
Entity Framework seems to be smart in this case. You are adding a new User to the context:
var user1 = context.Users.Create();
//...
context.Users.Add(user1);
//...
user1.Friends.Add(user2);
The entity is in Added state after that. Why should EF run a query with lazy loading to initialize the Friends collection? user1 is the principal in the relationships and because the state is Added it cannot exist yet in the database, hence there can't be any dependent in the database refering to it that could be loaded. So, EF does not try to load the collection at all (which is good in order to avoid unnecessary database roundtrips).
You could apply tricks to make it work - by attaching the new users before you add them to the context:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.UserId = 1;
context.Users.Attach(user1);
user2.UserId = 2;
context.Users.Attach(user2);
user3.UserId = 3;
context.Users.Attach(user3);
// ...because we cannot attach three users with the same key
user1.Friends.Add(user2);
user3.Friends.Add(user1);
// Lazy loading will run twice here based on the `UserId` which is 1,2,3
// and returns nothing, but the Friends collection will be initialized
// as empty collection
// This MUST be AFTER accessing the Friends collection
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
// the three "dummy UserIds" are ignored because state is Added now
context.SaveChanges();
Now, just forget this solution again. It's nonsense to force lazy loading (= expensive database query) to create an empty collection. C# has the new operator for this:
var user1 = context.Users.Create();
//...
var user2 = context.Users.Create();
//...
var user3 = context.Users.Create();
//...
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.Friends = new List<User>();
user3.Friends.Add(user1);
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
context.SaveChanges();
You can also just use var user1 = new User() in this scenario where you only add new entities to the context. Creating dynamic proxies has no benefit here (unless you would set any foreign key properties to other existing entities and want to access their corresponding navigation properties after calling SaveChanges - which doesn't seem to be the case in your example).
You must declare your "List<>" Properties like this:
public virtual ICollection<User> Friends { get; set; }
If you don't use virtual keyword EF will not initialize collection for you.
In your case, you are creating new object, use private properties to initialize it for new objects:
private ICollection<User> _friends;
public ICollection<User> Friends {
get { return _friends ?? (_friends = new List<User>()); }
set { _friends = value; }
}
You have to initialize the member Friends like this:
using (var context = new AdnLineContext())
{
context.Users.Add(user1);
context.Users.Add(user2);
context.Users.Add(user3);
user1.Friends = new List<User>();
user1.Friends.Add(user2);
user3.FromWhomIsFriend.Add(user1);
context.SaveChanges();
}
I think that you might want to consider how the database should model the relationship between users. It seems like you want to have a 1:N relationship between users and itself (IE, one user can have multiple other users associated with it). To be honest the only way I know how to achieve this is a lookup table that associates two UserIds together. You could do this in Entity Code First like this:
public class FriendDefinition
{
[ForeignKey("User")]
public int UserId { get; set; }
[ForeignKey("Friend")]
public int FriendUserId { get; set; }
public virtual User User { get; set; }
public virtual User Friend { get; set; }
}
Then you could update your User class like so:
public class User
{
public int UserId { get; set; }
public String Name { get; set; }
public int Age { get; set; }
public String City { get; set; }
public String Country { get; set; }
public String Email { get; set; }
public String InterestIn { get; set; }
public virtual ICollection<FriendDefinition> Friends { get; set; }
}
Finally, you would now use this as follows:
var user = db.context.Users.First();
var firstFriend = user.Friends.First().Friend;
It's a little clunky, but I think it would serve your purpose. Code first is an excellent tool but you still have to conceptualize how the data is actually being stored in the database to model what you need.
You should use the Create method of DbSet - it will give you a fully initialised proxy.
var user1 = context.Users.Create();
user1.Name = "User1";
user1.Age = 25;
user1.City = "City1";
user1.Country = "Country1";
user1.Email = "email_1#email.com";
user1.InterestIn = "User1's interests";
//....
context.Users.Add(user1);
Hard coding things like user1.Friends = new List<User>(); is hack not a solution.

Categories