I'm building a simple messaging system with the following data model:
public partial class Conversation
{
public Conversation()
{
this.Messages = new HashSet<Message>();
this.Customers = new HashSet<Customer>();
this.MessagingHubConnections = new HashSet<MessagingHubConnection>();
}
public int Id { get; set; }
public int BoatId { get; set; }
public System.DateTime TimeCreated { get; set; }
public virtual ICollection<Message> Messages { get; set; }
public virtual Boat Boat { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
public virtual ICollection<MessagingHubConnection> MessagingHubConnections { get; set; }
}
public partial class Message
{
public int Id { get; set; }
public int ConversationId { get; set; }
public string Text { get; set; }
public bool IsRead { get; set; }
public System.DateTime TimeSend { get; set; }
public int CustomerId { get; set; }
public virtual Conversation Conversation { get; set; }
public virtual Customer Customer { get; set; }
}
When a customer opens his account dashboard, I want to display a list of all the conversations. This should be ordered according to the following rule: The first conversation in the list is the one with a message with the latest Message.TimeSent. If the conversation has no messages, it has to pick the Conversation.TimeCreated.
The code below is what I have right now but this is obviously not working when a Conversation has no messages. The variable conversations in the code below is an IQueryable<Conversation>.
var orderedConversations = conversations.OrderByDescending(c => c.Messages.Max(m => m.TimeSend));
Anyone who can help me out?
By projecting the TimeSend into DateTime? before Max()-ing it, you can obtain a (DateTime?)null when the collection is empty instead of getting an InvalidOperationException. And then, you can null-coalesce this result with TimeCreated:
var orderedConversations = conversations
.OrderByDescending(c =>
c.Messages
.Select<Message, DateTime?>(x => x.TimeSend)
.OrderByDescending(x => x)
.FirstOrDefault() ??
c.TimeCreated);
Related
I have list of lines, the lines contains list of calls (call has DestinationNumber property)
here's my line model
public class Line
{
public Line()
{
Calls = new List<Call>();
Messages = new List<Sms>();
Payments = new List<Payment>();
}
public int LineId { get; set; }
public int CustomerId { get; set; }
public Customer Customer { get; set; }
public Package Package { get; set; }
public ICollection<Call> Calls { get; set; }
public ICollection<Sms> Messages { get; set; }
public ICollection<Payment> Payments { get; set; }
public string LineNumber { get; set; }
public LineStatus Status { get; set; }
[DataType(DataType.DateTime)]
public DateTime? CreatedDate { get; set; }
[DataType(DataType.DateTime)]
public DateTime? RemovedDate { get; set; }
}
and here's my call model
public class Call
{
public int CallId { get; set; }
public int LineId { get; set; }
public Line Line { get; set; }
public DateTime DateOfCall { get; set; }
public int Duration { get; set; }
public string DestinationNumber { get; set; }
}
I want to Count() the number of unique calls
I tried something like that..
Lines.Sum(line=>line.Calls.Sum(call=>call.DestinationNumber.Where(/*if DestiantionNumber not counted before*/))
GroupBy approach this picks all calls by their DestinationNumber which exist only once x.Count() == 1 and counts them Count()
int result = line.Calls.GroupBy(x => x.DestinationNumber).Count(x => x.Count() == 1);
I think something like this should work:
var count = lines.SelectedMany(l => l.Calls)
.Select(c => c.DestinationNumber)
.Distinct()
.Count();
i have the following problem, i need access to items of sales line from salesheader, when i try access by entity works fine by lazy loading, but i try map with Automapper 6
canĀ“t access to Item from sales header
thanks
public class SalesHeader
{
public int DocumentNo { get; set; }
public virtual ICollection<PostedSalesLine> SalesLines { get; set; }
}
public class SalesLine
{
public int LineNo { get; set; }
public int DocumentNo { get; set; }
public int ItemId { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
public decimal Amount { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public decimal UnitCost { get; set; }
public decimal UnitPrice { get; set; }
}
var result = unitOfWork.SalesHeader.GetById(documenNo);
Mapper.Initialize(cfg => cfg.CreateMap<SalesHeader, SalesHeaderDTO>()
return Mapper.Map<SalesHeaderDTO>(result);
Done!
Dont use lazy loading, it creates a mess of proxys
public IEnumerable<SalesHeader> GetAllFullDocuments()
{
return SalesContext.SalesHeader.Include(sh => sh.SalesLines.Select(i => i.Item))
.Include(sh => sh.SellToCustomer)
.Include(sh => sh.BillToCustomer)
.ToList();
}
So im trying to create a little website to help with my family's business.
I have two models, Jobs and Days.
public class Job
{
public int JobId { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double PerHour { get; set; }
public string Description { get; set; }
}
public class Day
{
public int JobId { get; set; }
public int DayId { get; set; }
public DateTime Date { get; set; }
public double Hours { get; set; }
public string Details { get; set; }
public int Invoice { get; set; }
}
I need to make an Invoice, the invoice will be numbered. The days have the invoice number so ill be able to pick the days needed.
It would look something like this.
Date LotNum Street Suburb Hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
1/1/01 1 John Street Hurstville x hours
I am able to get the days with the specific invoice number using this:
vm.Days = _dayRepo.GetAll().Where(d => d.Invoice == id);
By doing so i have the date and hours of that day but now i need to get the job information. Both Day and Job have the JobId so i would be able to link them but i just dont know how.
This is what i have so far:
public IActionResult Invoice(int id)
{
CreateInvoiceViewModel vm = new CreateInvoiceViewModel();
vm.Days = _dayRepo.GetAll().Where(d => d.Invoice == id);
//vm.JobId =
vm.Jobs = _jobRepo.GetAll();
return View(vm);
}
My view looks like this:
#model CreateInvoiceViewModel
<table>
#foreach (var item in Model.)
{
<tr>
<td>#item.Date.Date.ToString("dd/MM/yy")</td>
<td>#item.Hours</td>
</tr>
}
</table>
I dont know what to put in for the foreach.
Thanks in advance!
you just need a join query. Define Your ViewModel like:
public class InvoiceViewModel
{
public DateTime Date { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double Hours { get; set; }
}
Create a join query and convert it to ViewModel:
public IActionResult Invoice(int id)
{
var query = from d in _dayRepo.GetAll()
join job in _jobRepo.GetAll() on d.JobId equals job.JobId
select new { Date=d.Date, LotNum= job.job , Street =job.Street , Suburb =job.Suburb , Hours =d.Hours };
IEnumerable<InvoiceViewModel> viewModel = query.Select(c => new InvoiceViewModel()
{
Date=query.Date,
LotNum=query.LotNum,
Street=query.Street,
Suburb=query.Suburb,
Hours=query.Hours
});
return View(viewModel);
}
This should result in an IEnumerable with your ids
var ids = _dayRepo.GetAll().Where(d => d.Invoice == id).Select(x => x.JobId);
You can also add .ToList() to the end if you want it as a list.
This is more or less how I would set it up, or at least the beginnings of how.
(I'm using EntityFrameworkCore and yes I understand that you are using repos at the moment)
public class Job
{
public int Id { get; set; }
public int LotNum { get; set; }
public string Street { get; set; }
public string Suburb { get; set; }
public double PerHour { get; set; }
public string Description { get; set; }
// Navigation Helper
public virtual ICollection<InvoiceJobRelationship> Invoices { get; set; }
}
public class Day
{
public int Id { get; set; }
public DateTime Date { get; set; }
public double Hours { get; set; }
public string Details { get; set; }
// Navigation Helper
public virtual ICollection<InvoiceDayRelationship> Invoices { get; set; }
}
public class Invoice
{
public int Id { get; set; }
// Navigation Helpers
public virtual ICollection<InvoiceJobRelationship> Jobs { get; set; }
public virtual ICollection<InvoiceDayRelationship> Days { get; set; }
}
public class InvoiceDayRelationship
{
public int InvoiceId { get; set; }
// Navigation Helper
[ForeignKey("InvoiceId")]
public Invoice Invoice { get; set; }
public int DayId { get; set; }
// Navigation Helper
[ForeignKey("DayId")]
public Day Day { get; set; }
}
public class InvoiceJobRelationship
{
public int InvoiceId { get; set; }
// Navigation Helper
[ForeignKey("InvoiceId")]
public Invoice Invoice { get; set; }
public int JobId { get; set; }
// Navigation Helper
[ForeignKey("JobId")]
public Job Job { get; set; }
}
Then in your context
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<InvoiceDayRelationship>()
.HasKey(id => new { id.DayId, id.InvoiceId });
builder.Entity<InvoiceJobRelationship>()
.HasKey(ij => new { ij.JobId, ij.InvoiceId });
}
public DbSet<Invoice> Invoices { get; set; }
public DbSet<Day> Days { get; set; }
public DbSet<Job> Jobs { get; set; }
public DbSet<InvoiceDayRelationship> InvoiceDays { get; set; }
public DbSet<InvoiceJobRelationship> InvoiceJobs { get; set; }
Then you would be able to call pretty much anything you would like.
(Example Query)
(from x in context.Invoices where x.Id == id select x).Include(inv => inv.Days).Include(inv => inv.Jobs);
Whether it is possible so: it is a Messenger where the entity User content ICollection User that are collection Friends consist from the same Users?
If that possible please tell me how create a correct relationship between them in the DbContext file?
Or how better build this relationship. May be create separate entity?
Thanks in advance!
namespace Tinkl.Data.Core.Domain
{
public class User
{
public User()
{
Contacts = new List<User>();
Conversations = new List<Conversation>();
Invites = new List<User>();
}
public int UserId { get; set; }
public string NickName { get; set; }
public string EMail { get; set; }
public string Password { get; set; }
public DateTime? CreationDate { get; set; }
public DateTime? ExitDate { get; set; }
public string Picture { get; set; }
public string Status { get; set; }
public virtual ICollection<User> Invites { get; set; }
public virtual ICollection<User> Contacts { get; set; }
public virtual ICollection<Conversation> Conversations { get; set; }
}
}
You are going in right direction, see my below code same type of self-relationship in EF code first
public class ContentEntityRef : BaseModel
{
public ContentEntityRef()
{
RoleRefs = new HashSet<RoleRef>();
}
public int EntityId { get; set; }
public string EntityName { get; set; }
public int? ParentEntityId { get; set; }
public virtual ICollection<RoleRef> RoleRefs { get; set; }
public virtual ContentEntityRef Parent { get; set; }
public virtual ICollection<ContentEntityRef> Children { get; set; }
}
I had created seprate configuration file, you can same use in dbContext "OnModelCreating" method.
internal class ContentEntityRefConfiguration : EntityTypeConfiguration<ContentEntityRef>, IEntityConfiguration
{
public ContentEntityRefConfiguration()
{
this.HasKey(x => x.EntityId).Property(t => t.EntityId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(x => x.EntityName).IsRequired().HasMaxLength(50);
this.HasMany(c => c.Children).WithOptional(c => c.Parent).HasForeignKey(c => c.ParentEntityId);
this.HasMany<RoleRef>(role => role.RoleRefs)
.WithMany(content => content.ContentEntities)
.Map(contentRole =>
{
contentRole.MapLeftKey("EntityID");
contentRole.MapRightKey("RoleID");
contentRole.ToTable("RoleEntityMap");
});
}
}
hope this will help you :)
When I select artile, it select user, but user has a collection of article, so article select user again. May be recursive cause out of memory ,
The calling processing is :
article=>user=>article=>user...
ef entities is :
public partial class article
{
public int id { get; set; }
public string title { get; set; }
public string cont { get; set; }
public Nullable<int> uid { get; set; }
public System.DateTime addtime { get; set; }
public Nullable<int> colid { get; set; }
public virtual user user { get; set; }
public virtual column column { get; set; }
}
public partial class user
{
public user()
{
this.roleusers = new HashSet<roleuser>();
this.articles = new HashSet<article>();
}
public int id { get; set; }
public string email { get; set; }
public string uname { get; set; }
public string upass { get; set; }
public virtual ICollection<roleuser> roleusers { get; set; }
public virtual ICollection<article> articles { get; set; }
}
mysql EF operation class is :
public class ArtDao
{
readonly crmEntities _ent = new crmEntities();
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.ToList();
total = _ent.articles.Count();
return ll;
}
}
How to avoid to eager load the collection property roleusers and articles ?
You need to set LazyLoad on you edmx properties, and manually load only first level childs with Include() method when selecting:
public List<article> PageArts(int start, int limit, out int total)
{
var ll =
_ent.articles.OrderByDescending(o => o.id)
.Skip(start)
.Take(limit)
.Include(o => o.user)
.ToList();
total = _ent.articles.Count();
return ll;
}
You need to implement it in another class, wich can be a partial class.