access an array element inside a LINQ to Entities set - c#

I am using a Split method on a string inside a Linq query but only need the second element. I get a "System.InvalidOperationException: 'Unrecognized expression node: ArrayIndex'" on the below code:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
UniqueIdNo = rma.UniqueIdNo,
RmaNumber = line.RmaNumber,
RmaOriginalUniqueId = line.RmaOriginalUniqueId,
ItemSequenceNo = line.ItemSequenceNo,
ItemNumber = line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
rmaGood = line.RmaNumber.Split(new string[] { "/" }, StringSplitOptions.None)[1]
}).ToArray();
if I remove the [1] on the array it works and I can access both elements fine for the entire dataset with:
foreach (var item in RMA_stops_all)
{
var right = RMA_stops_all.First().rmaGood[1];
var left = RMA_stops_all.First().rmaGood[0];
Console.WriteLine("left {0} - right{1} ",left.ToString(), right.ToString());
}
EDIT - duh. The above "test" was completely useless (as gently pointed out) - however, the below does prove out that it is working correctly (some of the returned values only have 1 element, hence the additional if block - the output is as expected:
foreach (var item in RMA_stops_all)
{
string right, left;
if (item.rmaGood.Length == 1)
{
left = item.rmaGood[0].ToString();
right = "Not there";
}
else
{
left = item.rmaGood[0].ToString();
right = item.rmaGood[1].ToString();
}
Console.WriteLine("left {0} - right{1} ", left, right);
}
SQL Output from dbMonitor:
SELECT t2.datetime_created AS "DatetimeCreated",
t1.unique_id_no AS "UniqueIdNo",
t2.rma_number AS "RmaNumber",
t2.rma_original_unique_id AS "RmaOriginalUniqueId",
t2.item_sequence_no AS "ItemSequenceNo",
t2.item_number AS "ItemNumber",
(t2.rma_number LIKE :p3) OR (t2.rma_number LIKE :p4) AS "C1",
t2.rma_number AS "RmaNumber1"
FROM cops_reporting.distribution_stop_information t1
INNER JOIN cops_reporting.distribution_line_items t2
ON t1.unique_id_no = t2.unique_id_no
WHERE (t2.rma_number IS NOT NULL)
AND (t2.datetime_created > :p0)
AND (t2.datetime_created < :p1)
AND (t1.customer_no = :p2)

I didn't realize Linq to Entities had access to some SQL functions directly, so this may work for you:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
UniqueIdNo = rma.UniqueIdNo,
RmaNumber = line.RmaNumber,
RmaOriginalUniqueId = line.RmaOriginalUniqueId,
ItemSequenceNo = line.ItemSequenceNo,
ItemNumber = line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
rmaGood = line.RmaNumber.Substring(line.RmaNumber.IndexOf("/")+1)
}).ToArray();
assuming that RmaNumber looks like #/# and doesn't have additional slashes you want to avoid.

I don't see why it would work without the index operation either, and your SQL doesn't seem to reflect the same Linq code (have you used LinqPad?), but I would suggest since you are doing .ToArray() anyway, moving the split/index to the client:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
rma.UniqueIdNo,
line.RmaNumber,
line.RmaOriginalUniqueId,
line.ItemSequenceNo,
line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
}).AsEnumerable().Select(r => new {
r.dtCreated,
r.UniqueIdNo,
r.RmaNumber,
r.RmaOriginalUniqueId,
r.ItemSequenceNo,
r.ItemNumber,
r.goodRMA_flag,
rmaGood = r.RmaNumber.Split(new string[] { "/" }, StringSplitOptions.None)[1]
});
BTW, you don't have to repeat field/property names for a simple accessor expression so I removed them from your original select. Also, if you can live with the double indirection, you could just include r in the second select.

Related

The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities

I have implemented a linq expression to return a resultset and getting the following error
{"The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities."}
public IEnumerable<TBI.JV.Business.Objects.Asset> GetAssetsBasicBySedols(string[] sedols)
{
var priceDate = DateTime.UtcNow.Date.AddMonths(-8);
var typeList = new string[]
{
"UNIT TRUST",
"OEIC",
"INVESTMENT TRUST",
"INVESTMENT COMPANY",
"PENSION FUND",
"INSURANCE BOND",
"LISTED EQUITY",
"PREFERENCE SHARE",
"ZERO DIVIDEND PREF",
"GILT (CONVENTIONAL)",
"GILT (INDEX LINKED)",
"AIM",
"VCT",
"OFFSHORE FUND",
"ETP"
};
using (var dealingContext = new dbDealingContainer())
{
return (from fundprice in dealingContext.FundPrices
where (fundprice.FUND_STATUS == "ACTIVE" || fundprice.FUND_STATUS == "SUSPENDED") &&
(fundprice.INVNAME != null || fundprice.INVNAME != "") &&
!fundprice.INVNAME.StartsWith("IFSL Bestinvest") &&
// fundprice.WaterlooTradable == true &&
fundprice.BID_MID_PRICE > 0 && typeList.Contains(fundprice.FUND_TYPE)
&& ((sedols.Length > 0 && sedols.Contains(fundprice.SEDOL_NUMBER))
||sedols.Contains(fundprice.SEDOL_NUMBER_ACC)) || sedols.Length == 0
select new TBI.JV.Business.Objects.Asset
{
AssetName = fundprice.INVNAME,
AssetId = fundprice.Id,
AssetType = fundprice.FUND_TYPE,
Epic = fundprice.INVESTMENT_CODENAME,
StarRating = fundprice.STARLEN,
Sedol = fundprice.SEDOL_NUMBER,
SedolAcc = fundprice.SEDOL_NUMBER_ACC
}).ToList();
}
}
The error is thrown at the following line of code sedols.Length > 0 and also sedols.Length == 0. How do I resolve this. My method should be able to take an empty string array as input and return all records.
Define two variables above the query and than use them instead in the query:
var isGreaterThanZero = sedols.Length > 0;
var isEmpty = sedols.Length == 0;
The Any() extension method is supported by LINQ to Entities.
Using exampleArray.Any() and !exampleArray.Any() means you don't have to declare local variables and fortunately it's a nice, succinct syntax.

Expression predicate must be built separately with if and else blocks?

I have 2 versions of codes for class method "GetUserRoles". Version 1 always works OK while Version 2 is not working if passed-in argument "exludeRoleNames" for that method is absent and exception "Null object reference" is thrown as a result. I would like to understand why Version 2 codes are not always working, and why Version 1 codes require building predicate with "if" and "else" blocks. Thank you in advance.
Version 1:
The following codes are always working. You could notice that there are separate "if" and "else" blocks to build local variable Expression<Func<UserRoleInApplication, bool>> predicate
public List<UserInRoleViewModel> GetUserRoles(long userId, params string[] exludeRoleNames)
{
List<UserInRoleViewModel> results = null;
IQueryable<UserInRoleViewModel> items = null;
Expression<Func<UserRoleInApplication, bool>> predicate = null;
if (exludeRoleNames.Count() <= 0)
{
predicate = x => true;
}
else
{
predicate = x => !exludeRoleNames.Contains(x.UserRole.RoleName);
}
items = from uir in _repository.GetQuery<UserInRole>(x => x.UserId == userId)
join ura in _repository.GetQuery<UserRoleInApplication>(predicate)
on uir.UserRoleInApplicationId equals ura.UserRoleInApplicationId
into g
from item in g
select new UserInRoleViewModel
{
UserInRoleId = uir.UserInRoleId,
UserId = uir.UserId,
UserRoleInApplicationId = uir.UserRoleInApplicationId
};
if (items != null && items.Any())
{
results = new List<UserInRoleViewModel>();
results = items.ToList();
}
return results;
}
Version 2:
However, the following codes throw exception "Null object reference" when calling method GetUserRoles such as GetUserRoles(long userId) without passed-in argument "exludeRoleNames". You could notice that there are no separate "if" and "else" blocks for the codes to form local variable Expression<Func<UserRoleInApplication, bool>> predicate:
public List<UserInRoleViewModel> GetUserRoles(long userId, params string[] exludeRoleNames)
{
List<UserInRoleViewModel> results = null;
IQueryable<UserInRoleViewModel> items = null;
// Note: one line code and no separate "if" and "else" blocks *************
Expression<Func<UserRoleInApplication, bool>> predicate = x => exludeRoleNames.Count() <= 0 ? true : !exludeRoleNames.Contains(x.UserRole.RoleName);
items = from uir in _repository.GetQuery<UserInRole>(x => x.UserId == userId)
join ura in _repository.GetQuery<UserRoleInApplication>(predicate)
on uir.UserRoleInApplicationId equals ura.UserRoleInApplicationId
into g
from item in g
select new UserInRoleViewModel
{
UserInRoleId = uir.UserInRoleId,
UserId = uir.UserId,
UserRoleInApplicationId = uir.UserRoleInApplicationId
};
if (items != null && items.Any()) // Note: throw exception "Null object reference" if parameter "exludeRoleNames" is absent on calling method GetUserRoles such as GetUserRoles(long userId);
{
results = new List<UserInRoleViewModel>();
results = items.ToList();
}
return results;
}
Try changing this:
Expression<Func<UserRoleInApplication, bool>> predicate = x => exludeRoleNames.Count() <= 0 ? true : !exludeRoleNames.Contains(x.UserRole.RoleName);
To this:
Expression<Func<UserRoleInApplication, bool>> predicate = x => true;
if(exludeRoleNames != null)
{
foreach(string exl in exludeRoleNames)
{
string temp = exl;
predicate = predicate.Or(x=>x.UserRole.RoleName == temp);
}
}
The problem is that you're trying to call Count() on exludeRoleNames - which is null. So, rather than check Count(), compare it to null. If it is null, then you can treat it as an empty array. If it isn't null, then check it's contents.
The other problem is that you can't use string[].Contains in a query context. Entity Framework (which I assume you are using) doesn't support that. So, you have to build out the predicate.
I re-write the following codes and they are working. In case of absent parameter "exludeRoleNames", the codes creat an empty string array:
public List<UserInRoleViewModel> GetUserRoles(long userId, params string[] exludeRoleNames)
{
List<UserInRoleViewModel> results = null;
IQueryable<UserInRoleViewModel> items = null;
exludeRoleNames = !exludeRoleNames.Any() ? new string[] { } : exludeRoleNames; // a MUST
Expression<Func<UserRoleInApplication, bool>> predicate = x => !exludeRoleNames.Contains(x.UserRole.RoleName);
items = from uir in _repository.GetQuery<UserInRole>(x => x.UserId == userId)
join ura in _repository.GetQuery<UserRoleInApplication>(predicate)
on uir.UserRoleInApplicationId equals ura.UserRoleInApplicationId
into g
from item in g
select new UserInRoleViewModel
{
UserInRoleId = uir.UserInRoleId,
UserId = uir.UserId,
UserRoleInApplicationId = uir.UserRoleInApplicationId
};
if (items != null && items.Any())
{
results = new List<UserInRoleViewModel>();
results = items.ToList();
}
return results;
}

Get index in a IQueryable result

I have a LINQ query:
var result = (from CC in hc.ClaimCodings
join CM in hc.ClaimCodingProcedureCodeModifierXrefs on CC.ClaimCodingID equals CM.ClaimCodingID
join PCM in hc.ProcedureCodeModifiers on CM.ProcedureCodeModifierID equals PCM.ProcedureCodeModifierID
where CC.CTCustomerSubID == custSub && CC.ClaimID == claimID
select new { PCM.ModifierCode });
EDIT
Which can return 0 to 4 items. I want to set the value of each Modifier code to a property:
public string ModCode1 { get; set; }
public string ModCode2 { get; set; }
public string ModCode3 { get; set; }
public string ModCode4 { get; set; }
Modcode1 = result.ModifierCode.getindex(0).firstordefault();
ModeCode2 = second result's ModifierCode;
etc
etc
Unless I'm approaching this completely wrong. I'm not so good with LINQ yet :(
Is this what you have in mind?
var result =
(
from CC in hc.ClaimCodings
join CM in hc.ClaimCodingProcedureCodeModifierXrefs
on CC.ClaimCodingID equals CM.ClaimCodingID
join PCM in hc.ProcedureCodeModifiers
on CM.ProcedureCodeModifierID equals PCM.ProcedureCodeModifierID
where CC.CTCustomerSubID == custSub && CC.ClaimID == claimID &&
PCM.ModifierCode != null
select PCM.ModifierCode
).ToList();
ModCode1 = result.Length > 0 ? result[0] : null;
ModCode2 = result.Length > 1 ? result[1] : null;
ModCode3 = result.Length > 2 ? result[2] : null;
ModCode4 = result.Length > 3 ? result[3] : null;
Main changes:
Added PCM.ModifierCode != null check within LINQ query.
Removed redundant anonymous type construction.
Converted LINQ query result to List<string> through ToList().
However, as BrokenGlass said, you’re probably better off storing a list.
You can call ToArray() on the results to immediately execute the query and return an array of the values. You can then assign values to the properties using the some ternary operator shorthand which checks the length of the array.
res = result.Take(4).ToArray();
Modcode1 = res.Length > 0 ? res[0] : null;
Modcode2 = res.Length > 1 ? res[1] : null;
Modcode3 = res.Length > 2 ? res[2] : null;
Modcode4 = res.Length > 3 ? res[3] : null;

Invalid initializer member declarator when projecting into complex types

I was initializing the object in the code below using simple properties but then refactored elsewhere such that DispatchedDocumentDate became DispatchedPhase.DocumentDate. I did this because there is also sold and picked classes using precisely the same properties.
So now in a referencing assembly I have this code which doesn't compile:
public List<ItemMovementEntry> FillItemDispatchMovements(IEntityDateRange imqp)
{
var f = from detail in this.Context.DispatchDetails
join header in this.Context.Dispatches on detail.ClientOrderNumber equals header.ClientOrderNumber
where (detail.ProductCode == imqp.ItemKey)
&& (header.DateOrdered >= imqp.StartDate)
&& (header.DateOrdered <= imqp.EndDate)
orderby header.DateOrdered descending
select new ItemMovementEntry(ItemMovementEntryKind.Dispatch)
{
DispatchedPhase.DocumentDate = ((header.DateOrdered.HasValue) ? header.DateOrdered.Value : new DateTime(1900, 1, 1)),
DispatchedPhase.DocumentLKey = header.ClientOrderNumber,
MaterialItemLkey = detail.ProductCode,
DispatchedPhase.MovementDeltaQty = ((detail.QuantityDelivered.HasValue) ? (-1) * detail.QuantityDelivered.Value : 0),
DispatchedPhase.Comment = string.Empty,
JournalType = "DISPATCHED",
};
return f.ToList<ItemMovementEntry>();
}
I get an :
Invalid initializer member declarator
error message.
Hopefully the intent is clear but I'm not sure how to rewrite. I Googled and got something about Let but it was still unclear.
At this point I will go with adding an extra constructor to the ItemMovementEntry class specifically to deal with this problem.
public ItemMovementEntry(ItemMovementEntryKind comparerMovementKind,
DateTime documentDate,
string documentLKey,
string materialItemKey,
int movementDeltaQty,
string comment)
: this(comparerMovementKind)
{
ItemMovementEntryPhase p = null;
switch (comparerMovementKind)
{
case ItemMovementEntryKind.Sales:
p = this.SoldPhase;
break;
case ItemMovementEntryKind.Picking:
p = this.PickedPhase;
break;
case ItemMovementEntryKind.Dispatch:
p = this.DispatchedPhase;
this.JournalType = "DISPATCHED";
break;
}
p.DocumentDate = documentDate;
p.DocumentLKey = documentLKey;
this.MaterialItemLkey = materialItemKey;
p.MovementDeltaQty = movementDeltaQty;
p.Comment = comment;
}
public List<ItemMovementEntry> FillItemDispatchMovements(IEntityDateRange imqp)
{
var f = from detail in this.Context.DispatchDetails
join header in this.Context.Dispatches on detail.ClientOrderNumber equals header.ClientOrderNumber
where (detail.ProductCode == imqp.ItemKey)
&& (header.DateOrdered >= imqp.StartDate)
&& (header.DateOrdered <= imqp.EndDate)
orderby header.DateOrdered descending
select new ItemMovementEntry(ItemMovementEntryKind.Dispatch,
((header.DateOrdered.HasValue) ? header.DateOrdered.Value : new DateTime(1900, 1, 1)),
header.ClientOrderNumber,
detail.ProductCode,
((detail.QuantityDelivered.HasValue) ? (-1) * detail.QuantityDelivered.Value : 0),
string.Empty){};
return f.ToList<ItemMovementEntry>();
}
Could you build properties in the immediate type that alias set? e.g. where you hold the complex type, make a property that has a setter for the child entity's property that you'd like to set.

how to assign linq result to multidimensional array

i want to assign the linq result to a multidimensional array and the below is my code, can any one tell me what i am doing wrong.
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
if(query.Count()>0)
{
string[,,] x = new string[4, 4,4];
x[,,] = query;
for (int i = 0; i < query.Length; i++)
{
if (i == 0)
{
imgBody1.ImageUrl = "FourBodyImages/" + x[0,0,0].ToString();
}
if (i == 1)
{
imgBody2.ImageUrl = "FourBodyImages/" + x[1,1,1].ToString();
}
if (i == 2)
{
imgBody3.ImageUrl = "FourBodyImages/" + x[2,2,2].ToString();
}
if (i == 3)
{
imgBody4.ImageUrl = "FourBodyImages/" + x[3,3,3].ToString();
}
}
}
If you just need to get 4 objects from the database, and then use them to fill 4 images, all you need to do is:
//this generates an array of (up to 4) objects of an anonymous type
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
//no need for count, we can use the array's length
if (query.Length < 4)
{
//i have no idea about your requirements,
//but if we have less than 4 images - panic
throw new InvalidOperationException()
}
//no need for the "for-if", that's an anti-patern
imgBody1.ImageUrl = "FourBodyImages/" + query[0].BodyImage;
imgBody2.ImageUrl = "FourBodyImages/" + query[1].BodyImage;
imgBody3.ImageUrl = "FourBodyImages/" + query[2].BodyImage;
imgBody4.ImageUrl = "FourBodyImages/" + query[3].BodyImage;
It's not clear why you want a three-dimensional array to start with. You're only taking 4 values... why is that not just a one-dimensional array?
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
var bodies = new[] { imgBody1, imgBody2, imgBody3, imgBody4 };
for (int i = 0; i < query.Length; i++)
{
// Or whichever property you're interested in...
bodies[i].ImageUrl = "FourBodyImages/" + query[i].BodyImage;
}
If this doesn't help, please give us more idea of what you're actually trying to achieve. Why does your query return three properties, for example, and how do you want to use them?

Categories