I am less experienced with Lambda expression for .NET and trying to get data from SQL using lambda expression. With below query, I am able to get data back, but do not want to use include to get all properties from other tables.
public IEnumerable<ResourceGroup> GetAllServersByApplication(string application_name, string environment_name, string status)
{
var query = _context.ResourceGroup
.Include(a => a.Application)
.Include(t => t.Type)
.Include(e => e.ServersGroup).ThenInclude(e => e.Environment)
.Include(s => s.ServersGroup).ThenInclude(s => s.Server)
.Include(s => s.ServersGroup).ThenInclude(s => s.Server).ThenInclude(s => s.Status)
.Where(a => a.Application.Name == application_name && a.ServersGroup.Any(s => s.Environment.Name == environment_name && s.Server.Status.Name == status))
.ToList();
return query;
}
Lets take an example of below include statement.
.Include(s => s.ServersGroup).ThenInclude(s => s.Server)
From s.Server, I only want to select Id,ServerName,Status, and IPAddress. These are the properties from Servers class that I created as a model.
What is the easy way to exclude all the includes and only show properties that I am interested in?
Here are my tables and its properties:
Status table:
Id, Name
Application table:
Id, Name
Servers table:
Id, ServerName, Status
Environments table:
Id, Name
ResourceGroup table:
Id, Name, Application_Id, Environment_Id
ServersResourceGroup table:
Id, Server_Id, Resource_Id
UPDATE 1:
var query = _context.ResourceGroup
.SelectMany(rg => rg.ServersGroup
.Select(sg => new
{
ResourceName = rg.Name,
ApplicationName = rg.Application.Name,
ServerName = sg.Server.ServerName,
EnvironmentName = sg.Environment.Name,
Status = sg.Server.Status.Name
})).Where(a => a.ApplicationName == application_name && a.EnvironmentName == environment_name && a.Status == status).ToList();
return query;
And error from red line on query variable:
UPDATE 2:
Here is the query syntax:
var query = from rg in _context.ResourceGroup
let app = rg.Application
from sg in rg.ServersGroup
let env = sg.Environment
let srv = sg.Server
let stat = srv.Status
where app.Name == application_name
&& rg.ServersGroup.Any(s => s.Environment.Name == environment_name
&& s.Server.Status.Name == status)
select new
{
ResourceGroupName = rg.Name,
ApplicationName = app.Name,
ServerName = srv.ServerName,
Alias = srv.Alias,
IPAddress = srv.IPAddress,
Type = rg.Type.Name,
Status = stat.Name
};
return query;
Here is the red line error I get in query variable:
Your help is really appreciated. :)
Thanks,
Ray
With lambda expressions, you can use SelectMany to flatten 1-n associations into a 1 dimensional list (i.e. parent and child properties side-by-side). In your case, judging from the Where clause, I think only ResourceGroup - ServerGroup is 1 - n, so it should be something like:
var query = _context.ResourceGroup
.SelectMany
(
rg => rg.ServersGroup
.Select(sg => new
{
ResourceGroup = rg.Name,
Application = rg.Application.Name,
Server = sg.Server.ServerName,
// etc.
})
)
Of course it's good to know how to use lambda expressions, but there's really no point in using them when query syntax makes for much better comprehensible code.
The equivalent in query syntax is:
var query = from rg in _context.ResourceGroup
let app = rg.Application
from sg in rg.ServersGroup
let env = sg.Environment
let srv = sg.Server
let stat = srv.Status
where app.Name == application_name
&& sg.ServersGroup.Any(s => s.Environment.Name == environment_name
&& s.Server.Status.Name == status)
select new
{
ResourceGroup = rg.Name,
Application = app.Name,
Server = srv.ServerName,
// etc. use any property from rg, app, sg, srv, stat
};
As you see -
n - 1 associations are represented by a let statement (which really only helps here to shorten the references in the select)
1-n associations are represented by the from ... from syntax, which is query syntax for SelectMany.
I didn't change the Where clause in the query syntax. Maybe you can use ...
where app.Name == application_name
&& env.Name == environment_name
&& stat.Name == status)
... but note that this is different. The original where returns all ResourceGroup having at least one ServerGroup meeting the condition (and maybe other groups with different environments and statuses). The other where only returns data with environments and statuses equal to the search parameters.
Don't Include all the related tables, but Select all the fields you need. You might find it easier to make a new class to hold the data.
Sorry if I can't make a real query statement here, but your question doens't specify the fields you need.
Related
This code is from my sql query
SELECT Branchname
FROM Branch
WHERE NOT EXISTS (SELECT Branchname FROM vBranching
WHERE Branch.Branchname = vBranching.Branchname AND UserName = 'xCuttiepies');
iwant to execute it into linq c# web api
Its look like this
var Branch = _dbcontext.Branches.Select(c => c.Branchname).ToList();
var vBranching = _dbcontext.VBranchings.Select(a => a.BranchName).ToList();
var ass = Branch.Where(!Branch.Exists(Branch == vBranching));
return Ok(vBranching);
In Branch == vBranching it says cannot convert from bool to System.Predicate<string>
I want to fetch the data that not exists on that different tables.
You souldn't be using ToList except right at the end, and your lambda syntax is off, and the equivalent of EXISTS is Any. You are also returning the wrong value.
var Branches =
_dbcontext.Branches
.Where(b => !_dbcontext.VBranchings.Any(vb =>
vb.BranchName == b.BranchName && vb.UserName == "xCuttiepies"))
.Select(c => c.Branchname)
.ToList();
return Ok(Branches);
I'm having some issues with selecting the same model twice and more times with ORMLite.
My query does various LeftJoin<Model1,Model2>, and then it does something like this:
.LeftJoin<Model2, Model3>((x, y) => x.id1== y.id1, dbConnection.JoinAlias("Alias1")
.LeftJoin<Model2, Model3>((x, y) => x.id2 == y.id2, dbConnection.JoinAlias("Alias2")
.LeftJoin<Model2, Model3>((x, y) => x.id3 == y.id3, dbConnection.JoinAlias("Alias3")
.LeftJoin<Model2, Model3>((x, y) => x.id4 == y.id4, dbConnection.JoinAlias("Alias4"))
How can I use SelectMulti to get 4 times Model3? I mean something like this:
db.SelectMulti<Model1,Model2,Model3,Model3,Model3,Model3>(query);
I'm sorry if I'm unclear, but I can't post the real code due to NDA.
PS. I'm working with C#
UPDATE
I've added the following code following #mythz's suggestions (it's the last part of the answer, since I cannot upgrade for now):
String customSelection = typeof(Model1).GetModelMetadata().Alias + ".*, 0 EOT," +
typeof(Model2).GetModelMetadata().Alias + ".*, 0 EOT," +
"Alias1.*, 0 EOT," +
"Alias2.*, 0 EOT," +
"Alias3.*, 0 EOT," +
"Alias4.*, 0 EOT";
Then, in my query, I added this:
.Select(customSelection);
Now I try to get the result with this:
dbConnection.Select<Model1, Model2, Model3, Model3, Model3, Model3>(query);
This leads to a compile error, which tells me that IDbConnection does not contain a definition for Select and a Select method that accepts a first argument of the type IDbConnection was not found. (Compiler tells me that in Italian, I'm rephrasing it).
The error code from the compiler is CS1061.
You wont be able to use the SelectMulti API for selecting tables with Join Aliases. There's an example of selecting from multiple tables with JOIN aliases in OrmLite's home page:
var q = db.From<Sale>()
.LeftJoin<Contact>((s,c) => s.SellerId == c.Id, db.JoinAlias("seller"))
.LeftJoin<Contact>((s,c) => s.BuyerId == c.Id, db.JoinAlias("buyer"))
.Select<Sale, Contact>((s,c) => new {
s,
BuyerFirstName = Sql.JoinAlias(c.FirstName, "buyer"),
BuyerLastName = Sql.JoinAlias(c.LastName, "buyer"),
SellerFirstName = Sql.JoinAlias(c.FirstName, "seller"),
SellerLastName = Sql.JoinAlias(c.LastName, "seller"),
});
When finer-grain customization is needed you can use Custom SQL with the Dynamic ResultSet APIs.
I've just added support for being able to specify a Custom Table Select in this commit which will let you select tables using your custom aliases, e.g:
q = db.From<Sale>()
.LeftJoin<Contact>((s, c) => s.SellerId == c.Id, db.JoinAlias("seller"))
.LeftJoin<Contact>((s, c) => s.BuyerId == c.Id, db.JoinAlias("buyer"));
var results = db.SelectMulti<Sale, Contact, Contact>(q,
new[] { "Sale.*", "buyer.*", "seller.*" })
foreach (var result in results)
{
Sale sale = result.Item1;
Contact buyer = result.Item2;
Contact seller = result.Item3;
}
This change is available from v5.0.3 that's now available on MyGet.
If you can't upgrade you can achieve the same result by using a Custom Select, e.g:
var q = db.From<Sale>()
.LeftJoin<Contact>((s, c) => s.SellerId == c.Id, db.JoinAlias("seller"))
.LeftJoin<Contact>((s, c) => s.BuyerId == c.Id, db.JoinAlias("buyer"))
.Select("Sale.*, 0 EOT, buyer.*, 0 EOT, seller.*, 0 EOT");
var results = db.Select<Tuple<Sale, ContactIssue, ContactIssue>>(q);
I have a page with five text boxes, each one representing a field in my database table and a search button:
If I were using SQL I could build my SQL statement depending on which fields have data in them.
However, I want to use LINQ, and I'm at a loss as to how to accomplish this. For instance, take a look at the query below:
var db = new BookDBDataContext();
var q =
from a in db.Books
where a.Title.Contains(txtBookTitle) &&
a.Author.Contains(txtAuthor) &&
a.Publisher.Contains(txtPublisher)
select a.ID;
The query above will return data where all the fields match data in the table. But, what if the user didn't enter an Author in the txtAuthor field? If I were building this as a query string, I could check each field for data and add it to the query string. Since this is LINQ, I can't dynamically change the search criteria, it seems.
Any advice would be greatly appreciated!
var db = new BookDBDataContext();
var q = (from a in db.Books
where a.Title.Contains(txtBookTitle));
if(!String.IsNullOrEmpty(txtAuthor))
{
q = q.Where(a => a.Author.Contains(txtAuthor));
}
if(!String.IsNullOrEmpty(txtAuthor))
{
q = q.Where(a => a.Publisher.Contains(txtPublisher));
}
var id = q.Select(a => a.ID);
from a in db.Books
where (string.isNullorWhiteSpace(search) || a.Title.Contains(search)) &&
(string.isNullorWhiteSpace(txtAuthor) || a.Author.Contains(txtAuthor) ) &&
(string.isNullorWhiteSpace(txtPublisher) || a.Publisher.Contains(txtPublisher))
select a.ID;
I have the following tables
Users
- ID
- FirstName
- LastName
MultiplyItems
- ItemID
- Title
UserMultiplyItems
- UserID
- ItemID
I have a variable
List<int> delegateList = {1, 3, 5};
where 1, 3, 5 are ItemID
I want to select all users, where at least one ItemID linked selectable user.
I try the following:
var result = from i in _dbContext.Users
where
((delegateList == null) || i.MultiplyItems.Any(p=> delegateList.Any(a => a == p.ItemID)))
select new UserModel()
{
....
};
but it does not work. error:
Cannot compare elements of type 'System.Collections.Generic.List`1'.
Only primitive types, enumeration types and entity types are
supported.
How to do it correctly?
Thanks
I would write this one:
var filteredUsers = delegateList == null
? _dbContext.Users
: _dbContext.Users.Where(user => user.MultiplyItems
.Any(item => delegateList.Contains(item.Id)));
var result = filteredUsers.Select(user => new UserModel
{
//the UserModel initialization
});
You should not check the following line inside the query:
delegateList == null
It is translated to SQL, and SQL has no idea about what is List and how to compare it with null.
var result = from i in _dbContext.Users
from mi in i.multiplyItems
select new yourClass();
if(delegateList!=null)
{
result = result.Where(delegateList.contains(mi.ItemID));
}
result.ToList();
I dont have visual studio open to test, but it should be a lot like that.
I'm not exactly sure if this is what you wanted, but i thought its better to try and help.
This will output all users from the Users Table where the ItemID is contained in the delegateList. The magic lays in the Contains operator you can get elements from list a contained in list b
var selection = from a in db.UserMultiplyItems
from b in db.Users
where delegateList.Contains(a.ItemID) && a.UserID == b.ID
select b;
Try changing it to:
var result = from u in _dbContext.Users
where
((delegateList == null) || u.MultiplyItems.Any( mi => delegateList.Contains(mi.ItemID)))
select new UserModel()
{
....
};
Note I've also renamed your "i" and "p" things to "u" and "mi" to make it easier to read.
Alternatively you could not use LINQ at all and just stick with lambda expressions:
List<UserModel> usersWithItems =
context
.Users
.Where(u => u.MultiplyItems.Any(mi => (delegateList == null) || (delegateList.Contains(mi.ItemID))))
.Select(um => (new UserModel() { ... } ) )
.ToList();
Which personally I prefer, it means you don't need to know linq at all.
I have the following code. The function has a lot of Linq calls and I had help on putting this into place.
public IList<Content.Grid> Details(string pk)
{
IEnumerable<Content.Grid> details = null;
IList<Content.Grid> detailsList = null;
var data = _contentRepository.GetPk(pk);
var refType = this.GetRefType(pk);
var refStat = this.GetRefStat(pk);
var type = _referenceRepository.GetPk(refType);
var stat = _referenceRepository.GetPk(refStat);
details =
from d in data
join s in stat on d.Status equals s.RowKey into statuses
from s in statuses.DefaultIfEmpty()
join t in type on d.Type equals t.RowKey into types
from t in types.DefaultIfEmpty()
select new Content.Grid
{
PartitionKey = d.PartitionKey,
RowKey = d.RowKey,
Order = d.Order,
Title = d.Title,
Status = s == null ? null : s.Value,
StatusKey = d.Status,
Type = t == null ? null : t.Value,
TypeKey = d.Type,
Link = d.Link,
Notes = d.Notes,
TextLength = d.TextLength
};
detailsList = details
.OrderBy(item => item.Order)
.ThenBy(item => item.Title)
.Select((t, index) => new Content.Grid()
{
PartitionKey = t.PartitionKey,
RowKey = t.RowKey,
Row = index + 1,
Order = t.Order,
Title = t.Title,
Status = t.Status,
StatusKey = t.StatusKey,
Type = t.Type,
TypeKey = t.TypeKey,
Link = t.Link,
Notes = t.Notes,
TextLength = t.TextLength,
})
.ToList();
return detailsList;
}
The first uses one format for Linq and the second another. Is there some way that I could simplify and/or combine these? I would really like to make this code simpler but I am not sure how to do this. Any suggestions would be much appreciated.
Of course you can combine them. The Linq keywords such as from, where and select get translated into calls like the Extension methods that you call below, so effectively there's no difference.
If you really want to combine them, the quickest way is to put () around the first query, then append the method calls you use on details in the second query. Like this:
detailsList =
(from d in data // <-- The first query
// ...
select new Content.Grid
{
// ...
})
.OrderBy(item => item.Order) // <-- The calls from the second query
.ThenBy(item => item.Title)
.Select((t, index) => new Content.Grid()
{
//...
}).ToList();
But i think that would be ugly. Two queries are just fine IMO.