Home > Net >  ASP.NET Core - How to resolve 'ICollection<ApprovalAttachment>' does not contain a d
ASP.NET Core - How to resolve 'ICollection<ApprovalAttachment>' does not contain a d

Time:09-24

In ASP.NET Core-6 Web API, I have these models:

public class LeaveApplication
{
    public Guid Id { get; set; }
    public Guid EmployeeId { get; set; }
    public string ReferenceNumber { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public virtual Employee Employee { get; set; }
    public virtual LeaveApproval LeaveApproval { get; set; }
}

public class LeaveApproval
{
    public Guid Id { get; set; }
    public bool? IsApproved { get; set; }
    public DateTime? ApprovedDate { get; set; } = null;
    public virtual ICollection<LeaveApprovalAttachment> LeaveApprovalAttachments { get; set; }
}

public class LeaveApprovalAttachment
{
    public Guid Id { get; set; }
    public Guid LeaveApprovalId { get; set; }
    public string FileType { get; set; }
    public string FileName { get; set; }
    public string FilePath { get; set; }
    public long? FileSize { get; set; }
    public virtual LeaveApproval LeaveApproval { get; set; }
}

A LeaveApplication has one LeaveApproval, while a LeaveApproval can have many LeaveApprovalAttachments.

Based on parameter id, I want to get a filepath of a LeaveApprovalAttachment and download the document. So, I have this code:

public async Task<LeaveApplication> GetLeaveDocumentByIdAsync(Guid id)
{
    var leave = await _dbContext.LeaveApplications
                    .Where(m => (bool)m.IsApproved)
                    .Include(x => x.LeaveApproval)
                    .ThenInclude(x => x.LeaveApprovalAttachments)
                    .FirstOrDefaultAsync(x => x.LeaveApproval.LeaveApprovalAttachments.Any(x => x.Id == id));
    var path = Path.Combine(Directory.GetCurrentDirectory(), leave.LeaveApproval.LeaveApprovalAttachments.FilePath);
    var memory = new MemoryStream();
    using (var stream = new FileStream(path, FileMode.Open))
    {
       await stream.CopyToAsync(memory);
    }
    memory.position = 0;
    var contentType = "APPLICATION/octet-stream";
    var fileName = Path.GetFileName(path);
    return File(memory, contentType, fileName);
}

But I got this error:

Error CS1061 'ICollection' does not contain a definition for 'FilePath' and no accessible extension method 'FilePath' accepting a first argument of type 'ICollection' could be found (are you missing a using directive or an assembly reference?)

and it highlights FilePath in leave.LeaveApproval.LeaveApprovalAttachments.FilePath

How do I resolve this?

Thanks

CodePudding user response:

From here:

public virtual ICollection<LeaveApprovalAttachment> LeaveApprovalAttachments { get; set; }

LeaveApprovalAttachments property is a collection. You can't direct access via leave.LeaveApproval.LeaveApprovalAttachments.FilePath.

Instead, you need to provide the index to LeaveApprovalAttachments as:

leave.LeaveApproval.LeaveApprovalAttachments[0].FilePath

Or to get the first item of LeaveApprovalAttachments:

using System.Linq;

var leaveApprovalAttachment = leave.LeaveApproval.LeaveApprovalAttachments.FirstOrDefault();
if (leaveApprovalAttachment != null)
{
    var path = Path.Combine(Directory.GetCurrentDirectory(), leaveApprovalAttachment.FilePath);

    ...
}

CodePudding user response:

You're getting that error because LeaveApprovalAttachments is of type ICollection<LeaveApprovalAttachment> and not LeaveApprovalAttachment. You can see from the documentation of ICollection<T> that there is no FilePath property. You also can't just access a property of the element type, which is LeaveApprovalAttachment in this case, from the ICollection<T> because you have multiple LeaveApprovalAttachments. How can it know which one you want to access?

Looking at your code, it seems like you only need a single LeaveApprovalAttachment, so the best way to resolve it in this particular case is probably just to select the one LeaveApprovalAttachment that you need, rather than the entire LeaveApplication.

Maybe something like this...

var leaveAttachment = _dbContext
    .LeaveApplications
    .Include(la => la.LeaveApproval)
    .ThenInclude(la => la.LeaveApprovalAttachments)
    .Where(la => (bool)la.IsApproved)
    .SelectMany(la => la.LeaveApproval.LeaveApprovalAttachments)
    .FirstOrDefault(laa => laa.Id == id);    

Note that you're querying on LeaveApplication.IsApproved which doesn't seem to exist on your model, but I tried to replicate your query as much as possible.

  • Related