Orderby a string column in linq causes an error - c#

I have a query like this :
List<PresentClass.userpresentation> q =
(dbconnect.tblUsers.Where(
i => i.permission == permission)
.Select(arg => new PresentClass.userpresentation {
email = arg.email, pass = arg.password,
name = arg.name+" "+arg.family })).ToList();
After adding an orderby :
List<PresentClass.userpresentation> q =
(dbconnect.tblUsers.Where(
i => i.permission == permission)
.Select(arg => new PresentClass.userpresentation {
email = arg.email, pass = arg.password,
name = arg.name+" "+arg.family })).OrderBy(i=>i.family).ToList();
I got this error :
The member
'Novitiate.AdminPortal.PresentationClass.PresentClass+userpresentation.family'
has no supported translation to SQL.
My class:
public class userpresentation
{
public string username { set; get; }
public string email { set; get; }
public string family { set; get; }
public string name { set; get; }
public string pass{ set; get; }
}
Why?

It looks like it's trying to translate the OrderBy() into a SQL statement on your projection.
Try adding the OrderBy() before Select() if you want the database to do the ordering, or after the ToList() if you want to do the ordering once the collection has been loaded.
var q = (dbconnect.tblUsers.Where(i => i.permission == permission)
.OrderBy(i=>i.family)
.Select(arg => new PresentClass.userpresentation {
email = arg.email,
pass = arg.password,
name = arg.name+" "+arg.family
})).ToList();

Related

Why can't I select specific columns from collection using lambda expression?

using the lambda expression I just want to select 2 columns but it throws error.
Code:
public List<Certificates> GetClientsList(string certificationNo = "")
{
List<Certificates> certificatesList = new List<Certificates>();
var query = uow.CertificatesRepository.GetQueryable().AsQueryable();
if (!string.IsNullOrEmpty(certificationNo))
{
query = query.Where(x => x.CertificationNo.Contains(certificationNo)).Select(n => new { ClientName= n.Client, ID= n.CertificatesID});
}
certificatesList = query.ToList();
return certificatesList;
}
Certificates class:
public class Certificates
{
public int CertificatesID { get; set; }
public string FileName { get; set; }
[Required]
[Display(Name = "Certification No")]
public string CertificationNo { get; set; }
[Required]
[Display(Name = "Issue Date")]
public string IssueDate { get; set; }
[Required]
public string Details { get; set; }
[Required]
public string Client { get; set; }
}
Error:
Cannot convert Anonymous querable type to List
Why do you need it converted to Queryable first? what type does uow.CertificatesRepository.GetQueryable() return?
public List<Certificates> GetClientsList(string certificationNo = "")
{
var query = uow.CertificatesRepository.GetQueryable(); // do ToList here if it is IQuerable, but as it seems it was not.
return query.Where(x=>x.CertificationNo.Contains(certificationNo)).Select(x=> new Certificates(){ClientName= n.Client, ID= n.CertificatesID}).ToList();
}
You are probably looking for something like this (depending on the return type of CertificatesRepository)
public List<Certificates> GetClientsList(string certificationNo = "")
{
var query = uow.CertificatesRepository;
if (string.IsNullOrEmpty(certificationNo))
return query.ToList();
return query.Where(x => x.CertificationNo.Contains(certificationNo))
.ToList();
}
Update
The thing is want is to select 2 columns only
public List<Certificates> GetClientsList(string certificationNo = "")
{
var query = uow.CertificatesRepository;
if (string.IsNullOrEmpty(certificationNo))
return query.Select(n => new Certificates { ClientName = n.Client, ID = n.CertificatesID})
.ToList();
return query.Where(x => x.CertificationNo.Contains(certificationNo))
.Select(n => new Certificates { ClientName = n.Client, ID = n.CertificatesID})
.ToList();
}
or
public List<(int ID , string ClientName)> GetClientsList(string certificationNo = "")
{
var query = uow.CertificatesRepository;
if (string.IsNullOrEmpty(certificationNo))
return query.Select(n => (ID = n.CertificatesID, ClientName = n.Client))
.ToList();
return query.Where(x => x.CertificationNo.Contains(certificationNo))
.Select(n => (ID = n.CertificatesID, ClientName = n.Client))
.ToList();
}
Your Select returns anonymous objects. These anonymous objects of course can't be converted to Certificates.
If you really want to use your Certificates class, then you could just create new objects in your select:
public List<Certificates> GetClientsList(string certificationNo = "")
{
List<Certificates> certificatesList = new List<Certificates>();
var query = uow.CertificatesRepository.GetQueryable().AsQueryable();
if (!string.IsNullOrEmpty(certificationNo))
{
query = query.Where(x => x.CertificationNo.Contains(certificationNo)).Select(n => new Certificates{ Client = n.Client, CertificatesID = n.CertificatesID});
}
certificatesList = query.ToList();
return certificatesList;
}
A better solution would probably be to create a new class which only contains the two required properties.
A third possibility is to return a List<dynamic> instead. But then you are not strongly typed anymore.

Group By query in mvc5

Hi i want to write sql Group by query in C# of my MVC5 application.
In the above image I have group by query which i wrote in sql . That I want to write in C# front end.
I tried to write query in front end. But I am getting error which is mentioned in the image. Now I want to write that Group By query in C# and want to display the each employee with count (output same as mentioned in the first image). Can anyone help me to resolve this issue?
My ViewModel(Dashnboard View model)
public class DashboardViewmodel
{
public List<CustomerTypeCountModel> CustomerTypesCountModels { get; set; }
public List<View_VisitorsForm> Visits { get; set; }
public CustomerTypeViewModel CustomerTypeViewModels { get; set; }
public int sizingcount { get; set; }
public int Processingcount { get; set; }
//here i declared two properties
public string EmployeeName { get; set; }
public string EmployeeCount { get; set; }
}
My Controller code
[HttpGet]
public ActionResult SalesVisit()
{
return View();
}
public ActionResult GetDatesFromSalesVisit(DashboardViewmodel dvm)
{
var fromdate = Convert.ToDateTime(dvm.CustomerTypeViewModels.FromDate);
var todate = Convert.ToDateTime(dvm.CustomerTypeViewModels.ToDate);
List<View_VisitorsForm> empcount = new List<View_VisitorsForm>();
if (DepartmentID == new Guid("47D2C992-1CB6-44AA-91CA-6AA3C338447E") &&
(UserTypeID == new Guid("106D02CC-7DC2-42BF-AC6F-D683ADDC1824") ||
(UserTypeID == new Guid("B3728982-0016-4562-BF73-E9B8B99BD501"))))
{
var empcountresult = db.View_VisitorsForm.GroupBy(G => G.Employee)
.Select(e => new
{
employee = e.Key,
count = e.Count()
}).ToList();
empcount = empcountresult ;//this line i am getting error
}
DashboardViewmodel obj = new DashboardViewmodel();
return View("SalesVisit", obj);
}
When you use a GroupBy you get an IEnumerable<IGrouping<Key,YourOriginalType>> so you do not have .Employee and .VisitingID properties.
Change as following:
public class EmployeeCount
{
public string Employee {get; set;}
public int Count {get; set;}
}
List<EmployeeCount> result = db.View_VisitorsForm
.Where(item => item.VisitingDate >= beginDate && item.VisitingDate < endDate)
.GroupBy(G => G.Employee)
.Select(e =>new EmployeeCount
{
employee = e.Key,
count = e.Count()
}).ToList();
//Now add the result to the object you are passing to the View
Also keep in mind that you are not instantiating objects of type View_VisitorsForm but an anonymous object so assigning the result to empcount yet alone with the added FirstOrDefault will not compile
To pass this structure to the View and present it check this question
hope this helps you
var query = db.View_VisitorsForm.Where(o => o.VisitingDate >= new DateTime(2016,10,01) && o.VisitingDate <= new DateTime(2016, 10, 30)).GroupBy(G => G.Employee)
foreach (var item in query)
{
Console.WriteLine($"Employee Id {item.Key} : Count :{item.Count()}");
}

Using Linq to read from multiple tables

I'm sure someone else has asked this but I searched on what I could think of to find the solution.
I've got the following data models to match tables in my SQL db:
public class ProfileDetailModel
{
public string id { get; set; }
public string name { get; set; }
public StyleList[] styleList { get; set; }
public FabricList[] fabricList { get; set; }
}
public class StyleList
{
public string id { get; set; }
public string name { get; set; }
}
public class FabricList
{
public string id { get; set; }
public string fabricName { get; set; }
}
This is the current query code:
var query = (from t in db.tblProfiles
select new ProfileDetailModel()
{
id = t.id,
name = t.name
});
var querylist = await query.ToListAsync();
(prototyped linq queries below for style and fabric)
var styleQuery = (from t in db.tblStyles
select new styleList()
{
id = t.id,
name = t.name
});
var fabricQuery = (from t in db.tblFabrics
select new fabricList()
{
id = t.id,
name = t.name
});
if (queryList.Count > 0)
{
var item = queryList[0];
item.styleList = styleQuery;
item.fabricList = fabricQuery;
}
I'll have one profileDetailModel with multiple items in styleList and in fabricList. EG.
ProfileDetailModel
Data: Pants
styleList: Bell Bottom, Straight Leg, Boot fit
fabricList: jean-blue, jean-black, plaid
All three above models are tables in my db. I could issue 3 separate queries to read the data then assemble after the fact. But is there a way I can do a linq query to include the two arrays in the main query in one shot?
Try this:
var newQuery = (from p in db.tblProfiles
select p)
.AsEnumerable()
.Select(x => new ProfileDetailModel()
{
id = x.id,
name = x.name,
styleList = styleQuery,
fabricList = fabricQuery
});

Issue Related to SelectMany function in LINQ

I have two tables in Database:
PostCalculationLine
PostCaluclationLineProduct
PostCalculationLineProduct(table2) contains Foriegn key of PostCalucationLineId(table1)
In C# code I have two different Models for these two tables as follows:
public class PostCalculationLine : BaseModel
{
public long Id{ get; set; }
public string Position { get; set; }
public virtual Order Order { get; set; }
public virtual Task Task { get; set; }
//some other properties go here
public virtual IList<PostCalculationLineProduct> PostCalculationLineProducts { get; set; }
}
and
public class PostCalculationLineProduct : BaseModel
{
public long Id {get;set;}
public string Description { get; set; }
//some other properties go here
}
Now in Entityframework code, I fetch data from PostCalculationLineProduct as follows:
PostCalculationLineRepository pclr = new PostCalculationLineRepository();
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts)
.Where(c => c.Product.ProductType.Id == 1 && c.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.Id,
Date = c.From,
EmployeeName = c.Employee != null ?c.Employee.Name:string.Empty,
Description= c.Description,
ProductName = c.Product != null?c.Product.Name :string.Empty,
From = c.From,
To = c.Till,
Quantity = c.Amount,
LinkedTo = "OrderName",
Customer ="Customer"
PostCalculationLineId = ____________
})
.ToDataSourceResult(request);
In the above query I want to get PostCalculationLineId(from Table1) marked with underLine. How can I achieve this?
Thanks
You can use this overload of SelectMany to achieve this:-
DataSourceResult dsrResult = pclr.Get()
.SelectMany(p => p.PostCalculationLineProducts,
(PostCalculationLineProductObj,PostCalculationLineObj) =>
new { PostCalculationLineProductObj,PostCalculationLineObj })
.Where(c => c.PostCalculationLineProductObj.Product.ProductType.Id == 1
&& c.PostCalculationLineProductObj.DeletedOn == null)
.Select(c => new HourGridViewModel()
{
Id = c.PostCalculationLineProductObj.Id,
Date = c.PostCalculationLineProductObj.From,
//Other Columns here
PostCalculationLineId = c.PostCalculationLineObj.Id
};
This will flatten the PostCalculationLineProducts list and returns the flattened list combined with each PostCalculationLine element.

Dynamic Selector Linq To Entities

I have a dynamic selector expression that produces anonymous type. It's working fine in linq to objects, but in linq to entities, it throws:
Attempt 1
NotSupportedException
Only parameterless constructors and initializers are supported in LINQ to Entities.
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var newExpression = Expression.New(
typeof(T).GetConstructor(typeof(T).GenericTypeArguments),
userParam,
Expression.Constant("X"),
Expression.Constant("Y")
);
return Expression.Lambda<Func<User, T>>(newExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var obj = new { User = new User(), Address = string.Empty, Fax = string.Empty };
var arr = context.Set<T>()
.Select(DynamicSelect(obj, userParam))
.ToArray();
Attempt 2, If I create a custom type, it's working, but I don't want to, because I want to reuse this helper method without creating additional custom type for each entity, I want to be able to pass the type based on consumer.
public class Container
{
public User User { get; set; }
public string Address { get; set; }
public string Fax { get; set; }
}
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var initExpression = Expression.MemberInit(
Expression.New(typeof(T)),
Expression.Bind(typeof(T).GetProperty("User"), userParam),
Expression.Bind(typeof(T).GetProperty("Address"), Expression.Constant("X")),
Expression.Bind(typeof(T).GetProperty("Fax"), Expression.Constant("Y"))
);
return Expression.Lambda<Func<User, T>>(initExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var arr = context.Set<T>()
.Select(DynamicSelect<Container>(null, userParam))
.ToArray();
Attempt 3, I also tried using Tuple<User, string, string>, but it's not supported too.
NotSupportedException
LINQ to Entities does not recognize the method
'System.Tuple`3[User,System.String,System.String]
Create[User,String,String](User, System.String, System.String)'
method, and this method cannot be translated into a store expression.
Expression<Func<User, T>> DynamicSelect<T>(T obj, ParameterExpression userParam)
{
var createExpression = Expression.Call(
typeof(Tuple),
"Create",
typeof(T).GenericTypeArguments,
userParam,
Expression.Constant("X"),
Expression.Constant("Y"));
return Expression.Lambda<Func<User, T>>(createExpression, userParam);
}
var userParam = Expression.Parameter(typeof(User), "u");
var arr = context.Set<User>()
.Select(DynamicSelect<Tuple<User, string, string>>(null, userParam))
.ToArray();
Please help.
update
I was trying to reuse this helper method in any consumer (User, Customer, Associate, etc) without having specific implementation to each consumer.
The class structure look like.
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string CompanyName { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string Type { get; set; }
public string Content { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string UserName { get; set; }
public ContactDto Contact { get; set; }
}
public class CustomerDto
{
public int Id { get; set; }
public string CompanyName { get; set; }
public ContactDto Contact { get; set; }
}
public class ContactDto
{
public string Email { get; set; }
public string Address { get; set; }
public string Fax { get; set; }
// other contact informations
}
And I have many contacts that could be different for each consumer.
var users = context.Set<User>()
.Select(x => new UserDto
{
Id = x.Id,
UserName = x.UserName,
Contact = new ContactDto
{
Email = x.Contacts.Where(c => c.Type == "Email").Select(c => c.Value).FirstOrDefault()
}
})
.ToArray();
var customers = context.Set<Customer>()
.Select(x => new CustomerDto
{
Id = x.Id,
CompanyName = x.CompanyName,
Contact = new ContactDto
{
Address = x.Contacts.Where(c => c.Type == "Address").Select(c => c.Value).FirstOrDefault(),
Fax = x.Contacts.Where(c => c.Type == "Fax").Select(c => c.Value).FirstOrDefault(),
}
})
.ToArray();
And have refactored the x.Contacts.Where(c => c.Type == "Address").Select(c => c.Value).FirstOrDefault() into expression, but I can't use it directly inside the method like:
var users = context.Set<User>()
.Select(x => new UserDto
{
Id = x.Id,
UserName = x.UserName,
Contact = new ContactDto
{
Email = GetContactExpression("Email").Compile()(x)
}
})
.ToArray();
It will throw error because Invoke method is not supported in linq to expression, so that I need to refactored the whole Select expression, but I need to make it generic (User has UserName, but Customer has CompanyName, and any other information) and probably passing the contact type(s) too after this get solved. The expected result at the moment would be something lile:
var obj = new { User = new User(), Email = "" };
var users = context.Set<User>()
.Select(x => DynamicSelect(obj))
.Select(x => new UserDto
{
Id = x.User.Id,
UserName = x.User.UserName,
Contact = new ContactDto
{
Email = x.Email
}
})
.ToArray();
When I compare your expression with the one created by the compiler for something like u => new { User = u, Address = "X", Fax = "Y" }, the difference is that the latter has filled Members.
I don't quite understand what is the reason for Members to exist at all, but I would try to set it, my guess is that it will fix your problem. The code might look something like:
var constructor = typeof(T).GetConstructor(typeof(T).GenericTypeArguments);
var newExpression = Expression.New(
constructor,
new Expression[] { userParam, Expression.Constant("X"), Expression.Constant("Y") },
constructor.GetParameters().Select(p => typeof(T).GetProperty(p.Name)));

Categories

Resources