LINQ-2-SQL How-To "Reuse" sub selections - linq

Let's assume:
I have an EVENT table
I have a CLIENT table
And ADDRESS table have some column: UnitNo, StreetNo, StreetName, City, AddressType etc
A CLIENT has many EVENTs and CLIENT can have many ADDRESSes also
So if I want to query list of events with client HOME address street name, I just go
var qry = db.Events
.Select(evt => new {
EventAddress =
evt.Client
.Addresses
.FirstOrDefault(a => a.AddressType.Equals("HOME")).StreetName
});
However, if I want to get the full address I will need to concatenate sereval address fields. At the moment I am trying something like
var qry = db.Events
.Select(evt => new {
EventAddress =
evt.Client
.Addresses
.FirstOrDefault(a => a.AddressType.Equals("HOME")).StreetNo + " " +
evt.Client
.Addresses
.FirstOrDefault(a => a.AddressType.Equals("HOME")).StreetName + " " +
evt.Client
.Addresses
.FirstOrDefault(a => a.AddressType.Equals("HOME")).City
});
It doesn't work and looks ugly too
Is there a better way to make the
evt.Client.Addresses.FirstOrDefault(a => a.AddressType.Equals("HOME")) "reusable" so I can just go
var qry =
db.Events
.Select(evt => new {
EventAddress =
STUFF.StreetNo + " " + STUFF.StreetName + " " + STUFF.City
});
Many thanks in advance!
Hugh
UPDATE:
Thanks Ilian for the answer, it works well. And based on that I have created the extension version of the answer
var qry =
db.Events
.Select(evt => new {
EventAddress =
db.Addresses.Select(a => new
{
ClientId = a.ClientId,
AddressType = a.AddressType,
FullAddress = (a.addStreetNo ?? "") + (a.addStreetName ?? "")
})
.FirstOrDefault(a => a.ClientId == e.Client.ClientId && a.AddressType.Equals("HOME"))
.FullAddress
});

Use the query syntax:
var qry =
from evt in db.Events
let firstAddress = evt.Client.Addresses.FirstOrDefault(a => a.AddressType.Equals("HOME"))
where firstAddress != null
select new
{
EventAddress = firstAddress.StreetNo + " " +
firstAddress.StreetName + " " +
firstAddress.City
}

Related

LinqToDB Exception cannot be converted to SQL

Hello I have an issue with this query
var queryGrouped = queryFiltered
.GroupBy(c => new { c.Id, c.TableOneId, c.TableOneName, c.TableTwoId, c.TableTwoName, c.TableTwoCode, c.TableThreeId, c.TableThreeName, c.Description, c.EffectiveDate, c.CreatedBy, c.ServiceGroupName })
.DisableGuard()
.Select(cg => new Model
{
Id = cg.Key.Id,
TableOneId = cg.Key.TableOneId,
TableOneName = cg.Key.TableOneName,
TableTwoId = cg.Key.TableTwoId,
TableTwoCode = cg.Key.TableTwoCode,
TableTwoName = cg.Key.TableTwoName,
TableThreeId = cg.Key.TableThreeId,
TableThreeName = cg.Key.TableThreeName,
Description = cg.Key.Description,
EffectiveDate = cg.Key.EffectiveDate,
EffectiveDateText = cg.Key.EffectiveDate != null ? cg.Key.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.Empty,
ServiceGroupName = string.Join(", ", cg.Select(g => g.ServiceGroupName).Distinct()),
CreatedBy = cg.Key.CreatedBy
}).OrderBy(x => x.ServiceGroupName).ToListAsync();
If i run this when try to order by the field ServiceGroup it returns this message
LinqToDB.Linq.LinqException: ''Join(", ", cg.Select(g => g.ServiceGroupName).Distinct())' cannot be converted to SQL.'
So I don't know how to order by this field ServiceGroupName, thanks for any answer.
I would suggest to make grouping on the client side. Note that I have removed ServiceGroupName from grouping key.
var data = await queryFiltered
.Select(c => new {
c.Id,
c.TableOneId,
c.TableOneName,
c.TableTwoId,
c.TableTwoName,
c.TableTwoCode,
c.TableThreeId,
c.TableThreeName,
c.Description,
c.EffectiveDate,
c.CreatedBy,
c.ServiceGroupName
})
.ToListAsync();
var queryGrouped = data
.GroupBy(c => new { c.Id, c.TableOneId, c.TableOneName, c.TableTwoId, c.TableTwoName, c.TableTwoCode, c.TableThreeId, c.TableThreeName, c.Description, c.EffectiveDate, c.CreatedBy })
.Select(cg => new Model
{
Id = cg.Key.Id,
TableOneId = cg.Key.TableOneId,
TableOneName = cg.Key.TableOneName,
TableTwoId = cg.Key.TableTwoId,
TableTwoCode = cg.Key.TableTwoCode,
TableTwoName = cg.Key.TableTwoName,
TableThreeId = cg.Key.TableThreeId,
TableThreeName = cg.Key.TableThreeName,
Description = cg.Key.Description,
EffectiveDate = cg.Key.EffectiveDate,
EffectiveDateText = cg.Key.EffectiveDate != null ? cg.Key.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.Empty,
ServiceGroupName = string.Join(", ", cg.Select(g => g.ServiceGroupName).Distinct()),
CreatedBy = cg.Key.CreatedBy
})
.OrderBy(x => x.ServiceGroupName)
.ToList();

Where, Select and Single in Linq: What is the most efficient use with ASP.NET Core?

As far as I know, there is not a way to select information from the output of a Single statement. It means it is not possible to write like this:
var playerIdName = context.Players
.Single(p => p.ID == playerID)
.Select(p => new
{
ID = p.ID,
Name = p.Name + " " + p.LastName,
});
Instead, you can write in this two ways:
var playerIdName = context.Players
.Select(p => new
{
ID = p.ID,
Name = p.Name + " " + p.LastName,
})
.Single(p => p.ID == playerID);
or
var playerIdName = context.Players
.Where(p => p.ID == playerID)
.Select(p => new
{
ID = p.ID,
Name = p.Name + " " + p.LastName,
}).Single(p => p.ID == playerID);
but both of them seem tremendously unefficient. Any suggestion of what is the efficiency of this two statements and what is the better way to get information from a selected item apart to make two different statements like:
var player = context.Players
.Single(p => p.ID == playerID);
var playerIdName = new
{
ID = player .ID,
Name = player .Name + " " + player .LastName,
};
How about:
var playerIdName = context.Players
.Where(p => p.ID == playerID)
.Take(1)
.Select(p => new
{
ID = p.ID,
Name = p.Name + " " + p.LastName,
})
.Single();
Take(1) will get the first object that matches the filter in the Where(), Select() projects it, and Single() then executes the whole set.
The iterator in Where() will only iterate until it finds the first match.
I did not test if EF is able to convert this to SQL.

ElasticSearch NEST - DeleteByQuery(...) does not work

I am trying to delete the specific documents from ElasticSearch using DeleteByQuery(..) method but it does not work. The response i get shows 200 status.
I can get the results if i search same query.
Below is my query -
FilterContainer[] container = new FilterContainer[2];
FilterContainer TypeFilter = new TermFilter
{
Field = "TYPE",
Value = TableName
};
FilterContainer BRConnectionIDFilter = new TermFilter
{
Field = "BRCONNECTIONID",
Value = BRConnectionID
};
container[0] = TypeFilter;
container[1] = BRConnectionIDFilter;
IDeleteResponse response = objElasticNestClient.DeleteByQuery<dynamic>(s => s.Index(ExtractionContext.ElasticSearchIndex).Query(b => b.Filtered(q => q.Query(a => a.MatchAll()).Filter(f => f.Bool(m => m.Must(container))))));
if (!response.IsValid && response.ConnectionStatus.HttpStatusCode == 200)
{
throw new Exception("Delete failed for object " + TableName + ". Error: " + response.ServerError);
}
I have used INDEX as NOT_ANALYZED for all fields.
Can anyone please guide me on this?
It worked after replacing query like below -
IDeleteResponse response = objElasticNestClient.DeleteByQuery<dynamic>(s => s.Index(ExtractionContext.ElasticSearchIndex).Type(TableName).Query(b => b.Filtered(q => q.Query(a => a.MatchAll()).Filter(f => f.Term("BRCONNECTIONID", BRConnectionID)))));
if (!response.IsValid && response.ConnectionStatus.HttpStatusCode == 200)
{
throw new Exception("Delete failed for object " + TableName + ". Error: " + response.ServerError);
}

Query expressions to Lambda Syntax?

How can I change this query expression to lambda expression:
var dataUser = from cm in ConsumerName
join c in Consumer on cm.ConsumerId equals c.Id
join cua in ConsumerAccount on cm.ConsumerId equals cua.ConsumerId
join bd in BankDetail on cm.ConsumerId equals bd.ConsumerId
join cpm in CardPayment on cm.ConsumerId equals cpm.ConsumerId
where cm.ConsumerId == consumerId
select new { AccountNumber=bd.AccountNumber,CardNumber= cpm.CardNumber, Name = cm.FirstName + " " + cm.MiddleName + " " + cm.LastName, Email = c.Email, AccountId = cua.AccountId };
Simple, auto-converted by Resharper
var dataUser =
ConsumerName.Join(Consumer, cm => cm.ConsumerId, c => c.Id, (cm, c) => new { cm, c })
.Join(ConsumerAccount, #t => cm.ConsumerId, cua => cua.ConsumerId, (#t, cua) => new { #t, cua })
.Join(BankDetail, #t => cm.ConsumerId, bd => bd.ConsumerId, (#t, bd) => new { #t, bd })
.Join(CardPayment, #t => cm.ConsumerId, cpm => cpm.ConsumerId, (#t, cpm) => new { #t, cpm })
.Where(#t => cm.ConsumerId == consumerId)
.Select(
#t =>
new
{
AccountNumber = bd.AccountNumber,
CardNumber = cpm.CardNumber,
Name = cm.FirstName + " " + cm.MiddleName + " " + cm.LastName,
Email = c.Email,
AccountId = cua.AccountId
});

Linq GroupBy and Aggregate

Given the following list:
var data = new[]
{
new {category = "Product", text = "aaaa"},
new {category = "Product", text = "bbbb"},
new {category = "Product", text = "bbbb"},
};
how do I group it by category and return an object with category and a description of the different text put together ??
ie. would like to end yp with:
{
categroy="Product"
description = "aaaa,bbbb,cccc"
}
tried the following GroupBy and Aggregate, but something is not right
data.GroupBy(x => x.category).Select(g => new
{
category = g.Key,
description = g.Aggregate((s1, s2) => s1 + "," + s2)
});
TIA
Why don't you use String.Join(IEnumerable) method?
data.GroupBy(x => x.category).Select(g => new
{
category = g.Key,
description = String.Join(",", g.Select(x => x.text))
});
With Aggregate you should do following:
description = g.Aggregate(string.Empty, (x, i) => x + "," + i.text)
First parameter sets seed start value to String.Empty. Second parameter defines method to concatenate current seed value (string) with current element (anonymous_type).
data.GroupBy(x => x.category).Select(g => new
{
category = g.Key,
description = g.Select(x => x.text).Aggregate((s1, s2) => s1 + "," + s2)
});

Resources