Mapping one (entity class) to many (dto's/viewModels) - asp.net-mvc

I am trying to map information about the User to several dto's, but I'm getting null exceptions. Basically, the reason why I distributed the information among several classes is because there are common info that I need in several views. So this is what I ended up with:
User entity class
public class User
{
public int Id { get; set; }
public string Nickname { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public float Credits { get; set; }
public float PromotionalCredits { get; set; }
public string Telephone { get; set; }
public string Mobile { get; set; }
public double RatingAverage { get; set; }
public string ProfileImage { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
public virtual ICollection<CreditCard> CreditCard { get; set; }
public virtual ICollection<Message> ReceivedMessages { get; set; }
public virtual ICollection<Message> SentMessages { get; set; }
public virtual ICollection<Item> WatchList { get; set; }
public virtual ICollection<Rating> OwnRatings { get; set; }
public virtual ICollection<Rating> RatingsForOthers { get; set; }
}
DTO's and ViewModel calsses
public class UserInfoSummaryViewModel
{
public int Id { get; set; }
public string FullName { get; set; }
public UserDetailedStatus DetailedStatus { get; set; }
}
public class UserDetailedStatus
{
public float TotalCredits { get; set; }
public float Credits { get; set; }
public float PromotionalCredits { get; set; }
public BiddingAndItems BiddingAndItems { get; set; }
public int OngoingListings { get; set; }
public int NewMessages { get; set; }
public float Rating { get; set; }
public int NumberOfRatings { get; set; }
}
public class BiddingAndItems
{
public int TotalBids { get; set; }
public int WinningBids { get; set; }
public int AcquiredItems { get; set; }
public int ItemsAwaitingConfirmation { get; set; }
public List<ItemForUserBids> Items { get; set; }
}
Mappings inside AutoMapperBootStrapper class
Mapper.CreateMap<User, BiddingAndItems>()
.ForMember(m => m.TotalBids, o => o.MapFrom(s => s.TotalActiveBids()))
.ForMember(m=>m.ItemsAwaitingConfirmation, o=>o.MapFrom(s=>s.Items.Count(i=>i.IsAwaitingReceptionConfirmation().Equals(true))))
.ForMember(m=>m.AcquiredItems, o=>o.MapFrom(s=>s.AquiredItems().Count))
.ForMember(m => m.WinningBids,
o => o.MapFrom(s => s.Bids.Where(c => c.Item.CurrentHighestBidderId().Equals(s.Id))));
Mapper.CreateMap<User, UserDetailedStatus>()
.ForMember(m => m.NumberOfRatings, o => o.MapFrom(s => s.OwnRatings.Count()))
.ForMember(m => m.NewMessages, o => o.MapFrom(s => s.TotalUnreadMessages()))
.ForMember(m => m.OngoingListings, o => o.MapFrom(s => s.Items.Where(i => i.IsPublished())))
.ForMember(m => m.Rating, o => o.MapFrom(s => s.RatingAverage))
.ForMember(m => m.TotalCredits, o => o.MapFrom(s => s.TotalCredits()));
Mapper.CreateMap<User, UserInfoSummaryViewModel>();
Call to automapper inside UserController
public ActionResult Summary()
{
var user = _helper.GetUserFromSession();
var viewModel = Mapper.Map<User, UserInfoSummaryViewModel>(user);
return View(viewModel);
}
I thought because I have all the necessary mappings inside the bootstrapper this should, theoretically work, apparently I was wrong... How can I fix that?
UPDATE:
I got my mappings fixed and added a couple of value resolvers. Now I'm not getting a null reference exception but there seems to be something wrong because everytime I run the project it gets stuck and then the local server stops responding... Here's my code:
Mapper.CreateMap<User, BiddingAndItems>()
.ForMember(m => m.TotalBids, o => o.MapFrom(s => s.TotalActiveBids()))
.ForMember(m => m.ItemsAwaitingConfirmation,
o => o.MapFrom(s => s.Items.Count(i => i.IsAwaitingReceptionConfirmation().Equals(true))))
.ForMember(m => m.AcquiredItems, o => o.MapFrom(s => s.AquiredItems().Count))
.ForMember(m => m.WinningBids, o => o.ResolveUsing<WinningBidsResolver>())
.ForMember(m => m.Items, o => o.ResolveUsing<BiddingItemResolver>());
Mapper.CreateMap<User, UserDetailedStatus>()
.ForMember(m => m.NumberOfRatings, o => o.MapFrom(s => s.OwnRatings.Count()))
.ForMember(m => m.NewMessages, o => o.MapFrom(s => s.TotalUnreadMessages()))
.ForMember(m => m.OngoingListings, o => o.MapFrom(s => s.Items.Where(i => i.IsPublished()).Count()))
.ForMember(m => m.Rating, o => o.MapFrom(s => s.RatingAverage))
.ForMember(m=>m.BiddingAndItems, o => o.MapFrom(s=> s))
.ForMember(m => m.TotalCredits, o => o.MapFrom(s => s.TotalCredits()));
Mapper.CreateMap<User, UserInfoSummaryViewModel>()
.ForMember(m => m.DetailedStatus, o => o.MapFrom(s => s));
public class BiddingItemResolver : ValueResolver<User, List<ItemForUserBids>>
{
protected override List<ItemForUserBids> ResolveCore(User source)
{
var items = new List<ItemForUserBids>();
foreach (var bid in source.Bids)
{
var item = bid.Item;
var c = new ItemForUserBids
{
BidValue = bid.Amount,
Description = item.Description,
Id = item.Id,
ItemThumb = item.MainImageLink(),
Status = source.ItemBiddingStatus(item.Id),
TimeLeft = TimeUtility.TimeLeft(item.EndDate),
Title = item.Title
};
items.Add(c);
}
return items;
}
}
public class WinningBidsResolver : ValueResolver<User, int>
{
protected override int ResolveCore(User source)
{
return source.Bids.Where(c => c.Item.CurrentHighestBidderId().Equals(source.Id)).Count();
}
}
The problem is that I'm not getting any exceptions to give me any hints about what's going wrong... It just gets stuck! I suspect that my mappings are going into some sort of an infinite loops or something, but I am not sure what is happening exactly... Is there any way I could debug this problem?
Any help would be appreciated...

Without more information about the exception you receive I can only guess what could go wrong: I guess it's because you're using Linq on uninitialized collections in MapFrom. Try implementing a ValueResolver instead.

Related

NHibernte CreateAlias for 2 multiply class

I have 3 classes: Book, Genre, Authors
public class Book {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual int MfRaiting { get; set; }
public virtual int PageNumber { get; set; }
public virtual IList<Genre> Genres { get; set; }
public virtual Series Series { get; set; }
public virtual Mind Mind { get; set; }
public virtual IList<Author> Authors { get; set; }
public Book() {
Genres = new List<Genre>();
Authors = new List<Author>();
}
}
public class Genre {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Book> Books { get; set; }
public Genre() {
Books=new List<Book>();
}
}
public class Author {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string SurName { get; set; }
public virtual string Biography { get; set; }
public virtual IList<Book> Books { get; set; }
public Author() {
Books=new List<Book>();
}
}
And his classMaps
public class BookMap : ClassMap<Book> {
public BookMap() {
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Genres)
.Cascade.All()
.Table("Book_Genre");
HasManyToMany(x => x.Authors)
.Cascade.All()
.Table("Book_Author");
References(x => x.Series);
HasOne(x => x.Mind).Constrained();
}
}
public class GenreMap : ClassMap<Genre> {
public GenreMap() {
Id(x => x.Id);
Map(x => x.Name);
HasManyToMany(x => x.Books)
.Cascade.All()
.Inverse()
.Table("Book_Genre");
}
}
public class AuthorMap : ClassMap<Author> {
public AuthorMap() {
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.SurName);
Map(x => x.Biography);
HasManyToMany(x => x.Books)
.Cascade.All()
.Inverse()
.Table("Book_Author");
}
}
When i try write in code
var criteria = session.CreateCriteria();
criteria.CreateAlias("Genres", "genre", JoinType.LeftOuterJoin);
it works well, but when i do so
public ActionResult Index()
{
var criteria = session.CreateCriteria<Book>();
criteria.CreateAlias("Genres", "genre", JoinType.LeftOuterJoin);
criteria.CreateAlias("Authors", "author", JoinType.LeftOuterJoin);
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
}
i see exception Cannot simultaneously fetch multiple bags. How can I get result of that criteria?
Thank
I resolve this problem! I replace IList on ISet, and replace new List<"Class"> on new HashSet<"Class">
I read this problem here.
http://developer-should-know.tumblr.com/post/118012584847/jpa-fetching-strategies Only one collection that is fetched using JOIN strategy can be of type List, other collection must be of type Set. Otherwise an exception will be thrown:
HibernateException: cannot simultaneously fetch multiple bags

How to pass a string into MVC model with AutoMapper

I'm trying to include a csv string involvedBody in a View that is generated using AutoMapper but I can't work out where to map the string to the model.
I assumed it would be in the CreateMap call in the controller or as a .ForMember function in the mapping but I can't get it to work. I need to call the InvolvedBodies function and pass the string into the model before the model becomes an IOrderedEnumerable.
Is this possible or do I need to try something else?
ViewModel
public partial class FamilyInterventionListViewModel
{
public int Id { get; set; }
public int interventionId { get; set; }
public string interventionCategory { get; set; }
public string interventionType { get; set; }
public string outcome { get; set; }
public string achievement { get; set; }
public string involvedBody { get; set; }
public string startDate { get; set; }
public string endDate { get; set; }
public string notes { get; set; }
}
Repository
public string InvolvedBodies(int interventionId)
{
var q = (from body in context.tEntity
where body.tIntervention.Any(b => b.interventionID == interventionId)
select body.entityName
);
var ibcsv = string.Join(",", q.ToArray());
return ibcsv;
}
Controller
public ActionResult InterventionType(int Id, string achievement)
{
var model = GetDisplay(Id)
.Where(m => m.achievement == achievement)
.OrderByDescending(m => m.startDate)
;
return PartialView("_interventionType", model);
}
//Automapper for display model
private IEnumerable<FamilyInterventionListViewModel> GetDisplay(int Id)
{
var list = _repo.Get(Id);
Mapper.CreateMap<tIntervention, FamilyInterventionListViewModel>();
IEnumerable<FamilyInterventionListViewModel> viewModel = Mapper.Map <IEnumerable<tIntervention>, IEnumerable<FamilyInterventionListViewModel>>(list);
return viewModel;
}
Mapping
Mapper.CreateMap<tIntervention, FamilyInterventionListViewModel>()
.ForMember(d => d.Id, o => o.MapFrom(s => s.interventionID))
.ForMember(d => d.interventionCategory, o => o.MapFrom(s => s.tOutcome.tInterventionCategory.InterventionCategory))
.ForMember(d => d.interventionType, o => o.MapFrom(s => s.tInterventionType.interventionType))
.ForMember(d => d.outcome, o => o.MapFrom(s => s.tOutcome.outcome))
.ForMember(d => d.achievement, o => o.MapFrom(s => s.tAchievement.achievement))
.ForMember(d => d.involvedBody, o => o.MapFrom(s => s.tEntity.Select(m => m.entityName)))
;
I've not been able to find a solution to this using Automapper so have reverted to manually mapping the csv string after the model is created using:
foreach (var item in model)
{
item.involvedBody = _repo.InvolvedBodies(item.interventionId);
}
I'd be interested to know if this is deemed to be good practice or if there is some other technique I should be using.

Linq-To-Nhibernate multiples joins with conditionals

I'm using Fluent-Nhibernate version 1.3 and I'm trying to make a query envolving 5 tables. I created a sql query for an oracle database and I'm trying to replicate with linq-to-nhibernate.
Following a sample of my entities and mapping.
Entities:
public class A
{
public virtual int idA { get; set; }
public virtual String codA { get; set; }
public virtual String tipoA { get; set; }
public virtual IList<B> listB { get; set; }
}
public class B
{
public virtual C objectC { get; set; }
public virtual A objectA { get; set; }
public virtual DateTime dtBegin { get; set; }
public virtual DateTime dtEnd { get; set; }
}
public class C
{
public virtual int idC { get; set; }
public virtual String codeC { get; set; }
public virtual IList<B> listB { get; set; }
public virtual IList<D> listD { get; set; }
}
public class D
{
public virtual C objectC { get; set; }
public virtual string flgD { get; set; }
public virtual DateTime dtBegin { get; set; }
public virtual DateTime dtEnd { get; set; }
public virtual E objectE { get; set; }
}
public class E
{
public virtual int idE { get; set; }
public virtual String dsE { get; set; }
public virtual DateTime dtBegin { get; set; }
public virtual DateTime dtEnd { get; set; }
public virtual IList<D> listD { get; set; }
}
My mapping:
class AMap : ClassMap<A>
{
public AMap()
{
Table("A");
Id(x => x.idA, "ID_A").GeneratedBy.Sequence("StringA");
Map(x => x.tipoA, "TP_A");
Map(x => x.codA, "CODE_A");
HasMany(x => x.listB).Cascade.All().KeyColumn("ID_A");
}
}
class BMap : ClassMap<B>
{
public BMap()
{
Table("B");
CompositeId()
.KeyReference(x => x.objectC, "ID_C")
.KeyReference(x => x.objectA, "ID_A")
.KeyProperty(x => x.dtBegin, "DT_BEGIN");
Map(x => x.dtEnd, "DT_END");
}
}
class CMap : ClassMap<C>
{
public CMap()
{
Table("C");
Id(x => x.idC, "ID_C").GeneratedBy.Sequence("StringC");
Map(x => x.codeC, "CODE_C");
HasMany(x => x.listB).Cascade.All().KeyColumn("ID_C");
HasMany(x => x.listD).Cascade.All().KeyColumn("ID_D");
}
}
class DMap : ClassMap<D>
{
public DMap()
{
Table("D");
CompositeId()
.KeyReference(x => x.objectC, "ID_C")
.KeyProperty(x => x.flgD, "FLG_D")
.KeyProperty(x => x.dtBegin, "DT_BEGIN");
References(x => x.objectE, "CODE_E");
Map(x => x.dtEnd, "DT_END");
}
}
class EMap : ClassMap<E>
{
public EMap()
{
Table("E");
Id(i => i.idE, "ID_E").GeneratedBy.Assigned();
Map(m => m.dsE, "DSC_E");
Map(m => m.dtBegin, "DT_BEGIN");
Map(m => m.dtEnd, "DT_END");
HasMany(x => x.listD).Cascade.All().KeyColumn("ID_E");
}
}
My SQL Query(it works):
SELECT C.CODE_C, E.CODE_E, E.DT_BEGIN
FROM TABLEA A, TABLEB B, TABLEC C, TABLED D, TABLEE E
WHERE A.CODE_A = '0000' AND A.ID_A = B.ID_A AND B.ID_C = C.ID_C AND B.DT_END IS NULL
AND C.ID_C = D.ID_C AND D.DT_END IS NULL AND D.CODE_E = E.CODE_E AND E.DT_END IS NULL;
I tried to use multiple join but then some relathionship are collections so I would have to make a where inside the join.
So my question is: is possible to make this same Sql query as linq-to-nhibernate or is better to make a sequence of selects? And no i can't change the database.
Thanks in advance.
the is null query is only possible using DateTime? as type
var query = from b in B
from c in b.C
from d in c.listD
from e in d.E
where b.A.Code == "0000" && b.EndDate == null & ...
select new { Ccode = c.Code, Ecode = e.Code, E_BeginDate = e.BeginDate }
Update: to answer the third comment
grouping the results has to be done in memory because grouping in sql can only return aggregations
var results = query.AsEnumerable()
.GroupBy(a => a.Ccode, a => a.Ecode, (key, values) => new { Ccode = Key, Ecodes = values.ToList() })
.List();

Automapper complex mapping

How can I map the following:
public abstract class PersonBase
{
public Address Address { get; set; }
}
public class Address
{
public string AddressHome { get; set; }
public int PostalNumber { get; set; }
}
public class PersonEditVM : PersonBase
{
public int PersonId { get; set; }
}
public PersonEntity
{
public int PersonId { get; set; }
public string AddressHome { get; set; }
public int PostalNumber { get; set; }
}
I would like to map PersonEntity to PersonEditVM.
EDIT: Solved after a cup of coffee but it would be nice if it's a cleaner solution?.
Mapper.CreateMap<PersonEntity, PersonEditVM >()
.ForMember(d=>d.Address, s=>s.MapFrom(p=> new Address{
AddressHome = p.AddressHome,
PostalNumber = p.PostalNumber
}));
Here's one way to do it, which would split your Adress (which is correctly spelled Address by the way) mapping into another CreateMap call:
Mapper.CreateMap<PersonEntity, PersonEditVM>()
.ForMember(dest => dest.Adress, opt => opt.MapFrom(src => src));
Mapper.CreateMap<PersonEntity, Adress>()
.ForMember(dest => dest.AdressHome, opt => opt.MapFrom(src => src.AdressHome))
.ForMember(dest => dest.PostalNumber, opt => opt.MapFrom(src => src.PostalNumber));

NCommon 1.1 and EF4

I am new to NCommon and am looking for an example project to get started.
I am using EF4.
I assume one needs to use EF4 with POCOs?
Thanks for any help.
NCommon.Configure.Using(new StructureMapContainerAdapter(ObjectFactory.Container))
.ConfigureState<DefaultStateConfiguration>()
.ConfigureData<EFConfiguration>(config => config.WithObjectContext(
() =>
{
SiteContext db = new SiteContext(ConfigurationManager.ConnectionStrings["TestTheBest"].ConnectionString);
if (Transaction.Current != null )
{
db.Context.Connection.Open();
}
return db.Context;
}))
.ConfigureUnitOfWork<DefaultUnitOfWorkConfiguration>(config => config.AutoCompleteScope());
public class SiteContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Test> Tests { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<Section> Sections { get; set; }
public DbSet<Answer> Answers { get; set; }
public DbSet<GlobalSettings> GlobalSettings { get; set; }
public DbSet<PassageTest> PassageTest { get; set; }
public DbSet<PassageTestAnswer> PassageTestAnswer { get; set; }
public DbSet<SaaSUser> SaaSUser { get; set; }
public SiteContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
Context.SavingChanges += new EventHandler(Context_SavingChanges);
}
void Context_SavingChanges(object sender, EventArgs e)
{
if(Context.Connection.State==ConnectionState.Open)
Context.Connection.Close();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasMany(x => x.Invitations).WithRequired(x=>x.user).WillCascadeOnDelete(true);
modelBuilder.Entity<User>().HasMany(x => x.PassageTests).WithRequired(x => x.user).WillCascadeOnDelete(true);
modelBuilder.Entity<PassageTest>().HasMany(x => x.PassageTestAnswers).WithRequired(x => x.passageTest).WillCascadeOnDelete(true);
// modelBuilder.Entity<Company>().HasMany(x => x.Users).WithRequired(x => x.company).WillCascadeOnDelete(true);
modelBuilder.Entity<Question>().HasMany(x => x.Answers).WithRequired(x => x.question).WillCascadeOnDelete(true);
modelBuilder.Entity<Question>().HasMany(x => x.PassageTestAnswers).WithRequired(x => x.question).WillCascadeOnDelete(true);
modelBuilder.Entity<Test>().HasMany(x => x.Invitations).WithRequired(x => x.test).WillCascadeOnDelete(true);
modelBuilder.Entity<Test>().HasMany(x => x.PassageTests).WithRequired(x => x.test).WillCascadeOnDelete(true);
modelBuilder.Entity<Test>().HasMany(x => x.Sections).WithRequired(x => x.test).WillCascadeOnDelete(true);
modelBuilder.Entity<Client>().HasMany(x => x.Tests).WithRequired(x => x.client).WillCascadeOnDelete(true);
}
public ObjectContext Context
{
get { return ((IObjectContextAdapter)this).ObjectContext; }
}
}

Resources