how to convert multiple linq statement to selectmany - c#

I am a beginner with LINQ and lambda function's. I was wondering how can i convert multiple linq statement to one statement using selectmany function.
string viewname = (from a in StoredProcedureParameters.Tables[0].AsEnumerable()
where a.Field<string>("ViewName") == displayName
select a.Field<string>("View")).Single();
DateTime date = (from d in StoredProcedureParameters.Tables[0].AsEnumerable()
select d.Field<DateTime>("Date")).First();
Thanks for help in advance.

If you want a single query, you could have the select value be an anonymous type. But the scope of the return value is the method where the type is created.
var result = (from a in StoredProcedureParameters.Tables[0].AsEnumerable()
where a.Field<string>("ViewName") == displayName
select new { View = a.Field<string>("View"),
Date = d.Field<DateTime>("Date") }).Single();
then the variable result will be an object with the properties 'View' and 'Date'. You can't return this value from a method though. If you need to return the value, you could create a simple class
public class ViewDate
{
public string View { get; set; }
public DateTime Date { get; set; }
}
Then using an object initializer, you will end up with result containing an instance of the ViewDate object, which you can return from your method.
var result = (from a in StoredProcedureParameters.Tables[0].AsEnumerable()
where a.Field<string>("ViewName") == displayName
select new ViewDate() { View = a.Field<string>("View"),
Date = d.Field<DateTime>("Date") }).Single();

I don't think SelectMany does what you think it does. It's usually just a flatten operation. Why do you think it would help you in this case? Why do you want to avoid two queries?

Related

OrderBy nested property

I'm trying to use OrderBy for a nested property but I can't get it to work.
Models:
public class TPRenewalCycle
{
public virtual ICollection<TPCaseEvent> CaseEvents { get; set; }
}
public class TPCaseEvent
{
public DateTime? DueDate { get; set; }
}
Method:
List<TPRenewalCycle> cycles = renewalCycles
var nextRenewalCycle = cycles.OrderBy(cycle => cycle.CaseEvents.OrderBy(caseEvent => caseEvent.DueDate)).FirstOrDefault();
This gives me the runtime error:
At least one object must implement IComparable.
Is this due to the nullable DateTime or CaseEvents? How can I solve this?
In T-SQL I can do this:
SELECT CE.DueDate
FROM TPRenewalCycles RC
INNER JOIN TPCaseEvents CE on (CE.BusinessSystemId = RC.BusinessSystemId and CE.CaseId = RC.CaseId and CE.Action = RC.Action and CE.Cycle = RC.Cycle)
Order by CE.DueDate
Since OrderBy expression needs to supply a value to be used as the comparison key for the entire record, you need to select the earliest due date in the list:
var nextRenewalCycle = cycles
.OrderBy(cycle => cycle.CaseEvents.Select(caseEvent => caseEvent.DueDate).Min())
.FirstOrDefault();
If you are looking for the earliest date, as in your SQL query, you could use SelectMany instead:
var nextRenewalCycle = cycles
.SelectMany(cycle => cycle.CaseEvents)
.Select(caseEvent => caseEvent.DueDate)
.Min();

Linq query expression

I am writing a join query that produces an anonymous result set. My problem is that I don't know which data type should be returned from my function service and I tried to return object type but I don't know how to access the elements of result in my source code...
Here is my code:
public static IEnumerable<object> GetProductSalesInfoById(int id)
{
var query = from product in database.Products
join sales in database.SalesOrderDetails
on product.ProductID equals sales.ProductID
select new {Name = product.Name,OrderId = sales.SalesOrderID,TotalPrice = (sales.UnitPriceDiscount)*sales.OrderQty*sales.UnitPrice};
IEnumerable<object> result = query.ToList();
return result;
}
You should create a DTO class that contains the properties in your anonymous object and return an IEnumerable<T> of it. Another not so good solution is to use dynamic but I wouldn't use that.
crate one custom class like this
public partial class ResultClass
{
public string Name {get;set;}
public int OrderId {get;set;}
public double TotalPrice {get;set;}
}
public List<ResultClass> GetProductSalesInfoById(int id)
{
var query = from product in database.Products
join sales in database.SalesOrderDetails
on product.ProductID equals sales.ProductID
select new ResultClass {Name = product.Name,OrderId = sales.SalesOrderID,TotalPrice = (sales.UnitPriceDiscount)*sales.OrderQty*sales.UnitPrice};
return result.ToList();
}
hope this will help you out.
You can use Tuple or anonymous type(you need to use casting to get back the object).
Check this link for usage : Anonymous & tuple objects

How to return LINQ object from method?

I have method which has LINQ query and query return columns from multiple tables.
How can I return that LINQ results object and catch it in caller method iterate results and assign to model class?
public ??? GetLocation(string CustomerNum)
{
if (!string.IsNullOrEmpty(CustomerNum))
{
var results = from ca in _context.CUS_ADDRESS
join cad in _context.CUS_ADDRESS_DETAIL on ca.CUS_ADDRESS_ID equals cad.CUS_ADDRESS_ID
where (cad.PRIORITY_SEQ == 0) && (ca.MASTER_CUSTOMER_ID == CustomerNum)
select new
{
CustomerNumber = ca.MASTER_CUSTOMER_ID,
ca.ADDRESS_1,
ca.ADDRESS_2,
ca.ADDRESS_3,
ca.ADDRESS_4,
ca.CITY,
ca.STATE,
ca.COUNTRY_DESCR,
cad.ADDRESS_TYPE_CODE,
cad.ADDRESS_STATUS_CODE
};
return results;
}
else
{
return null;
}
}
Caller method
var results = Data.GetLocation(CustomerNum)
if (results.Any())
{
var location = results.FirstOrDefault();
.....
.....
}
What will be the GetLocation return type?
Depending on how you are actually using the results, you could return an IQueryable instead of IQueryable<T>.
I've used this in some situations (using IEnumerable), like WebForms, that have dynamic binding (either through Eval or by using a BoundField for instance.
You are creating an anonymous object with select new, you can't return a collection of anonymous object from your function, instead you have to create a class which would have all the properties from your select statement and then return IQueryable<YourClass>
class YourClass
{
public int CustomerNumber { get; set; }
public string ADDRESS_1 { get; set; }
//..............
}
and then :
var results = from ca in _context.CUS_ADDRESS
join cad in _context.CUS_ADDRESS_DETAIL on ca.CUS_ADDRESS_ID equals cad.CUS_ADDRESS_ID
where (cad.PRIORITY_SEQ == 0) && (ca.MASTER_CUSTOMER_ID == CustomerNum)
select new YourClass
{
CustomerNumber = ca.MASTER_CUSTOMER_ID,
ADDRESS_1 = ca.ADDRESS_1,
//...............
And modify your function return type as:
public IQueryable<YourClass> GetLocation(string CustomerNum)
You can look at this question for returning IQueryable or Not
If you didn't feel like creating a class you could use Tuples:
public IEnumerable<Tuple<int, string, string>> GetCustomer(int custId) {
return from p in customers
where p.Id == custId
select new Tuple<int, string, string>(
p.Id,
p.FirstName,
p.LastName
);
}
Though this means that you can't name their fields since you access the data like this:
var customer = GetCustomer(1);
var custId = customer.Item1;
var custFirstName = customer.Item2;
var custLastName = customer.Item3;
Create a custom helper class having all columns as properties. Say its MyClass. Fill this as below. I know this not exactly what you want but will help you to get what you want.
var o= (from c in context.table1
from d in context.table2
where c.key=d.key
select new MyClass
{
Property1=c.abc,
Property2=d.xyz
}).SingleOrDefault();
Or write your joins and where in such a way that it will give you only single row fron db.
In the function you are creating an anonymous object and hence cannot be used in caller without some methods of reflection. But it will be much easier to return an object like
public class CustomerLocation
{
public string CustomerNumber {get; set;}
// and so on
}
which will can be placed in a common layer and accessed by both caller and sender and use properties explicitly.
For this your function is better be
public IQueryable<CustomerLocation> GetLocation(string CustomerNum)
{
// your code here
}

How to cast back an object with a linq query inside it with more than 1 entity? [duplicate]

This question already has answers here:
How to return query results from method that uses LINQ to SQL
(7 answers)
Closed 9 years ago.
I have the following scenario. One linq query with a join statment:
public Object getMGM(int MEB_Id)
{
var unitOfWork = new DAL.Implementations.Entity_Framework.UnitOfWork<dbgmEntities>();
var queryRoles = from a in unitOfWork._ctx.MembrosMGM
join b in unitOfWork._ctx.Membros on a.MGM_Pai equals b.MEB_Id
where a.MGM_Filho == MEB_Id
select new { b.MEB_Nome, b.MEB_Id, a.MGM_Familiar };
return queryRoles;
}
queryRoles is running inside a thread. So, I only have to return it as an object. But I cannot find a way to cast it and get the { b.MEB_Nome, b.MEB_Id, a.MGM_Familiar } of the select. I tried the following code with one Entity set on the return and it works fine, but if I have more than 1 entity on the same linq query i don't know how to cast it back:
//this one works fine if i have just one entity set (DAL.MembroResponsavel)
var queryMembroResponsaveis = ((IEnumerable)smartThreadPool.QueueWorkItem(x => editMeb.getMembroResponsavel(currentId)).Result).Cast<DAL.MembroResponsavel>().ToList();
Thanks.
For those having the same problem returning an Anonymous Type I created a class that gets its return values:
public class MemberGetaMember
{
public int MEB_Id {get;set;}
public string MEB_Nome { get; set;}
public bool? MGM_Familiar { get; set; }
}
Then i can cast it back on the return:
foreach (var x in queryRoles)
list.Add(new MemberGetaMember { MEB_Id = x.MEB_Id, MEB_Nome = x.MEB_Nome, MGM_Familiar = x.MGM_Familiar });
return list;
This question helped me how to figure this out
How to return query results from method that uses LINQ to SQL
Thanks everyone.
I suspect you need to enumerate the results and cast to a list:
var queryRoles = (from a in unitOfWork._ctx.MembrosMGM
join b in unitOfWork._ctx.Membros on a.MGM_Pai equals b.MEB_Id
where a.MGM_Filho == MEB_Id
select new { b.MEB_Nome, b.MEB_Id, a.MGM_Familiar }).ToList();

How to equate `var` variable to another query

I have varvariable called retVal which equals to some query. After some conditions I want to equate it to another query. But I get an error like implicit cast of type "System.Collections.Generic.IEnumerable<AnonymousType#1>" in "System.Collections.Generic.IEnumerable<AnonymousType#2>" is impossible. You can ask me why I don't want to define another var variable. Because this one is used in foreach cycle. Let's have a look on the code:
var retVal = from groupItem in result.AsEnumerable()
where groupItem.Sms.First().amountOfParts == (
(from item in rawSmsList.AsEnumerable()
where item.referenceNumber == groupItem.Sms.First().referenceNumber
select item).Count()
)
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
//CONDITION
if (retVal.ToArray().Length==0)
{
//HERE I NEED TO RETVAL EQUATE NEW QUERY
retVal = from groupItem in result.AsEnumerable()
where groupItem.Sms.First().amountOfParts == (
(from item in rawSmsList.AsEnumerable()
where item.senderNumber == groupItem.Sms.First().senderNumber
select item).Count()
)
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
}
foreach (var item in retVal)//FOREACH EXPECTS THE SAME RETVAL!!!
So how to cast different queries to the same var variable? Or how to find type of var variable and then cast it to a new defined variable?
var means implicitly typed variable, that means its type will be determined at compile time, So on your first usage it will be assigned an anonymous type, in your second you are trying to assign it a different anonymous type, you can't do that.
You can modify your code to use a class instead of anonymous object and then project to that, then you will be to do what you are doing now. You can create the class like:
public class MyClass
{
public int Value {get;set;}
public string Sms {get;set;}
}
and then project it by modifying your select statement as:
var retVal = ......
select new MyClass
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
The anonymous class you're using for your projections looks the same to us, but they're two separate classes as far as the compiler is concerned, which is why you'll see AnonymousType#1 and AnonymousType#2 in the error.
One solution is to simply select "groupItem" instead of projecting with an anonymous class since you're only using properties within the groupItem itself. This would give you the same IQueryable type.
Side note: you should replace "retVal.ToArray().Length == 0" with "!retVal.Any()"
To add to Habib's answer:
Just create an actual class instead of using the anonymous type (which is a different class every time you use it). I don't know the types from your example so you'll have to specify:
public class ValueSmsThing // Give it a better name!
{
public IDontKnow Value { get; private set; } // specify the type!
public SmsNumber Sms { get; private set; } // !!
public ValueSmsThing( IDontKnow value, SmsNumber sms) {
Value = value;
Sms = sms;
}
}
And then in your query, instead of using the anonymous type:
select new
{
Value = groupItem.Value,
Sms = groupItem.Sms
};
Use the concrete class you created
select new ValueSmsThing( groupItem.Value, groupItem.Sms );
Then your for loop will know to iterate over ValueSmsThings.

Categories