LINQ to SQL MAX in WHERE clause - linq

I am new to Linq so as expected I have encountered difficulties.
What I am trying to achieve is this:
SELECT id, name, password
FROM users u
WHERE u.id = (SELECT MAX(u1.id) FROM users u1);
My Linq is:
var dbUsers = from u in context.Users
where u.Id == (context.Users.Max(u1 => u1.Id))
select u;
But I always end with the following exception:
Unable to create a constant value of type 'Bla.Users'. Only primitive
types ('such as Int32, String, and Guid') are supported in this
context.
Here is the users class:
public class Users
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
}
}
Here is my context class:
public class EFDbContext : DbContext
{
public DbSet<User> Users{ get; set; }
}

You need to select the ID property
var dbUsers = from u in context.Users
where u.Id == (context.Users.Select(u1 => u1.Id).Max())
select u;

I usually do my LINQing in lambda format...
var dbUsers = DataContext.Users
.Where(u => u.Id == (DataContext.Users.Max(u1 => u1.Id)))
.Select(u => new
{
Id = u.Id,
Name = u.Name,
Password = u.Password
});
If you want the comprehension format...
var dbUsers = from u in DataContext.Users
where u.Id == (DataContext.Users.Max(u1 => u1.Id))
select new
{
Id = u.Id,
Name = u.Name,
Password = u.Password
};

Consider using a let statement:
var dbUsers = from u in context.Users
let int maxId = context.Users.Max(u1 => u1.Id)
where u.Id == maxId
select u;

Please let me know if this one solves your problem:
var dbUser = (from u in context.Users
orderby u.Id descending).FirstOrDefault()

You could use lambda expressions:
var dbUser = context.Users.First(u => u.Id== (context.Users.Select(u2
=> u2.Id).Max()));
or:
var dbUser = context.Users.OrderByDescending(u => u.Id).FirstOrDefault();

Related

Left Join using LAMBDA to get Result in API

How to implement this Join which is in the code below into C# using LAMBDA
Select
VD.Id
, VD.BusinessAddress
, VD.BusinessDesc
, VD.BusinessEmail
, VD.BusinessName
, VD.BusinessZip
, VD.ContactPerson
, VD.ContactNo
, VD.ProfileUrl
, L.Name
, BC.BusinessCategory
from vendorDomain VD WITH(NOLOCK)
left Join Location L WITH(NOLOCK) ON VD.City = L.Id
left join Business_Category BC WITH(NOLOCK) ON VD.BusinessCategory = BC.BusinessId
where VD.IsDeleted = 0
I have to implement the join operation in the following API:
[HttpGet]
public async Task<IActionResult> Get()
{
var VendorList =await _vendorRepository.Query().Where(x => x.IsDeleted == false).ToListAsync();
return Ok(VendorList);
}
There are alot of examples out there but are way to confusing for a novice developer..
EDIT:
This is what I have tried as of now:
var employees = from vndr in context.vendorDomain
join C in context.Location on vndr.City equals C.Id into dep
from dept in dep.DefaultIfEmpty()
select new
{
vndr.BusinessAddress,
vndr.BusinessDesc,
vndr.BusinessEmail,
vndr.BusinessName,
vndr.BusinessWebsite,
vndr.BusinessZip,
vndr.ContactNo,
vndr.ContactPerson,
vndr.Created_At,
vndr.ProfileUrl,
vndr.Url,
dept.Name
};
We will do things first: do the joins and create a view model class that you will return. Because returning anonymous object and using dynamic does get messy.
ViewModel for the joined entities:
public class EmployeesViewModel
{
public string BusinessAddress { get; set; }
public string BusinessDesc { get; set; }
public string BusinessEmail { get; set; }
/* ....all remaining properties */
}
Then we join them properly and select them as an EmployeeViewModel:
var employees = from vndr in context.vendorDomain
join loc in context.Location on vndr.City equals loc.Id
join bus in context.Business_Category on vndr.BusinessCategory = bus.BusinessId
select new EmployeeViewModel
{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
};
Or, if you want the method syntax:
var employees = context.vendorDomain
.Join(context.Location,
vndr => vndr.City,
loc => loc.Id,
(vndr, loc) => new { vndr, loc,})
.Join(context.Business_Category,
vndr_loc.vndr.BusinessCategory,
bus.BusinessId,
(vndr_loc, bus) => new {vndr_loc.vndr, vndr_loc.loc, bus})
.Select(x => new EmployeeViewModel{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
});
As per your comment, you need to print the vendorList after the join. Now that is pretty vague, but I assume you want to submit both to your client / view, so again, we create a ViewModel class for it:
public class EmployeeVendorListViewModel
{
public VendorList VendorList { get; set; }
public EmployeeViewModel Employees { get; set; }
}
The last thing we do is glue it all together in your ActionMethod and return it:
[HttpGet]
public async Task<IActionResult> Get()
{
//renamed using a lower case "v"
var vendorList = await _vendorRepository.Query()
.Where(x => x.IsDeleted == false)
.ToListAsync();
//the join from earlier. You should put it in a repo somewhere, so it does not clutter your controller
var employees = from vndr in context.vendorDomain
join loc in context.Location on vndr.City equals loc.Id
join bus in context.Business_Category on vndr.BusinessCategory = bus.BusinessId
select new EmployeeViewModel
{
BusinessAddress = vndr.BusinessAddress,
BusinessDesc = vndr.BusinessDesc,
BusinessEmail = vndr.BusinessEmail,
/* ... remaining properties here*/
};
//create the final view model and return it
var vm = new EmployeeVendorListViewModel
{
VendorList = vendorList,
Employees = employees
}
return Ok(vm);
}
If you want to use NOLOCK in your query, you have to wrap it in a TransactionScope. This has already been answered here on StackOverflow: NOLOCK with Linq to SQL

Extending a Select.... to accommodate more fields as extension using EF

I have a class:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public string Address {get;set;}
}
And query using Entity Framework is:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary
});
My question is:
I want to allow extending this query without rewriting the base query. It should allow adding a new field in the .Select(.....) by extending the above query.
Without rewriting the complete query:
var selectedEmployee = entities.Employees
.Where(e=>e.Salary>10000)
.Select(emp => new EmpDTO
{
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
How can I do that?
Thanks
If I understand, you can try this:
public IQuerable<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection = null)
{
if(projection == null)
projection = emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
};
return entities.Employees.Where(e => e.Salary > 10000).Select(projection);
}
Implementation:
var query = classInstance.GetEmployee();
//or
query = classInstance.GetEmployee(emp => new EmpDTO {
Id = emp.Id,
Name = emp.Name,
Salary = emp.Salary,
Address = emp.Address
});
If you always want to get some set of fields, like Id, Name and
Salary and sometimes take additional fields(and specify only their
as method arguments), you should to take all fields from DB and only
then filter them depends on your condition - it is bad practice to do
SELECT *, so you should get default set of fields or specify all desired fields mannualy.
Solution with SELECT *:
public List<EmpDTO> GetEmployee(Func<Employee, EmpDTO> projection)
{
var query = entities.Employees.Where(e => e.Salary > 10000).ToList().Select(x => {
var item = projection == null ? new EmpDTO() : projection(x);
item.Id = x.Id;
item.Name = x.Name;
item.Salary = x.Salary;
return item;
}).ToList();
}
At this case return value is List<T> not IQuerable<T>;
Implementation:
var items = classInstance.GetEmployee(emp => new EmpDTO { Address = emp.Address });
//items also will contain fields: Id, Name and Salary by default

How to solve obejct reference is not instance of object in linq left join query?

Here is my SQL query :
select AU.Last_Name
From t_Batch B
left join (select distinct customer_id, subbatchtypeid
from t_allocationconfig where isactive='Y')
AA on AA.customer_id = B.Customer_id and
AA.SubBatchtypeid = B.SubBatchtypeid
left join t_batchallocation BA on BA.Batchid = B.Batchid
left join VW_users AU on AU.User_ID = BA.User_id;
I want to convert this SQL to Linq but I get an error :
User_ID obecjt reference is not instance of object
How do I solve this ?
IList<Batch> result = new List<Batch>();
var batch = _batchservice.GetBatch().Where(r => r.ISACTIVE == "Y");
var batchallocation = _batchallocationservice.GetBatchAllocation();
var allocationconfig=_allocationconfigservice.GetAllocationConfig().Where(r=>r.ISACTIVE=="Y");
result = (from B in batch
join ac in (
from cl in allocationconfig where cl.ISACTIVE=="Y"
select new {cl.CUSTOMER_ID,cl.SUBBATCHTYPEID}
) on new {B.CUSTOMER_ID,B.SUBBATCHTYPEID}
equals new {ac.CUSTOMER_ID,ac.SUBBATCHTYPEID} into aac
from v in aac.DefaultIfEmpty(null)
join BA in batchallocation on B.BATCHID equals BA.BATCHID into x
from y in x.DefaultIfEmpty()
join AU in users on y.USER_ID equals AU.USER_ID into g
from h in g.DefaultIfEmpty()
select new Batch
{
AllocatedUserName=h.LAST_NAME
}).ToList();
I guess the issue is that the x.DefaultIfEmpty is returning a null value which is then causing the error when accessed by the join onto the users table (y.USER_ID).
There is an overload on the DefaultIfEmpty clause that accepts a default type e.g.
x.DefaultIfEmpty<BatchAllocation>(new BatchAllocation () { USER_ID = "-1", .... })
What you could try is returning a dummy batch allocation default with a USER_ID property of say -1 (assuming that no user ids can be -1) This would then return the h as a null and you can then filter the nulls out via a where in your master query.(where h != null && h.LAST_NAME != null)
Example below -
class Program
{
static void Main(string[] args)
{
LeftOuterJoinExample();
Console.ReadLine();
}
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
public static void LeftOuterJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };
// Create two lists.
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
var query = from person in people
join pet in pets on person equals pet.Owner into gj
from subpet in gj.DefaultIfEmpty<Pet>(new Pet() { Name = "", Owner = null })
where subpet != null && subpet.Name != String.Empty
select new { person.FirstName, PetName = (subpet == null ? String.Empty : subpet.Name) };
foreach (var v in query)
{
Console.WriteLine("{0,-15}{1}", v.FirstName + ":", v.PetName);
}
}
}
You need to check that y is not null before you use it

Linq query join table

I have a User table and a group table one user can be part of many groups
[USERID]----*-[USERID GROUPID]-*-----[GROUPID]
(join table)
public partial class user
{
public int user_id { get; set; }
public string name { get; set; }
public virtual ICollection<group> groups{ get; set; }
}
public partial class group
{
public int user_id { get; set; }
public string name { get; set; }
public virtual ICollection<user> users{ get; set; }
}
1) I need to return all the users that belongs to a specific group
var queryable = (from g in db.groups
where g.group_id == id
select g.users)
.ToList()
.Select(u => new {
id = u.id, //Error does not contain definition for id
name = u.name //Error does not contain definition for name
});
2) I need to return a specific user that belongs to a specific group
Thanks for your suggestions
I need to return all the users that belongs to a specific group
Lambda syntax:
db.groups.Where(g => g.group_id == id).SelectMany(g => g.users)
Or query syntax:
from g in db.groups
where g.group_id == id
from u in g.users
select u
I need to return a specific user that belongs to a specific group
Just like query above, with one additional filter
db.groups.Where(g => g.group_id == id)
.SelectMany(g => g.users)
.Where(u => u.id == userId)
Query syntax:
from g in db.groups
where g.group_id == id
from u in g.users
where u.id == userId
select u
Try something like this:
var queryable = (from p in db.plants
join u in db.users on p.userId equals u.id
where p.plant_id == plant_id
select u)
.ToList()
.Select(u => new {
id = u.id,
name = u.name
});
where p.userId is the column which connect plants and users tables
var queryable = (from p in db.plants
join u in db.users on p.userId equals u.id
where p.plant_id == plant_id
select new { p.id, p.name, u.name}).ToList();

select all related objects

I have a linq query where I'm trying to return all MlaArticles that are related to all other WebObjects but I'm getting the error: The specified type member 'RelatedWebObjectIds' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Here's the Model...
public abstract class WebObject : IValidatableObject
{
public WebObject()
{
this.Id = Guid.NewGuid();
RelatedTags = new List<Tag>();
RelatedWebObjects = new List<WebObject>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
public virtual ICollection<WebObject> RelatedWebObjects { get; set; }
public IList<Guid> RelatedWebObjectIds { get; set; }
}
Thanks for your help...
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjectIds
where w.Id == id
select r).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
New query. Produces different error: WebObject does not contain a definition for 'Contains' and the best extension method overload ... has some invalid arguments.
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.RelatedWebObjectIds).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
Is there a RelatedWebObjects navigation property in your MlaArticle entity? If there is, you can do that instead:
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.Id).Contains(e.Id)
select e).OrderBy(x => x.Title).ToList();
List<MlaArticle> assignedWebObjects = (from e in db.MlaArticles
where
(from w in db.WebObjects
from r in w.RelatedWebObjects
where w.Id == id
select r.RelatedWebObjectIds).Any(i => i == e.Id)
select e).OrderBy(x => x.Title).ToList();

Resources