For code shown below, I'm wondering is there a more efficient way of assigning the Status and Types in the select statement? There is no relationship between the contract and the statuses/types as the contract items are coming from an API call and the statuses/types are from a local database.
The part in question is
Status = statuses.FirstOrDefault(y => y.StatusId == x.StatusId)
Type = types.FirstOrDefault(y => y.TypeId == x.TypeId)
Is there a better way of assigning these?
var statuses = this.StatusRepository.GetActiveStatuses().ToList();
var types = this.TypeRepository.GetActiveTypes().ToList();
var contracts = this.ContractApi.GetCurrentContracts().Select(x => new ContractItem {
Id = x.Id,
Name = x.Name,
Status = statuses.FirstOrDefault(y => y.StatusId == x.StatusId) ?? Status.Empty(),
Type = types.FirstOrDefault(y => y.TypeId == x.TypeId) ?? Type.Empty()
});
For better performance you should use a dictionary or lookup:
var statuses = this.StatusRepository.GetActiveStatuses().ToLookup(x => x.StatusId);
var types = this.TypeRepository.GetActiveTypes().ToLookup(x => x.TypeId);
var emptyStatus = Status.Empty();
var emptyType = Type.Empty();
var contracts = this.ContractApi.GetCurrentContracts()
.Select(x => new ContractItem {
Id = x.Id,
Name = x.Name,
Status = statuses[x.StatusId].DefaultIfEmpty(emptyStatus).First(),
Type = types[x.TypeId].DefaultIfEmpty(emptyType).First()
});
The lookup is more readable because it enables to use DefaultIfEmpty
If you know they will exist in your local database you could store them in a Dictionary like;
var statusDict = this.StatusRepository.GetActiveStatuses().ToDictionary(s => s.StatusId);
....
Status = statusDict[x.StatusId]
Related
I'm a newbie here. I'm using c# MVC 5.
I'll try to get all id with status of "Picked-up" in table1 and save into table2
this is my error.
Unable to cast object of type 'WhereSelectListIterator`2[ChenvelIntl.Core.Domain.Packages.Package,System.Int32]' to type 'System.IConvertible'.
This is my code. but But it's not working. Can someone help me on how to solve this?
var package = _packageService.GetAll().Where(x => x.Status == "Picked-up");
var shipmentItem = new ShipmentItem
{
ShipmentId = model.ShipmentId
};
if (package != null)
{
shipmentItem.PackageId = Convert.ToInt32(package.Select(x => x.Id));
}
_shipmentItemService.Add(shipmentItem);
return RedirectToAction("addshipmentitem");
Your package variable would contain a WhereSelectListIterator or an object that links to MANY packages.
Change it to .First(), e.g.
var package = _packageService.GetAll().First(x => x.Status == "Picked-up");
and then below:
// If package.Id is not an Int32 already...
shipmentItem.PackageId = Convert.ToInt32(package.Id);
If you need all, change the code like so:
var packages = _packageService.GetAll().Where(x => x.Status == "Picked-up").ToArray().Where(x => x != null).ToArray();
foreach(var package in packages) {
var shipmentItem = new ShipmentItem
{
ShipmentId = model.ShipmentId,
PackageId = Convert.ToInt32(package.Id)
};
_shipmentItemService.Add(shipmentItem);
}
return RedirectToAction("addshipmentitem");
just do it :
var shipmentItems= _packageService.GetAll()
.Where(x => x.Status == "Picked-up")
.Select(x=>new ShipmentItem(){ShipmentId = x.ShipmentId , PackageId=int.Pars(x.Id)});
if(shipmentItems.Any())
_shipmentItemService.Add(shipmentItems);
return RedirectToAction("addshipmentitem");
if _shipmentItemService.Add() only receives one shipmentItem to add , do this :
shipmentItems.ToList().ForEach(x=>_shipmentItemService.Add(x))
int[] packageIds = _packageService.GetAll().Where(x => x.Status == "Picked-up").Select(x=>x.Id).ToArray();
your will get an int array of packageids
then loop throw the array and add your shipment item one by one..
I hope it will work..
foreach (var packageId in packageIds)
{
var shipmentItem = new ShipmentItem
{
ShipmentId = model.ShipmentId,
PackageId = Convert.ToInt32(packageId)
};
_shipmentItemService.Add(shipmentItem);
}
If you would split your functions and change var into the actual types, you would immediately see your problem:
IEnumerable<Package> fetchedPackages = _packageService.GetAll();
IEnumerable<Package> pickedUpPackages = fetchedPackages
.Where(package => package.Status == "Picked-up");
and later:
IEnumerable<int> pickedUpPackageIds = pickedupPPages
.Select(pickedUpPackage => pickedUpPackage.Id);
shipmentItem.PackageId = Convert.ToInt32(pickedUpPackageIds);
It is easy to see that Convert.ToInt32 does not know how to handle a sequence of pickedUpPackageIds
What you should change, depends on what you want.
From the identifier name, I get the impression that you expect that there is at utmost one Package with status "Picked-Up":
int pickedUpPackageId = _packageService.GetAll()
.Where(package => package.Status == "Picked-up")
.Select(package => package.Id)
.FirstOrDefault(); // or SingleOrDefault
shipmentItem.PackageId = Convert.ToInt32(pickedUpPackage.Id);
Note: if there is no such pickedUpPackageId, the value will be zero
Why am I getting only one entry in DownTimeDetails list even though in Data we have 3 entries.
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Join(dbEntity.DownTimeDetails, dth => dth.DownTimeHeaderID, dtd => dtd.DownTimeHeaderID, (dth, dtd) => new { dth, dtd })
.Where(x => x.dth.DownTimeHeaderID == 42)
.GroupBy(gx => gx.dtd.DownTimeDetailID)
.Select(t => new VehicleEventDetails()
{
BookingId = t.Select(a => a.dth.BookingId).FirstOrDefault(),
DownTimeDetails = t.Select(ab => new DownTimeDetails
{
LocalDTStartTime = (DateTime)ab.dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)ab.dtd.LocalDTEndTime,
CalculatedEventDTReason = ab.dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)ab.dtd.CalculatedEventDT,
}).ToList()
}).FirstOrDefault();
You are looking for something like this:
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Where(x => x.DownTimeHeaderID == 42)
.Select(x => new VehicleEventDetails
{
BookingId = x.BookingId,
DownTimeDetails = x.DownTimeDetails
.Select(dtd=> new DownTimeDetails
{
LocalDTStartTime = (DateTime)dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)dtd.LocalDTEndTime,
CalculatedEventDTReason = dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)dtd.CalculatedEventDT,
})
.ToList()
})
.FirstOrDefault();
Notes:
Using .Join is an anti-Entity Framework pattern. Always try to use navigation properties, they exist for a reason.
Don't use .GroupBy unless you actually need a group. You don't want any grouping in this query.
As a general note, try not to make the expression variable names so confusing.
I have seen multiple questions that are similar to this one but I think my case is slightly different. I'm using EF6 to query the database and I'm using data projection for better queries.
Given that performance is very important on this project I have to make sure to just read the actual fields that I will use so I have very similar queries that are different for just a few fields as I have done this I have noticed repetition of the code so I'm been thinking on how to reuse code this is currently what I Have:
public static IEnumerable<FundWithReturns> GetSimpleFunds(this DbSet<Fund> funds, IEnumerable<int> fundsId)
{
IQueryable<Fund> query = GetFundsQuery(funds, fundsId);
var results = query
.Select(f => new FundWithReturns
{
Category = f.Category,
ExpenseRatio = f.ExpenseRatio,
FundId = f.FundId,
Name = f.Name,
LatestPrice = f.LatestPrice,
DailyReturns = f.FundDailyReturns
.Where(dr => dr.AdjustedValue != null)
.OrderByDescending(dr => dr.CloseDate)
.Select(dr => new DailyReturnPrice
{
CloseDate = dr.CloseDate,
Value = dr.AdjustedValue.Value,
}),
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
})
.ToList();
foreach (var result in results)
{
result.DailyReturns = result.DailyReturns.ConvertClosingPricesToDailyReturns();
}
return results;
}
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
}).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
I'm trying to reuse the part where I map the f.Returns so I tried created a Func<> like the following:
private static Func<Return, ReturnValues> MapToReturnValues = r => new ReturnValues
{
Daily = r.AdjDaily,
FiveYear = r.AdjFiveYear,
MTD = r.AdjMTD,
OneYear = r.AdjOneYear,
QTD = r.AdjQTD,
SixMonth = r.AdjSixMonth,
ThreeYear = r.AdjThreeYear,
YTD = r.AdjYTD
};
and then use like this:
public static IEnumerable<FundListVm> GetFundListVm(this DbSet<Fund> funds, string type)
{
return funds
.Where(f => f.StatusCode == MetisDataObjectStatusCodes.ACTIVE
&& f.Type == type)
.Select(f => new FundListVm
{
Category = f.Category,
Name = f.Name,
Symbol = f.Symbol,
Yield = f.Yield,
ExpenseRatio = f.ExpenseRatio,
LatestDate = f.LatestDate,
Returns = f.Returns.Select(MapToReturnValues).FirstOrDefault()
}).OrderBy(f=>f.Symbol).Take(30).ToList();
}
The compiler is ok with it but at runtime, it crashes and says: Internal .NET Framework Data Provider error 1025
I tried to convert the Func into Expression like I read on some questions and then using compile() but It didn't work using AsEnumerable is also not an option because It will query all the fields first which is what I want to avoid.
Am I trying something not possible?
Thank you for your time.
It definitely needs to be Expression<Func<...>>. But instead of using Compile() method (not supported), you can resolve the compile time error using the AsQueryable() method which is perfectly supported (in EF6, the trick doesn't work in current EF Core).
Given the modified definition
private static Expression<Func<Return, ReturnValues>> MapToReturnValues =
r => new ReturnValues { ... };
the sample usage would be
Returns = f.Returns.AsQueryable().Select(MapToReturnValues).FirstOrDefault()
I am selecting data from a data store
I am able to fetch first array [0] {IHSWCFService.ServiceReference1.Observation} using below query
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
PeriodData = Convert.ToDateTime(a.ObservationVector.Select(x => x.Period).FirstOrDefault()),
StatusID = Convert.ToInt32(a.ObservationVector.Select(x => x.StatusId).ToList()),
Price = Convert.ToDouble(a.ObservationVector.Select(x => x.price).FirstOrDefault()),
});
But I want to select next array also. as showing in below screen screenshot
[0]{IHSWCFService.ServiceReference1.Observation}
[1]{IHSWCFService.ServiceReference1.Observation}
[2]{IHSWCFService.ServiceReference1.Observation}
Could you please help me. Thanks
You might want all your properties in IHSData to be lists:
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
PeriodData = a.ObservationVector.Select(x => Convert.ToDateTime(x.Period)).ToList(),
StatusID = a.ObservationVector.Select(x => Convert.ToInt32(x.StatusId)).ToList(),
Price = a.ObservationVector.Select(x => Convert.ToDouble(x.price)).ToList(),
});
Which is not such a good idea, because you have to index them separately. So another option would be to use SelectMany:
var newData = data
.SelectMany(a => a.ObservationVector.Select(v =>
new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId), // parent PriceId
PeriodData = Convert.ToDateTime(v.Period),
StatusID = Convert.ToInt32(v.StatusId),
Price = Convert.ToDouble(v.price),
}))
.ToList();
The latter approach will create a separate IHSData instance for each ObservationVector, and some of them will share the same PriceId of the parent class.
Or, the third approach would be to have a new class, which would be the "parsed version of the ObservationVector", i.e. contain properties for parsed values, something like:
var newData = data.Select(a => new IHSData
{
PriceSymbol = Convert.ToString(a.PriceId),
Data = a.ObservationVector.Select(x => ConvertObservationVector(x)).ToList()
});
where ConvertObservationVector is a method which converts from an ObservationVector to your parsed class.
Basically, if the user selected no option from the dropdown combo, I want it to be left out from my Linq query that looks something like this:
// this is how I manage the form post data, please
// propose a better way if you know one
Dictionary<string, string> formdata = new Dictionary<string, string>();
foreach(string key in Request.Form.AllKeys)
{
formdata.Add(key, Request.Form[key]);
}
// getting the title
string title = "";
formdata.TryGetValue("postedTitle", out title);
// getting the level
string levelString = "";
formdata.TryGetValue("postedLevel", out levelString );
int level = -1;
if(levelString != "")
{
Int32.TryParse(levelString , out level);
}
var model = new FooIndexVM
{
Foos = _ctx.SomeDbSet.Where(w => w.Title.Contains(title) && w.Level == (Level?)level.Value).Select(x => new FooBarRow
{
FooBarId = x.Id,
....
Since I'm getting either 0 or -1 for the level -- I need a way to gracefully leave the Enum part from the query completely. I will also later add some additional fields similar to this one (may be unselected) so the solution will also work for those, I guess.
You can chain Where commands so this line:
Foos = _ctx.SomeDbSet.Where(w => w.Title.Contains(title) && w.Level == (Level?)level.Value).Select(x => new FooBarRow
{
FooBarId = x.Id,
....
Could be rewritten to be this without changing its behaviour (multiple Wheres effectively become combined with &&s):
Foos = _ctx.SomeDbSet.Where(w => w.Title.Contains(title)).Where(w => w.Level == (Level?)level.Value).Select(x => new FooBarRow
{
FooBarId = x.Id,
....
This then means that you can add some logic around whether to apply the second Where or not like this, for example:
var query = _ctx.SomeDbSet.Where(w => w.Title.Contains(title));
if (level != -1)
{
query = query.Where(w => w.Level == (Level?)level.Value)
}
Foos = query.Select(x => new FooBarRow
{
FooBarId = x.Id,