Convert if and foreach statement to select and where in linq - linq

How would I go about changing my if statement and foreach to something cleaner in linq using select and where.
I've tried to make the if statement into a where clause and then use the select query as a replacement for the Foreach loop but that seem to have type issues and wasn't working.
{
StripeConfiguration.ApiKey = _appSettings.StripeSecretKey;
var profile = await _userManager.FindByIdAsync(customerServiceID);
var stripeId = profile.StripeAccountId;
if (stripeId == null)
throw new ArgumentException("No associated Stripe account found.");
List<PaymentMethodDto> result = new List<PaymentMethodDto>();
var options = new PaymentMethodListOptions
{
Customer = stripeId,
Type = "card",
};
var service = new PaymentMethodService();
var payments = await service.ListAsync(options);
if (payments != null && payments.Data?.Count > 0)
{
payments.Data.ForEach((x) =>
{
result.Add(
new PaymentMethodDto
{
Brand = x.Card.Brand,
LastDigits = x.Card.Last4,
StripeToken = x.Id,
CustomerID = x.CustomerId
});
});
}
return result;
}

Just do a regular Select.
List<PaymentMethodDto> result = payments.Data.Select(x => new PaymentMethodDto
{
Brand = x.Card.Brand,
LastDigits = x.Card.Last4,
StripeToken = x.Id,
CustomerID = x.CustomerId
})
.ToList();
If payments.Data has nothing in it, this will give you an empty list, which is what you want.
If payments is null, you'll get an exception, which I think if you think about it really hard is probably what you really want in that case too. Why would .ListAsync() yield a null value?

Related

EF Core ToDictionary throw expression could not be translated

I have this query:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
}).ToDictionary(y => y.NodeId, y => y)
}).SingleOrDefaultAsync();
and also tried like this:
Project = await Context.Projects.Where(x => x.Id == project.Id)
.Select(x => new ProjectModel
{
Id = project.Id,
Name = project.Name,
TreeDataDict = x.Tasks.ToDictionary(y => y.Id, y => new TreeItemModel(
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
}).SingleOrDefaultAsync();
Both ways I got this exception:
What could be causing this? and is there a way I could make this work without fetching as a list and then covert it to dictionary? What could be the most efficient way to achieve this?
Regards
What could be causing this?
Translation of the nested ToDictionary call (none of the available overloads) is not supported. Consider it one of the (many) current (latest at the time of writing official v5.0.11) EF Core shortcomings.
Interestingly though, the Dictionary<TKey, TValue> constructor overload with IEnumerable<KeyValuePair<TKey, TValue>> argument as well as projection (Select) to KeyValuePair<TKey, TValue> is supported, which gives the workaround - replace the ToDictionary call with the aforementioned constructor and Select, e.g. (replace int with the type of the Id)
TreeDataDict = new Dictionary<int, TreeItemModel>(
x.Tasks.Select(y => new TreeItemModel()
{
NodeId = y.Id,
ParentId = SetTaskParentId(y, y.Type),
NodeTitle = y.Name,
Expanded = false,
Object = new TaskBaseModel
{
Milestone = y.Milestone,
StartDate = y.StartDate,
CurrentEndDate = y.CurrentEndDate,
EndDate = y.EndDate,
},
Icon = TaskHelper.GetTaskIcon(y.Type),
Children = new List<TreeItemModel>()
})
.Select(e => new KeyValuePair<int, TreeItemModel>(e.NodeId, e)));
This will fix the current error. But note that you are using other non-translatable constructs (custom method calls like SetTaskParentId(y, y.Type) and TaskHelper.GetTaskIcon(y.Type)) which are supported only in the final Select, so make sure to not add LINQ operator like Where, OrderBy etc. after the root query Select(x => new ProjectModel { ... }, otherwise you'll get other runtime errors. ToList, `First

IQueryable.Union/Concat in .net core 3

I want to add a dummy member to an IQueryable and came up with this solution:
IQueryable<Geography> geographies = _unitOfWork.GeographyRepository.GetAll(); //DbSet<Geography>
var dummyGeographies = new Geography[] { new Geography { Id = -1, Name = "All" } }.AsQueryable();
var combinedGeographies = geographies.Union(dummyGeographies);
var test = combinedGeographies.ToList(); //throws runtime exc
But it throws the following exception:
Processing of the LINQ expression 'DbSet
.Union(EnumerableQuery { Geography, })' by 'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core.
How could I make it work?!
you can only union on data structure which are the same
IQueryable is only applicable if the query expression not been been expressed (ToList) before its run against db and you want the expression modifiable . aka nothing which which is not going to db as a query needs to be IQueryable (simple explanation better to research and understand this yourself)
List<Geography> geographies = _unitOfWork.GeographyRepository
.GetAll() //DbSet<Geography>
.Select(o => new Geography { Id = o.Id, Name = o.Name })
.ToList();
List<Geography> dummyGeographies = new List<Geography>() {
new Geography[] { new Geography { Id = -1, Name = "All" } }
};
var combinedGeographies = geographies.Union(dummyGeographies);
var test = combinedGeographies.ToList();
I was able to achieve it with the following code:
IQueryable<Geography> geographies = _unitOfWork.GeographyRepository.GetAll().Select(o => new Geography { Id = o.Id, Name = o.Name });
IQueryable<Geography> dummyGeographies = _unitOfWork.GeographyRepository.GetAll().Select(o => new Geography { Id = -1, Name = "All" });
var combinedGeographies = geographies.Union(dummyGeographies);

Linq CopyToDataTable using extension methods

Hello I'm trying to copy the following linq results to a datatable. The only examples I see of copytodatatable is using the query format, and not the extension methods. Wondering if anyone knows how to use it with the extension methods (I've tried casting the results to IEnumerable datarow but it didn't work).
DataTable dtItemPricingBreakDown =
SiteHelper.getItemPricingBreakDown(CustomerId,
PriceBookID,
deliveryZip,
dtItems,
db,
departmentId);
dtItemPricingBreakDown.AsEnumerable()
.GroupBy(i => new {
sku = i.Field<string>("sku"),
deptid = i.Field<int>("department_id")
})
.Select(group => new
{
sku = group.Key.sku,
deptid = group.Key.deptid,
cnt = group.Count()
});
Update
Better late than never, sorry for the delay in response. My actual issue appears to be for both the query syntax and the extension methods.
Something like this works according to msdn
(http://msdn.microsoft.com/en-us/library/bb386921(v=vs.110).aspx):
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);
DataTable orders = ds.Tables["SalesOrderHeader"];
DataTable details = ds.Tables["SalesOrderDetail"];
var query =
from order in orders.AsEnumerable()
join detail in details.AsEnumerable() on order.Field<int>("SalesOrderID") equals detail.Field<int>("SalesOrderID")
where order.Field<bool>("OnlineOrderFlag") == true
&& order.Field<DateTime>("OrderDate").Month == 8
select new
{
SalesOrderID = order.Field<int>("SalesOrderID"),
SalesOrderDetailID = detail.Field<int>("SalesOrderDetailID"),
OrderDate = order.Field<DateTime>("OrderDate"),
ProductID = detail.Field<int>("ProductID")
};
DataTable orderTable = query.CopyToDataTable();
But when I try this...
var query = from exx in dtItemPricingBreakDown.AsEnumerable()
group exx by new { sku = exx.Field<string>("sku"), companyId = exx.Field<int>("department_id") } into grp
select new { sku = grp.Key.sku, DepartmentID = grp.Key.companyId, Cnt = grp.Count() };
DataTable dt3 = query.CopyToDataTable();
I get this exception: "No implicit reference conversion from anonymoustype1 to system.data.datarow". I've tried doing what they did with the dataset in the msdn example as well and I still get the error. Extension method wise I was trying something like this...and still got the same exception.
DataTable dt3 = dtItemPricingBreakDown.AsEnumerable().GroupBy(i => new
{
sku = i.Field<string>("sku"),
deptid = i.Field<int>("department_id")
}).Select(group => new
{
sku = group.Key.sku,
DepartmentID = group.Key.deptid,
cnt = group.Count()
}).Cast<DataRow>().CopyToDataTable();

Problem returning an IEnumerable<T>/IList<T>

I am having trouble to return an IEnumerable and IList, I cant do it!
I am using EF 4 with POCOs
Here's the whole method:
//public IList<Genre> GetGenresByGame(int gameId)
public IEnumerable<Genre> GetGenresByGame(int gameId)
{
using(var ctx = new XContext())
{
var results =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new Genre
{
GenreId = t0.GenreId,
GenreName = t1.GenreName
};
return results.ToList();
}
}
I have tried different ways that I have found on the net.. but can't make it work!
Question 2:
I saw a screencast with Julie something, saying that "You should always return an ICollection" when using EF4.
Any thoughts about that ?
BR
EDIT:
When I load the page in Debug-mode i get these errors: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. OR The entity or complex type 'XModel.Genre' cannot be constructed in a LINQ to Entities query.
Genre must not be a L2EF type. Try this:
public IEnumerable<Genre> GetGenresByGame(int gameId)
{
using(var ctx = new XContext())
{
var resultList =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new { t0.GenreId, t1.GenreName };
var genres = resultList.AsEnumerable().Select(o => new Genre
{
GenreId = o.GenreId,
GenreName = o.GenreName
});
return genres.ToList();
}
}
First an foremost if Genre is in the database you should select it? If you have FKs from Genre->GenreCultureDetails let me know and I can update the below, but from the looks of it you could do it like this:
using(var ctx = new XContext())
{
var results =
from g in ctx.Genre
join gcd in ctx.GenreCultureDetails on g.GenreId equals gcd.GenreId
where g.GameId == gameId && gcd.CultureId == _cultureId
select g;
return result.ToList();
}
Alternatively continue down your path select them into an annoynmous type, and then copy them. You can use select instead of convertall if you please.
IList<Genre> returnMe = Null;
using(var ctx = new XContext())
{
var results =
from t0 in ctx.GameGenres
join t1 in ctx.GenreCultureDetails on t0.GenreId equals t1.GenreId
where t0.GameId == gameId && t1.CultureId == _cultureId
select new
{
GenreId = t0.GenreId,
GenreName = t1.GenreName
};
returnMe = results.ToList().ConvertAll(x=>new Genre(){
GenreId = x.GenreId,
GenreName = x.GenreName
}
);
}
return returnMe;

LINQ: Group By + Where in clause

I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};

Resources