I have the following query, often have excution timeout errors, have any ideas for optimization.
Thank you!
queryResult = db.Products
.Where(x => !x.product_is_deleted)
.Where(x => x.product_name.Contains(keyword) || x.product_barcode.Contains(keyword) || x.ProductType.pro_type_name.Contains(keyword) || x.ProductType.pro_type_name.Contains(keyword))
.Select(x => new ProductView
{
product_id = x.product_id,
product_barcode = x.product_barcode,
product_name = x.product_name,
product_unit = x.product_unit,
product_size = x.product_size,
product_weight = x.product_weight,
product_type_id = x.product_type_id,
product_inventory = shelf == 0 ? (x.ImportProductDetails.Where(y => y.imp_pro_detail_product_id == x.product_id && y.ImportProduct.import_pro_warehouse_id == warehouse_id ).Select(y => y.imp_pro_detail_inventory).DefaultIfEmpty(0).Sum()) : (x.ImportProductDetails.Where(y => y.imp_pro_detail_product_id == x.product_id && y.ImportProduct.import_pro_warehouse_id == warehouse_id && y.imp_pro_detail_shelf_id == shelf).Select(y => y.imp_pro_detail_inventory).DefaultIfEmpty(0).Sum()),
product_opening_stock = x.product_opening_stock,
product_type_name = x.ProductType.pro_type_name,
product_price = x.ProductPrices.Where(y => !y.proprice_is_deleted).Where(y => y.proprice_applied_datetime <= DateTime.Now).OrderByDescending(y => y.proprice_applied_datetime).Select(y => y.proprice_price).FirstOrDefault(),
product_discount_type = x.ProductDiscounts.Where(y => !y.pro_discount_is_deleted && y.pro_discount_start_datetime <= datetimeNow && y.pro_discount_end_datetime >= datetimeNow).OrderByDescending(y => y.pro_discount_id).Select(y => y.pro_discount_type).FirstOrDefault(),
product_discount_value = x.ProductDiscounts.Where(y => !y.pro_discount_is_deleted && y.pro_discount_start_datetime <= datetimeNow && y.pro_discount_end_datetime >= datetimeNow).OrderByDescending(y => y.pro_discount_id).Select(y => y.pro_discount_value).FirstOrDefault()
//product_norm_name = x.ProductionNorms.Where(y => y.pro_norm_name == x.product_id && y.pro_norm_is_applied == true).Select(y => y.pro_norm_id).FirstOrDefault()
});
Related
var _query = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date >= From.Date && x.CreationDate.Date <= To.Date && (x.Type == Common.TransactionType.CPV || x.Type == Common.TransactionType.BPV) && x.CompanyCode == BranchCode && x.DebitAmount > 0)
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
});
var balances = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode)
//.Exist(_query.Account) (I want only account which exist in upper query)
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
Balance = g.Sum(x => x.DebitAmount - x.CreditAmount),
});
I want to use something like EXIST here in first query I have some specific accounts and in 2nd query I want to calculate balances of those accounts.
Can you please tell how I can use Exist function in LINQ.
Thank you.
I want this query to be implemented in LINQ:
SELECT `c`.`AccountCode`, `c`.`CompanyCode` AS `LocationCode`, COALESCE(SUM(`c`.`DebitAmount` - `c`.`CreditAmount`), 0) AS `Balance`
FROM `CashVoucherDetails` AS `c`
WHERE (CONVERT(`c`.`CreationDate`, date) <= '2022-12-20') AND (`c`.`CompanyCode` = '002') and `c`.`AccountCode` IN
(
SELECT `c`.`AccountCode`
FROM `CashVoucherDetails` AS `c`
WHERE ((((CONVERT(`c`.`CreationDate`, date) >= '2022-12-20') AND (CONVERT(`c`.`CreationDate`, date) <= '2022-12-20')) AND `c`.`Type` IN ('CPV', 'BPV')) AND (`c`.`CompanyCode` = '002')) AND (`c`.`DebitAmount` > 0)
GROUP BY `c`.`AccountCode`
)
GROUP BY `c`.`AccountCode`, `c`.`CompanyCode`;
you can use Contains or Any.
_dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode)
.Where(x => _query.Select(q => q.AccountCode).Contains(x.AccountCode))
Or
.Where(x => _query.Any(q => q.AccountCode == x.AccountCode))
Maybe something like that
var balances = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode && _query.Any(q => q.AccountCode == x.AccountCode))
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
Balance = g.Sum(x => x.DebitAmount - x.CreditAmount),
});
Using Entity Framework core, can I get the total sum of the column and row count in one call?
I have the following code, but I think there is a better way to do this.
TotalCostResponse result = new TotalCostResponse
{
TotalCost = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.SumAsync(x => x.Amount),
TotalNumber = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date = request.StartDate
&& x.Date <= request.EndDate)
.CountAsync()
};
So instead of calling dbContext two times, I need to make it in one call.
var result = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.GroupBy(x => 1)
.Select(group => new TotalCostResponse
{
TotalCost = group.Sum(x => x.Amount),
TotalNumber = group.Count()
})
.FirstOrDefaultAsync();
Here is an example with one query, there are certainly other ways that you can find.
// In the select get only what you need, in your case only the amount
var transactions =
await this.dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.Select(y => new
{
Amount = y.Amount,
}).ToListAsync();
// Calculating the data
var result = new TotalCostResponse
{
TotalCost = transactions.Sum(x => x),
TotalNumber = transactions.Count(),
}
The following C# Linq example displays the correct records in a serverSide datatable. However, when I add the following line: data = data.Concat(unconfirmed);, it increases the page/datatable load time from ~500 ms to ~2 seconds when year equals the current year. Searching by the current year displays the most amount of records, but they are only ~90. Searching by a previous year displays ~10 records at most and it only takes ~500 ms for the page/datatable to load:
var data = _db.ResidencyConfirmations.AsQueryable();
var year = Request.Form.GetValues("columns[11][search][value]")[0];
var showConfirmed = Request.Form.GetValues("columns[12][search][value]")[0];
int.TryParse(year, out var resultYear);
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
var allNotesReleased = data.Where(rc => rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Select(aap => aap.ActivityPhas.ActivityPhase)
.FirstOrDefault() == "All Notes Released" &&
rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Select(aap => aap.ActivityPhaseDate)
.FirstOrDefault().Year >= resultYear);
var complete = data.Where(rc => rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == rc.Application.ApplicationActivityPhas
.Max(x => x.ActivityPhas.WorkFlowStep))
.Select(aap => aap.ActivityPhas.ActivityPhase)
.FirstOrDefault() == "Complete" &&
rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 5)
.Select(aap => aap.ActivityPhaseDate)
.FirstOrDefault().Year < resultYear);
var unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear))
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
data = allNotesReleased.Concat(complete)
.Where(rc => rc.ResidencyConfirmationDate.Year == resultYear);
if (showConfirmed == "false")
data = data.Where(rc => !rc.Deed || !rc.Utility || !rc.VitalRecords);
data = data.Concat(unconfirmed);
Not sure how it compiles, written in text editor, but query should be faster. Note that instead of using .Year introduced range variables, it will speedup query if you have indexes on date.
var year = Request.Form.GetValues("columns[11][search][value]")[0];
var showConfirmed = Request.Form.GetValues("columns[12][search][value]")[0];
int.TryParse(year, out var resultYear);
var startOfTheYear = new DateTime(resultYear, 1, 1);
var nextYear = startOfTheYear.AddYears(1);
var data = _db.ResidencyConfirmations.AsQueryable();
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
var allNotesReleased =
from rc in data
from aap in rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Take(1)
where
aap.ActivityPhas.ActivityPhase == "All Notes Released" &&
app.ActivityPhaseDate >= startOfTheYear &&
rc.ResidencyConfirmationDate >= startOfTheYear && rc.ResidencyConfirmationDate < nextYear
select rc;
var complete =
from rc in data
from aap in rc.Application.ApplicationActivityPhas
.OrderByDescending(aap => aap.WorkFlowStep)
.Take(1)
from aap2 in rc.Application.ApplicationActivityPhas
.Where(aap2 => aap2.ActivityPhas.WorkFlowStep == 5)
.Take(1)
where
aap.ActivityPhas.ActivityPhase == "Complete" &&
app2.ActivityPhaseDate < startOfTheYear &&
rc.ResidencyConfirmationDate >= startOfTheYear && rc.ResidencyConfirmationDate < nextYear
select rc;
var unconfirmed = complete
.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate >= startOfTheYear && x.ResidencyConfirmationDate < nextYear)
)
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc
.OrderByDescending(x => x.ResidencyConfirmationDate)
.First()
);
if (showConfirmed == "false")
data = data.Where(rc => !rc.Deed || !rc.Utility || !rc.VitalRecords);
data = data.Concat(unconfirmed);
To improve performance and to display the correct records, I made the following changes/additions:
if (resultYear == DateTime.Now.Year)
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab")
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
else
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
IQueryable<ResidencyConfirmation> unconfirmed = null;
if (resultYear == DateTime.Now.Year)
unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear));
else
unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear))
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
It now takes right under 1 second for the page/datatable to load.
I need to convert this SQL:
SELECT
sb.Id AS id,
sb.name as name,
sb.age AS age,
sb.BirthDay as BirthDay,
su.UnitId AS unitId,
sb.WorkedInDodo AS workedInDodo,
sb.RelativesWorkedInDodo AS relativesWorkedInDodo,
sb.PhoneNumber AS phoneNumber,
sb.Email AS email,
sb.DeliveryCheck AS deliveryCheck,
sb.RestaurantCheck AS restaurantCheck,
sb.StandardsRatingCheck as standardsRatingCheck,
sb.SocialNetwork AS socialNetwork,
sb.SocialNetworkId AS socialNetworkId,
sb.socialNetworkScreenName AS socialNetworkScreenName,
sb.SourceOfInformationAboutSecretBuyers AS SourceOfInformationAboutSecretBuyers,
sb.suitable AS suitable,
sb.SocialNetworkMessagingEnable as socialNetworkMessagingEnable,
sb.IsInGroup as isInGroup,
sb.IsKeyWord as IsKeyWord,
sb.IsBanned as IsBanned,
sb.IsFraud as IsFraud,
sb.LastUnitsUpdateUtcDate as lastUnitsUpdateUtcDate,
sb.Comments as comments,
sb.MessagingApproval as messagingApproval,
sb.ProcessDataApproval as processDataApproval,
sb.CreatedDateTimeUtc AS createdDateTimeUtc,
sb.ModifiedDateTineUTC AS modifiedDateTime,
sb.Country AS country,
sb.PhoneNumberEditedUtc AS PhoneNumberEditedUtc
FROM (select * from
(SELECT s.*,
IF(s.LastSearchMessageDateUtc is null, 0, 1) as searched,
IF(cc.Id is null, 0, 1) as onCheck
FROM secretbuyers s
JOIN secretbuyerunits sbu ON sbu.SecretBuyerId = s.Id
LEFT JOIN outgoingMessageQueue mq ON mq.Type = 1 AND mq.VkUserId = s.SocialNetworkId and mq.State in (1,2)
LEFT JOIN checkcandidates cc ON cc.SecretBuyerId = s.id AND cc.State IN (1,4) AND cc.Date >= #p_checkBeginDateTime AND cc.Date <= #p_checkEndDateTime
WHERE sbu.UnitId = #p_unitId
AND (s.LastSearchMessageDateUtc is null OR s.LastSearchMessageDateUtc < #p_lastSearchMessageDateUtcLimit)
AND ((#p_deliveryCheck = true AND s.DeliveryCheck = true) OR (#p_restaurantCheck = true AND s.RestaurantCheck = true) OR (#p_standardsRatingCheck = true AND s.StandardsRatingCheck = true))
AND s.Suitable = true
AND s.IsRemove = false
AND mq.Id is null
AND s.IsBanned = false
AND s.IsFraud = false
AND s.IsAutoSearchable = true
group by s.Id
Order By onCheck ASC, searched asc, s.LastSearchMessageDateUtc asc) as temp
LIMIT #p_count) AS sb
JOIN secretbuyerunits su ON su.SecretBuyerId = sb.Id;
to EF Core 3.0 linq. The main problem is left joins. At first I've tried to do it like this:
private async Task<IEnumerable<MysteryShopper>> FindMysteryShoppers(
SearchMysteryShoppersParameters searchParameters, CancellationToken ct)
{
var lastSearchEdge = _nowProvider.UtcNow().Date.AddDays(-3);
bool shouldHaveDeliveryCheckMark = ShouldHaveDeliveryCheckMark(searchParameters.SearchType);
bool shouldHaveRestaurantCheckMark = ShouldHaveRestaurantCheckMark(searchParameters.SearchType);
bool shouldHaveStandardsCheckMark = ShouldHaveStandardsCheckMark(searchParameters.SearchType);
var lastCheckupEdgeDate = searchParameters.CheckupDates.Max().AddDays(-30);
return await _ratingsContext.SecretBuyers.AsNoTracking().Where(ms => !ms.IsBanned &&
!ms.IsFraud
&& ms.IsAutoSearchable
&& ms.Suitable
&& !ms.IsRemove
&& ms.SocialNetworkMessagingEnable
&& (ms.LastSearchMessageDateUtc == null
|| ms.LastSearchMessageDateUtc < lastSearchEdge)
)
.Where(ms => !shouldHaveDeliveryCheckMark && ms.DeliveryCheck)
.Where(ms => !shouldHaveRestaurantCheckMark && ms.RestaurantCheck)
.Where(ms => !shouldHaveStandardsCheckMark && ms.StandardsRatingCheck)
.Where(ms => ms.MysteryShopperUnits
.Any(u => searchParameters.UnitIds.Contains(u.UnitId))
)
.GroupJoin(_ratingsContext.Checkups.AsNoTracking().Where(cc => !_cancelledStates.Contains(cc.State)), ms => ms.Id,
cc => cc.SecretBuyerId, (ms, cc) => new
{
MysteryShopper = ms,
LastCheckupDate = cc
.Max(c => c.Date)
}
)
.GroupJoin(_ratingsContext.Messages.AsNoTracking().Where(m =>
m.Type == OutgoingMessageType.CandidateSearch
&& (m.State == OutgoingMessageState.Sending || m.State == OutgoingMessageState.Waiting)),
ms => ms.MysteryShopper.SocialNetworkId,
m => m.VkUserId,
(ms, m) =>
new {ms.MysteryShopper, ms.LastCheckupDate, HasPendingMessages = m.Any()})
.Where(ms => !ms.HasPendingMessages)
.Where(ms => ms.LastCheckupDate < lastCheckupEdgeDate)
.OrderBy(ms => ms.LastCheckupDate != null)
.ThenBy(ms => ms.MysteryShopper.LastSearchMessageDateUtc != null)
.ThenBy(ms => ms.MysteryShopper.LastSearchMessageDateUtc)
.Select(ms => ms.MysteryShopper)
.ToArrayAsync(ct);
}
After running this query I got error:
... by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
After that I discovered from this question that you can't make group by queries evaluated on client anymore.
After more searching I've discovered another solution. So i've re-written my method like this:
private async Task<IEnumerable<MysteryShopper>> FindMysteryShoppers(
SearchMysteryShoppersParameters searchParameters, CancellationToken ct)
{
var lastSearchEdge = _nowProvider.UtcNow().Date.AddDays(-3);
bool shouldHaveDeliveryCheckMark = ShouldHaveDeliveryCheckMark(searchParameters.SearchType);
bool shouldHaveRestaurantCheckMark = ShouldHaveRestaurantCheckMark(searchParameters.SearchType);
bool shouldHaveStandardsCheckMark = ShouldHaveStandardsCheckMark(searchParameters.SearchType);
var lastCheckupEdgeDate = searchParameters.CheckupDates.Max().AddDays(-30);
var mysteryShoppersLeftJoinedWithCheckups = from ms in _ratingsContext.Set<MysteryShopper>()
where ms.MysteryShopperUnits
.Any(u => searchParameters.UnitIds.Contains(u.UnitId)
&& !shouldHaveStandardsCheckMark && ms.StandardsRatingCheck
&& !shouldHaveRestaurantCheckMark && ms.RestaurantCheck
&& !shouldHaveDeliveryCheckMark && ms.DeliveryCheck
&& !ms.IsBanned
&& !ms.IsFraud
&& ms.IsAutoSearchable
&& ms.Suitable
&& !ms.IsRemove
&& ms.SocialNetworkMessagingEnable
&& (ms.LastSearchMessageDateUtc == null || ms.LastSearchMessageDateUtc < lastSearchEdge))
join cc in _ratingsContext.Set<Checkup>()
on ms.Id equals cc.SecretBuyerId into checkups
from cc in checkups.DefaultIfEmpty()
where !_cancelledStates.Contains(cc.State)
select new {MysteryShopper = ms, LastCheckupDate = checkups.Max(c => c.Date)};
var mysteryShoppersLeftJoinedWithCheckupsAndMessages =
from mysteryShopperWithCheckup in mysteryShoppersLeftJoinedWithCheckups
join m in _ratingsContext.Set<Message>()
on mysteryShopperWithCheckup.MysteryShopper.SocialNetworkId equals m.VkUserId into messages
from m in messages.DefaultIfEmpty()
where m.Type == OutgoingMessageType.CandidateSearch && (m.State == OutgoingMessageState.Sending ||
m.State == OutgoingMessageState.Waiting)
select new
{
mysteryShopperWithCheckup.MysteryShopper,
mysteryShopperWithCheckup.LastCheckupDate,
HasPendingMessages = messages.Any()
};
var orderedMysteryShoppersWithoutPendingMessage =
from mysteryShopperWithCheckupAndMessage in mysteryShoppersLeftJoinedWithCheckupsAndMessages
where !mysteryShopperWithCheckupAndMessage.HasPendingMessages &&
mysteryShopperWithCheckupAndMessage.LastCheckupDate < lastCheckupEdgeDate
orderby mysteryShopperWithCheckupAndMessage.LastCheckupDate != null,
mysteryShopperWithCheckupAndMessage.MysteryShopper.LastSearchMessageDateUtc != null,
mysteryShopperWithCheckupAndMessage.MysteryShopper.LastSearchMessageDateUtc
select mysteryShopperWithCheckupAndMessage.MysteryShopper;
return await orderedMysteryShoppersWithoutPendingMessage.ToArrayAsync(ct);
}
But now i get another error:
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
After some more testing i know for sure that checkups.Max(c => c.Date)} and HasPendingMessages = messages.Any() is the source of this error. How to fix this query? You can suggest completely different solution or approach. The main idea is to bring logic from sql to c#.
PS: Sorry for long question.
Added navigation properties and nugget Z.EntityFramework.Plus (for IncludeFilter) and rewritten query like this:
var query = _ratingsContext.SecretBuyers
.AsNoTracking()
.IncludeFilter(ms => ms.Checkups.Where(cc => !_cancelledStates.Contains(cc.State)))
.IncludeFilter(ms => ms.Messages.Where(m => m.Type == OutgoingMessageType.CandidateSearch &&
(m.State == OutgoingMessageState.Sending ||
m.State == OutgoingMessageState.Waiting)))
.Where(ms => !ms.IsBanned
&& !ms.IsFraud
&& ms.IsAutoSearchable
&& ms.Suitable
&& !ms.IsRemove
&& ms.SocialNetworkMessagingEnable
&& (ms.LastSearchMessageDateUtc == null || ms.LastSearchMessageDateUtc < lastSearchEdge)
)
.Where(ms => !shouldHaveDeliveryCheckMark || ms.DeliveryCheck)
.Where(ms => !shouldHaveRestaurantCheckMark || ms.RestaurantCheck)
.Where(ms => !shouldHaveStandardsCheckMark || ms.StandardsRatingCheck)
.Where(ms => ms.MysteryShopperUnits
.Any(u => searchParameters.UnitIds.Contains(u.UnitId))
)
.Where(ms => !ms.Messages.Any())
.Select(x => new {MysteryShopper = x, LastCheckupDate = x.Checkups.Max(c => c.Date)})
.Where(ms => ms.LastCheckupDate < lastCheckupEdgeDate)
.OrderBy(x => x.LastCheckupDate != null)
.ThenBy(x => x.MysteryShopper.LastSearchMessageDateUtc != null)
.ThenBy(x => x.MysteryShopper.LastSearchMessageDateUtc)
.Select(x => x.MysteryShopper)
.Take(searchParameters.MessagesPerUnit);
How can I write this with where clause where CreatedDate is in between Date1var & Date2var?
return _context.Logs.Where(x => x.ApplicationID == applicationId)
.OrderByDescending(x => x.ID)
.Take(count)
.Select(record => new LoggingLogic.entities.Log
{
DataL = record.Data,
LogMessage = record.Message,
CreatedDate = record.CreateDate,
ApplicationName = record.Application.Name,
Environment = record.Application.Environment.EnvironmentName,
ID = record.ID
});
Change your Where() to Where(x => x.ApplicationID = applicationId && x.CreatedDate >= Date1var && x.CreatedDate <= Date2var)
I'm assuming that you want the range to be inclusive.
try this
return _context.Logs.Where(x => x.ApplicationID == applicationId && x.CreatedDate >= Date1Var && x.CreatedDate <= Date2Var)
.OrderByDescending(x => x.ID)
.Take(count)
.Select(record => new LoggingLogic.entities.Log
{
DataL = record.Data,
LogMessage = record.Message,
CreatedDate = record.CreateDate,
ApplicationName = record.Application.Name,
Environment = record.Application.Environment.EnvironmentName,
ID = record.ID
});