new blank, non null anonymous type - c#

I need to insert a new blank, but non null anonymous type into a list of other anonymous types returned by a linq query. Is that possible? All I can get are nulls
var something =
( from a in x.As
where x != null
join b in x.Bs
on a.key equals b.key
select new
{
a.prop1,
a.prop2,
b.prop1,
b.prop2,
b.prop3
}).ToList();
// insert blank
//something.InsertRange(0, something.DefaultIfEmpty());
//something.InsertRange(0, something.Take(0));
//?

I don't know of a way to do it in a single query since the default for an anonymous type is null. What I would do is pre-create a "default" item and append it if necessary:
var blank = new {
prop1 = default(string), // can't use null
prop2 = default(string), // because the type cannot be inferred
prop3 = default(string),
prop4 = default(string)
};
var something = /*...*/.ToList();
if(!something.Any())
something.Add(blank);
Note that as long as the field names match (in name and type) blank will be of the same anonymous type as the one created by the Linq query.

Related

Linq to SQL to map a nullable bool value to IsNull and not = NULL

I'm trying to use Linq to SQL to filter a table with a nullable bool value based on a variable.
But if the variable's value is NULL, LINQ seems to translate the query to Field = NULL instead of Field IS NULL.
Based on other posts I've seen to use object.Equals to map correctly. And that works, but only when the variable's value is null.
Sample code:
bool? relevantValue = null;
var q = from m in MyTable
where ???
select m;
Case 1:
If where clause is object.Equals(m.IsRelevant, relevantValue), it works when relevantValue is null and true, but returns incorrect results when relevantValue = false.
It translates in SQL to
WHERE (
(CASE
WHEN [t0].[IsRelevant] = 1 THEN 1
ELSE 0
END)) = 0
Case 2:
For where (relevantValue == null ? object.Equals(m.IsRelevant, relevantValue) : m.IsRelevant == relevantValue) it works for relevantValue = true or false, but when null gives an error that:
The argument 'value' was the wrong type. Expected 'System.Boolean'. Actual 'System.Nullable`1[System.Boolean]'.
Do I have other options that will work for null, true and false?
Maybe I missed something about the question but, Why not use the Nullable<> .HasValue and .Value to get a real boolean?
IQueryable<Person> People = new List<Person>
{
new Person
{
PersonId = 1,
IsMarried = true
},
new Person
{
PersonId = 2,
IsMarried = false
},
}
.AsQueryable();
bool? isMarried = null;
var query = from p in People
where !isMarried.HasValue || (isMarried.HasValue && p.IsMarried == isMarried.Value)
select p;

Mapping from join - The type 'CarDTO' appears in two structurally incompatible initializations within a single LINQ to Entities query

I am trying to map into CarDTO from right side of left join, but gets the following error:
The type 'CarDTO' appears in two structurally incompatible
initializations within a single LINQ to Entities query. A type can be
initialized in two places in the same query, but only if the same
properties are set in both places and those properties are set in the
same order.
I am trying to make self left join and my query looks like this:
var query = from left in db.Cars
join right in db.Cars on left.id_Base equals right.ID into rightGrouped
from rightGr in rightGrouped.DefaultIfEmpty()
select new CarDTO()
{
ID = left.ID,
Description = left.DisplayName,
Name = left.Name,
IsSystem = left != null ? left.Template.isSystem : (byte?)null,
IdBase = left.id_Base,
InnerCar = new CarDTO()
{
ID = rightGr.ID,
Description = rightGr.DisplayName,
Name = rightGr.Name,
IsSystem = rightGr != null ? rightGr.Template.isSystem : (byte?)null,
IdBase = rightGr.id_Base,
InnerCar = new CarDTO()
}
};
If I change the following row InnerCar = new CarDTO() to InnerCar = null,
then I get the following error:
Unable to create a null constant value of type 'CarDTO'. Only entity
types, enumeration types or primitive types are supported in this
context.`
Does anybody know what I am doing wrong?
Error message explains that EF expects all initializations of the same type (CarDto in this case) in the same expression to follow a rule: in each initialization the same properties should be set, and in the same order. Your code violates this rule, because you have 3 initializations. 2 of them set the same properties in same order, but third one (last InnerCar = new CarDTO()) does not.
The only possible value to use in InnerCar.InnerCar initialization to satisfy this rule is null (because of recursive definition). However, EF will not allow you to do that, because it cannot create constant values of arbitrary types (doesn't know how to convert such thing into SQL).
So doing this with recursive type is just not possible. Instead, we should use different type for InnerCar. This can be done for example with anonymous types:
select new
{
ID = left.ID,
Description = left.DisplayName,
Name = left.Name,
IsSystem = left != null ? left.Template.isSystem : (byte?)null,
IdBase = left.id_Base,
InnerCar = new
{
ID = rightGr.ID,
Description = rightGr.DisplayName,
Name = rightGr.Name,
IsSystem = rightGr != null ? rightGr.Template.isSystem : (byte?)null,
IdBase = rightGr.id_Base
}
};
This will work, because two anonymous types here are different. First one has 6 properties (including InnerCar) and second one has 5 properties (without InnerCar). Of course for type to be anonymous is not a requirement here.
You can also just flatten this structure:
select new
{
LeftID = left.ID,
RightID = rightGr.ID,
// etc
};
After you performed such mapping, and have done any other filtering\paging\whatever - convert it to your type after materializing:
// query here is complete
var result = query.AsEnumerable().Select(c => {
new CarDto {
ID = c.ID,
Description = c.Description,
// etc
}
});

LINQ: Set a default value if navigation property is null

This is my current LINQ statement:
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID
};
The trouble I'm having is that the last item is often null. So when I try to convert "results" to a List, I get
{"The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."}
I don't want to make SelectedProposalEngineerID a nullable int, for problems that would cause down stream. How would I give it a value of 0 when it's null?
I have seen a LOT of other threads about this, but I can't seem to adapt any of their answers to this case.
Use DefaultIfEmpty extension method.
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID =
l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer)
.Select(contact => contact.ContactID)
.DefaultIfEmpty(0)
.FirstOrDefault()
};
Nullable<int> ID;
var results = from l in leads
select new MyObject
{
LeadID = l.LeadID,
SelectedProposalEngineerID = (ID = l.LeadContacts.Where(contact => contact.LeadContactTypeID == LeadContactType.ProposalEngineer).FirstOrDefault().ContactID).HasValue ? ID.Value : 0;
};
A ternary operator should do the job. Assign the result to a variable, then if it's not null, cast the variable to int and return it, else return 0.

How can I refine a linq query that binds to a DTO via another list?

I'm doing some SalesForce work via C# and I have a list of Cases. You should not need to be familiar with SalesForce to understand what the issue is. I'm trying to retrieve a list of CaseComment objects where the parentId of CaseComment matches the Id of Case. i.e:
Case 1
Id = 123456
Case Comment 1
Id = 5532094
ParentId = 234242
Case Comment 2
Id = 984984
ParentId = 123456
In this case, the parentId of case comment 2 matches Case 1 and thus should be returned. Here is the code I'm currently using:
static List<SalesForceCaseComment> GetCaseCommentsFromListOfCases(List<SalesForceCase> Cases)
{
using (salesforce_backupsEntities le = new salesforce_backupsEntities())
{
List<SalesForceCaseComment> casecomments = (from c in le.CaseComments select new SalesForceCaseComment
{
CommentBody = c.CommentBody,
CreatedById = c.CreatedById,
Id = c.Id,
IsDeleted = c.IsDeleted,
IsPublished = c.IsPublished,
LastModifiedById = c.LastModifiedById,
ParentId = c.ParentId
}).Where(n => Cases.Any(s=> s.Id == n.ParentId)).ToList();
return casecomments;
}
}
This unfortunately results in the following error:
Unable to create a constant value of type 'DTO.SalesForceCase'. Only primitive types or enumeration types are supported in this context.
It looks like you're effectively joining a local List<SalesForceCase> to a table on the provider side, which probably isn't going to work. If the provider supports the Contains operator, you can do this:
var ids = Cases.Select(c => c.Id).ToArray();
var comments = from c in le.CaseComments
where ids.Contains(c.ParentId)
select new /* projection */;
Basically it means that underline provider doesnt support enumeration types, so you need to either write your own visitor to enumerate it (harder) or just convert it to expression with primitive types with "or", which simply means looping through your array and adding conditions.
Second way could be easier if salesforce_backupsEntities inherited from ObjectContext. In this case you can use IN esql operator.
Also, if there is navigation property in Case object, you may want to attach object to context and do load on navigation property ( case.Comments.Load() )
I would guess something like this should work (join instead of Where):
static List<SalesForceCaseComment> GetCaseCommentsFromListOfCases(List<SalesForceCase> Cases)
{
using (salesforce_backupsEntities le = new salesforce_backupsEntities())
{
List<SalesForceCaseComment> casecomments = (from c in le.CaseComments
where Cases.Select(x => x.Id).Contains(c.ParentId)
select new SalesForceCaseComment
{
CommentBody = c.CommentBody,
CreatedById = c.CreatedById,
Id = c.Id,
IsDeleted = c.IsDeleted,
IsPublished = c.IsPublished,
LastModifiedById = c.LastModifiedById,
ParentId = c.ParentId
})).ToList();
return casecomments;
}
}

The property 'x' on 'tblX' could not be set to a 'null' value. You must set this property to a non-null value of type 'Int16'

List<tblX> messages = (from x in db.tblX where x.msg_id == id_id
|| x.name == firstName select x).ToList();
I get the error:
The property 'x' on 'tblX' could not be set to a 'null' value. You must set this property to a non-null value of type 'Int16'.
I have a property msg_blocked in db, which is nullable and integer. I know that I need to make a conversion, but I don't use it or need it anywhere in my linq.
Seems like your class definition for tblX doesn't match the database representation, so either modify your class to accept a nullable value, or just project out the required fields:
List<tblX> messages = (from x in db.tblX
where (x.msg_id == id_id || x.name == firstName)
select new tblX
{
//required fields
msg_id = x.msg_id,
name = x.name,
...
}).ToList();
Addendum: The reason you run into this problem is behind the scenes when you
select x
this is translated into a
select new tblX
which projects into all its available fields. The code provided is more explicit and specifies which fields to query for and then project into.
List<tblX> messages = (from x in db.tblX
where (x.msg_id == id_id || x.name == firstName) &&
x.msg_blocked != null
select x).ToList();

Categories