Constructor for object model similar from entity - asp.net-mvc

I'm trying my best to follow some MVC guidelines and for now, I have created a model with the fields I need from an entity I have. I have created the following Model class:
public class PersonStyle
{
public string Name { get; set; }
public int? Age { get; set; }
public string City { get; set; }
public string Style { get; set; }
}
My Entity is sometihng like:
public class PersonOE
{
public string Name { get; set; }
public int? Age { get; set; }
public string City { get; set; }
}
Im trying to build a constructor for the following:
PersonON personBus = new personBus();
List<PersonStyle> personStyleList = new List<PersonStyle>(personBus.getPeople()); //getPeople(); returns a PersonOE list
For this all, I need suggestions on how to create the PersonStyle constructor that will put "null" into the only different variable from the PersonOE model.

All you need to do is use LINQ to generate your properties. Though, adding your Style to each of them could get a little harder to deal with, but you haven't given any input on where they're coming from. This is a quick and easy way to convert from your data to model objects.
List<PersonStyle> personStyleList = personBus.GetPeople()
.Select(p => new PersonStyle {
Name = p.Name,
Age = p.Age,
City = p.City
});

Related

How can I query Entity Framework to a different class and get a count instead of a list property?

Let's take an example model:
public class Person
{
public string Name {get; set;}
public List<Guitar> Guitars {get; set;}
}
public class Guitar
{
public string Brand {get; set;}
public string Model {get; set;}
}
I need to create a View with a list of all People and a count of how many guitars they have. To do that I would like to pass a ViewModel like this one populated with EF without loading all Guitards:
// This will be send to the View, I can also just send a List
public class ViewModelPassed
{
List<PeopleGuitarViewModel> AllPeople { get; set; }
}
// This should be populated from EF
public class PeopleGuitarViewModel
{
public string Name { get; set; }
public int NumberOfGuitars { get; set; }
}
Can I query EF to a different class, and avoid bringing the whole list of guitars and instead get a COUNT in SQL?
Thanks!
You should provide primary key to your models for EF core to create the relationship:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public List<Guitar> Guitars { get; set; }
}
public class Guitar
{
public int ID { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
}
Also use public for AllPeople property in ViewModelPassed:
public List<PeopleGuitarViewModel> AllPeople { get; set; }
Then query like :
ViewModelPassed modelPassed = new ViewModelPassed();
modelPassed.AllPeople = new List<PeopleGuitarViewModel>();
modelPassed.AllPeople = _applicationDbContext.Persons.Select(x =>
new PeopleGuitarViewModel
{
Name = x.Name,
NumberOfGuitars = x.Guitars.Count
}).ToList();
One way is to do it like this:
var viewModel=context.Persons.Select(x=>new PeopleGuitarViewModel
{
Name=x.Name,
NumberOfGuitars=Guitars.Count()
}).ToList();
this kind of query is also named Projection and is consider best practice (do not send full entity to the view).
There are 2 libraries which I often use for such projections: AutoMapper and/or Mapster.
Hope this help you.
When populating your ViewModel, you can use Linq to get the count. Like this:
PeopleGuitarViewModel p= new PeopleGuitarViewModel();
p.Name = person.Name;
p.NumberOfGuitars = person.Guitars.Count;
Where person is an object of your Person class.

The properties expression is not valid. The expression should represent a property

I have these two entities
public class Song : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
[Required]
public virtual Album Album { get; set; }
[Required]
public int TrackNumber { get; set; }
}
public class Album : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
public virtual IEnumerable<Song> Songs { get; set; }
[Required]
public int AlbumNumber { get; set; }
}
Path is defined in the IPathHavingEntity interface.
In my Seed method I want to add a song to the Songs table only if it doesn't exist. For this reason I check that the album path and song path combination don't exist already before adding it thus
context.Songs.AddOrUpdate(
s => new { FilePath = s.Path, AlbumPath = s.Album.Path },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
The problem is I get this error
The properties expression 's => new <>f__AnonymousType0``2(FilePath =
s.Path, AlbumPath = s.Album.Path)' is not valid. The expression should
represent a property: C#: 't => t.MyProperty' VB.Net: 'Function(t) t.MyProperty'. When specifying multiple properties use an anonymous type: C#: 't => new { t.MyProperty1, t.MyProperty2 }' VB.Net: 'Function(t) New With { t.MyProperty1, t.MyProperty2 }'.
What's the problem?
In my case, The Only modification I did that on the Model Classes forget to put {get; set;} with the property declaration, Thus ...It's solved my problem.
Like this:
Before:
public int Supplier_ID;
public String Supplier_Code;
After:
public int Supplier_ID { get; set; }
public String Supplier_Code { get; set; }
Kindly Check your Model Classes should have the Get/Set property
I struggled with a similar issue for several hours today and was finally able to resolve it. I'm not sure if this will work for your situation but it's worth investigating.
The problem may be caused by the Album property of your Song entity being marked as virtual. I'm not an EF expert but I don't think it likes that virtual property when initializing your anonymous type. Add a non-virtual property for the album path (but keep the virtual navigation property), like this:
public class Song : IPathHavingEntity
{
public int Id { get; set; }
[Required]
public string Path { get; set; }
[Required]
public virtual Album Album { get; set; }
public string AlbumPath { get; set; }
[Required]
public int TrackNumber { get; set; }
}
And then perform the AddOrUpdate using that non-virtual property, like this:
context.Songs.AddOrUpdate(
s => new { FilePath = s.Path, AlbumPath = s.AlbumPath },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
EF should then only allow you to add songs where the given song path and album path do not already exist. Whether your Song domain object can have a non-virtual AlbumPath property is another question but this should at least allow you to run your seed method in the way you described.
EF does not create a column for a property which does not have either getters or setters. For example, EF will not create columns for the following City and Age properties.
using System.ComponentModel.DataAnnotations;
public class Student
{
private int _age = 0;
public int StudentId { get; set; }
public string StudentName { get; set; }
public string City { get{ return StudentName;} }
public int Age { set{ _age = value;} }
}
Referance : https://www.entityframeworktutorial.net/code-first/notmapped-dataannotations-attribute-in-code-first.aspx
In my case, changing the following values in the mapper worked.
From:
this.HasKey(t => new { FirstName = t.FirstName, LastName = t.LastName });
To:
this.HasKey(t => new { t.FirstName, t.LastName });
What has not been mentioned in any of other answers is that the source of the problem in any case shown is the same: The "custom identification expression" passed in as a parameter of the AddOrUpdate method has to be a valid property of the entity being inserted or updated. Also, it will not accept ComplextType.Property there.
For example:
context.Songs.AddOrUpdate(
s => new { k.Path, k.AlbumPath },
new Song { TrackNumber = 1, Path = "01 Intro.mp3", Album = one });
Note that the problem was resolved when the AlbumPath was used and also note that the anonymous type requires no other fields to be created. Instead, you just need to specify the property names.
It worth mentioning to be careful when using AddOrUpdate as the result can be destructive.

Saving many to many relationship tables in Asp.Net MVC

I use Asp.Net MVC, Entity Framework. I have a form it looks like below.
Here, dropdownlist is filled from a table(types). Checkboxes is filled from another table(test). Tables are like below:
public class Types
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
}
public class Tests
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
}
public class Types_Tests
{
public int Types_TestsID{ get; set; }
public int TypesID { get; set; }
public int TestsID { get; set; }
public virtual Types Types { get; set; }
public virtual Tests Tests { get; set; }
}
Types_test table is relation table between Types and Tests. When I click Kaydet button, it shuld save type and checked tests. I made this operation using ViewBag, javascript and hdnvalue.I added checked checkboz values to a hdntext. I made saving process like below:
[HttpPost]
public ActionResult Index(string drpType, string hdntesttypes)
{
var TypeList = Types.GetAll();
ViewBag.TypesList = new SelectList(TypeList, "Id", "Name");
var testypeList = testTypes.GetAll();
ViewBag.TestTypesList = new SelectList(testypeList, "Id", "TestName");
GenericRepository<TestDisabledTypes> testDisabledRepository = new GenericRepository<TestDisabledTypes>(_context);
if (!string.IsNullOrEmpty(hdntesttypes))
{
string[] disabletypesArray = hdntesttypes.Split(',');
using (TransactionScope trns = new TransactionScope())
{
for (int i = 0; i < disabletypesArray.Length; i++)
{
Test_Types types = new Test_Types ();
types.TestTypesID = Convert.ToInt32(disabletypesArray[i]);
types.TypesID = Convert.ToInt32(drpType);
testDisabledRepository.Insert(types);
}
trns.Complete();
}
}
return View();
}
It wokrs. But I search better solution for this process. Can someone give me any idea?
Thanks.
If you don't need additional attributes for your entity class, you don't need create link table.
Just define the following class, and EF will generate the link table for you automatically.
public class Type
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
public ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
public ICollection<Type> Types {get;set;}
}
Well, in EntityFramework if you want to create a many to many relation object you need to create new object of "linking" entity. Unfortunately, it is not possible to add first object, add second object and say "Guys, you are in many to many relationships. Are you happy then?" :) You need to create relation object, set appropriate fields in it (I think these are ids of two objects itself) and add it to relation collection (entity) in your model. But before doing so you need to be sure that objects with data you are linking with are already exists in database. Otherwise you'll get an error
Also it's not necessary to create manually transaction because EF does it for you automatically each time you get/save your data

MVC How to map from my domain model to a specific view model

I'm getting started with the concept of mapping domain models to view models in ASP.NET MVC after watching a recommendation to do this to pass specific viewModels to the views.
I've been able to manage a basic mapping of one domain model to a simpler viewmodel with less properties but now need to produce a more complex viewmodel and can't figure it out. I have the following domain models
public class Club
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public string Telephone { get; set; }
public string URL { get; set; }
public DateTime CreatedDate { get; set; }
public virtual ICollection<Member> Members{ get; set; }
}
public class Member
{
public int MemberID{ get; set; }
public string Name { get; set; }
public MemberType Membership{ get; set; }
public virtual Club Club { get; set; }
public virtual int ClubID { get; set; }
}
public enum MemberType
{
Standard,
Special,
Limited
}
I want to map to a view model such as this (note: I've split it like this because I think it makes sense but I'm not sure)...
public class ClubDetailsViewModel
{
public int ClubID { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public IList<ClubDetailsMemberSummaryViewModel> Members { get; set; }
}
public class ClubDetailsMemberSummaryViewModel
{
public MemberType Membership { get; set; }
public int MemberCount { get; set; }
}
What I'm trying to end up with is a page which displays some of the club details plus a summary report of the member types at the club with a count of the members. Such as:
Some Club Name
Description of the club.....
CLUB MEMBERS
Limited - 15
Standard - 100
So I think the viewmodel makes sense for this (although might be a better way to do it). Where I'm struggling is how to map the elements. I can get the Club to map the main fields to the club viewmodel but really can't work out how to map the result of the list of clubs onto their view model and then add that to the main view model as a list.
I'm getting the clubs from my repository using this
var clubs = _clubRepository.GetClubByID(ID);
Then I can transform the Courts which are returned using an include in the data access layer from entity framework using this
var grpCourts = from c in clubs.Members
group c by c.Membership into grp
select new { st = grp.Key, count = grp.Distinct().Count() };
How would I loop through the resulting records and map those to the ClubDetailsMemberSummaryViewModel and then add the list of those to the main ClubDetailsViewModel?
Your mapping from Club to ClubDetailsViewModel will be trivial with the exception of Members. For that property, you could write a quick resolver inline or write your own custom resolver. An inline resolver would look something like this:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing(src =>
{
return src.Members
.GroupBy(m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
});
}));
I think it's good practice to refactor more complex resolvers like this out to their own classes:
public class MembershipClubDetailsResolver : ValueResolver<Club, IList<ClubDetailsMemberSummaryViewModel>>
{
protected override IList<ClubDetailsMemberSummaryViewModel> ResolveCore (Club source)
{
return source.Members
.GroupBy (m => m.Membership)
.Select(grp => new ClubDetailsMemberSummaryViewModel
{
Membership = grp.Key,
MemberCount = grp.Distinct().Count()
})
.ToList();
}
}
And then use that resolver in your mapping:
Mapper.CreateMap<Club, ClubDetailsViewModel>()
.ForMember(dest => dest.Members, opt => opt.ResolveUsing<MembershipClubDetailsResolver>());
Your mapping appears to be rather complex, I think I would use the .ConvertUsing method of automapper
Mapper.CreateMap<List<Club>,List<ClubDetailsViewModel>>()
.ConvertUsing<ClubToClubDetailsViewModel>();
The conversion class has the following inheritance
public class ClubToClubDetailsViewModel: TypeConverter<List<Club>,List<ClubDetailsViewModel>>
{
....
}
Alternatively you can tinker with creating two "simple" mappings
Mapper.CreateMap<Club,ClubDetailsViewModel>()
That will map everything except the property called Members
Then you need to create a mapping for the members to ClubDetailsMemberSummaryViewModel, you can do that mapping manually or you can configure this in automapper aswell.
For more specific details on automapper you can visit https://github.com/AutoMapper/AutoMapper/wiki

How to generically map a domain model to a presentation model?

I am trying to figure out how to generically map a domain model to a presentation model. For example, given the following simple objects and interfaces ...
// Product
public class Product : IProduct
{
public int ProductID { get; set; }
public string ProductName { get; set; }
}
public interface IProduct
{
int ProductID { get; set; }
string ProductName { get; set; }
}
// ProductPresentationModel
public class ProductPresentationModel : IProductPresentationModel
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public bool DisplayOrHide { get; set; }
}
public interface IProductPresentationModel
{
int ProductID { get; set; }
string ProductName { get; set; }
bool DisplayOrHide { get; set; }
}
I would like to be able to write code like this ...
MapperObject mapper = new MapperObject();
ProductService service = new ProductService();
ProductPresentationModel model = mapper.Map(service.GetProductByID(productID));
... in which the "MapperObject" could automatically figure out which properties map across the two objects and what sort of objects it is mapping using something like reflection, convention-based mapping, etc. So, I could then just as easily try to map objects like UserPresentationModel and User with the same MapperObject.
Is this possible? If so, how?
EDIT: Just for clarity, here is an example of a non-generic MapperObject that I am currently using:
public class ProductMapper
{
public ProductPresentationModel Map(Product product)
{
var presentationModel = new ProductPresentationModel(new ProductModel())
{
ProductID = product.ProductID,
ProductName = product.ProductName,
ProductDescription = product.ProductDescription,
PricePerMonth = product.PricePerMonth,
ProductCategory = product.ProductCategory,
ProductImagePath = product.ProductImagePath,
ProductActive = product.ProductActive
};
return presentationModel;
}
}
I am still trying to work out how to get this to work with List, instead of just a single Product, but that's a different topic :)
I see want you want. You want to map your domain entities (Product) to aome kind of DTO object (ProductPresentationModel) for communication with your clients (GUI, external services etc).
I you have all this functionality you're looking for packed into AutoMapper framework.
You can write like this with AutoMapper:
Mapper.CreateMap();
look at this wiki https://github.com/AutoMapper/AutoMapper/wiki/Flattening
Good luck.
/Best Regards Magnus

Resources