LINQ performance when using nullable properties in select - performance

I have an IEnumerable collection.
Using LINQ, I am populating the collection from a web service response.
Below is the sample I am using.
lookupData = from data in content["data"].Children()
select new LookupData
{
LookupKey = (data["data"]["key"]).ToString(),
LookupValue = (string)data["data"]["name"]
};
I will be using the same code for a lot of similar responses which will return a key and value.
Now, I got a scenario when I needed an additional field from the service response for few of the responses(not for all). So, I created an "Optional" property in "LookUpData" class and used as below:
lookupData = from data in content["data"].Children()
select new LookupData
{
LookupKey = (data["data"]["key"]).ToString(),
LookupValue = (string)data["data"]["name"],
Optional = referenceConfig.Optional != null
? (data["data"]["optional"]).ToString()
: String.Empty
};
The null check here is a performance issue. I do not want to use the below since I have other conditions and all together it will become a very big if else loop.
if(referenceConfig.Optional != null){
lookupData = from data in content["data"].Children()
select new LookupData
{
LookupKey = (data["data"]["key"]).ToString(),
LookupValue = (string)data["data"]["name"],
Optional = (data["data"]["optional"]).ToString()
};
}
else{
lookupData = from data in content["data"].Children()
select new LookupData
{
LookupKey = (data["data"]["key"]).ToString(),
LookupValue = (string)data["data"]["name"]
};
}
But I have at least 10 web server response with lots of data in each.

If the value of referenceConfig.Optional is available at compile time you can do
#if OPTIONAL
...
#else
...
If not - you can implement the Null Object Pattern i.e. have all of your ["data"][...] properties always return a value(for instance string.Empty if the type is string) so you won't have check explicitly in the code.

Related

Linq in C# using IEnumerable

Appearently, I got an error if using the following code. It said:
Cannot implicity converrt type System.Linq.IQueryable<AnonymousType> to System.Collection.Generic.IEnumerable.
Please advise how I can fix this?
public IEnumerable<Session> GetAllListDetailConsumer(string refId)
{
ObjectQuery<Session> sessions = db.Sessions;
ObjectQuery<SessionsList> sessionsLists = db.SessionsList;
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
return query.ToList();
}
You are selecting using select new, which would create an anonymous type, you need to project to class Session in your query like.
select new Session
{
....
But remember if your Session class is a representing a table in your database/data context, then you can't project to that class, instead you may have to create a temporary class and project the selection to that class.
EDIT (Since the question now has been edited)
Now you are selecting new SessionConsumerList and you are returning IEnumerable<Session>, you need to modify method signature to return IEnumerable<SessionConsumerList>
Why not separate the creation of the SessionConsumerList in another method? Makes the code a lot cleaner. Like this:
public static SessionConsumerList CreateSessionConsumerList(
Session s,
SessionsList sList)
{
return new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
}
And then:
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select CreateSessionConsumerList(s, sList);

Not all columns retrieved on `new ColumnSet(true)` in a plug-in

I'm building the following query. For some reason, it doesn't bring to me all the fields. I've checked the spelling and when I assign values to those field, I even switched the name, leading to an exception. So I know for sure that they exist and are used. I'm adding a pre-image to the update-step with all data, just to be sure.
QueryExpression request = new QueryExpression
{
EntityName = "myLogicalName",
ColumnSet = new ColumnSet { AllColumns = true },
Criteria =
{
Filters =
{
new FilterExpression
{
FilterOperator = LogicalOperator.Or,
Conditions =
{
new ConditionExpression("someField", ConditionOperator.NotEqual, someValue),
new ConditionExpression("someField", ConditionOperator.Equal, somValue)
}
}
}
}
};
EntityCollection result = Service.RetrieveMultiple(request);
What can I be possibly missing?!
Its probably because; the field does not have a value or field level security is being applied.
As a side generally you should avoid using AllColumns = true
Setting the AllColumns property to true is essentially the same as doing a Select * in sql. The columns won't be adding to the ColumnSet, but they will be returned in the results of your query expression.

if-else clause in Linq to entity framework query

Following Linq to Entities query is causing the "Unable to create a constant value of type 'Data.InhouseUnit'. Only primitive types ('such as Int32, String, and Guid') are supported in this context" exception.
IList<FaultReport> faultReports = (from fr in _session.FaultReports
where fr.CreatedOn > dateTime
select new FaultReport
{
Id = fr.Id,
ExecutionDate = fr.ExecutionDate ?? DateTime.MinValue,
FaultType = fr.FaultType,
Quarters = fr.Quarters,
InhouseSpaceId = fr.InhouseSpaceId,
InhouseSpace = new InhouseSpace { Id = fr.InhouseSpace.Id, Name = fr.InhouseSpace.Name },
InhouseUnitId = fr.InhouseUnitId ?? Guid.Empty,
**InhouseUnit = fr.InhouseUnitId == Guid.Empty ? null : new InhouseUnit { Id = fr.InhouseUnit.Id, Name = fr.InhouseUnit.Name }**
}).ToList();
Specifically, it is the if expression in bold font which causes the exception. I need to make the check as fr.InhouseUnitId is a nullable. If I take out the the bolded expression, the rest of the statement works just fine. I have spent a fair amount of time, in msdn forum and on web, to understand what is causing the exception but still cannot quite understand. Guid is scalar so it should work, right? Even this expression InhouseUnit = true ? null: new InhouseUnit() in place of the bolded expression in the above statement wouldn't work. Can we even write if/else
If i try to write an extension method to take away the logic and just return a result, following exception is thrown:
LINQ to Entities does not recognize the method 'System.Object
GuidConversion(System.Nullable`1[System.Guid], System.Object)' method, and this method
cannot be translated into a store expression
It looks like you are projecting into new objects of the same type that you are querying from. Is that the case? It seems a little weird, but assuming you have a good reason for doing this, you could split the query into two parts. The first part would get what you need from the database. The second part would run locally (i.e. LINQ-to-Objects) to give you the projection you need. Something like this:
var query =
from fr in _session.FaultReports
where fr.CreatedOn > dateTime
select new {
fr.Id,
fr.ExecutionDate,
fr.FaultType,
fr.Quarters,
InhouseSpaceId = fr.InhouseSpace.Id,
InhouseSpaceName = fr.InhouseSpace.Name,
InhouseUnitId = fr.InhouseUnit.Id,
InhouseUnitName = fr.InhouseUnit.Name,
};
IList<FaultReport> faultReports = (
from fr in query.ToList()
select new FaultReport {
Id = fr.Id,
ExecutionDate = fr.ExecutionDate ?? DateTime.MinValue,
FaultType = fr.FaultType,
Quarters = fr.Quarters,
InhouseSpaceId = fr.InhouseSpaceId,
InhouseSpace = new InhouseSpace { Id = fr.InhouseSpaceId, Name = fr.InhouseSpaceName },
InhouseUnitId = fr.InhouseUnitId ?? Guid.Empty,
InhouseUnit = fr.InhouseUnitId == Guid.Empty ? null : new InhouseUnit { Id = fr.InhouseUnitId, Name = fr.InhouseUnitName }
}).ToList();

Converting an entity model to a dataset - can this be done?

Very frustrated here ...
I can usually find an answer of some kind to complex issues in .Net somewhere on the net, but this one eludes me.
I'm in a scenario where I have to convert the result of a LINQ to Entity query into a DataSet so the data can then be processed by existing business logic, and I can't find a single working solution out ther for this.
I've tried basic approaches like the EntityCommand generating a reader, but this one does not work because DataTable.Load() thorws an excpetion (the reader generated by EntityCommand does not support GetSchemaTable() ).
I've also tried more [supposedly] friendly approaches like Entity to IDataReader(http://l2edatareaderadapter.codeplex.com/), but this one throws exceptions, has very little docs, and hasn't been touched since 2008.
Another approach I found is here (http://blogs.msdn.com/b/alexj/archive/2007/11/27/hydrating-an-entitydatareader-into-a-datatable-part-1.aspx), but does not have a working copy of the code; only snippets.
I find it hard to believe that first of all MS would not have offered this backwards-compatibility item out of the box, and second, that it would not have been created by the community either.
I'm willing to look at commercial solutions as well if any are available.
Thx!
You can convert the result into a list and use the following to convert the list to a datatable.
public DataTable ConvertToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
http://social.msdn.microsoft.com/Forums/br/csharpgeneral/thread/6ffcb247-77fb-40b4-bcba-08ba377ab9db
Hope this helps
Preetam
This might not be the greatest solution, but if your scenario have only one or two table that you need to add to the DataSet, why not build them directly manually.
var result = db.YourTable; // get your Linq To Entities result.
DataSet ds = new DataSet();
DataTable tbl = new DataTable();
tbl.Columns.Add("col1", typeof(string));
tbl.Columns.Add("col2", typeof(int));
foreach (var r in result)
{
var row = tbl.NewRow();
row[0] = r.Col1;
row[1] = r.Col2;
tbl.Rows.Add(r);
}
ds.Tables.Add(tbl);
The Col1 and Col2 comes from your Linq To Entity objects, you can create all the table you need like this and return your DataSet.
This is a flexible code and should handle most of your needs:
public DataTable LINQToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}

Only primitive types ('such as Int32, String, and Guid') are supported in this context when I try updating my viewmodel

I am having some trouble with a linq query I am trying to write.
I am trying to use the repository pattern without to much luck. Basically I have a list of transactions and a 2nd list which contains the description field that maps against a field in my case StoreItemID
public static IList<TransactionViewModel> All()
{
var result = (IList<TransactionViewModel>)HttpContext.Current.Session["Transactions"];
if (result == null)
{
var rewardTypes = BusinessItemRepository.GetItemTypes(StoreID);
HttpContext.Current.Session["Transactions"] =
result =
(from item in new MyEntities().TransactionEntries
select new TransactionViewModel()
{
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
}).ToList();
}
return result;
}
public static List<BusinessItemViewModel>GetItemTypes(int storeID)
{
var result = (List<BusinessItemViewModel>)HttpContext.Current.Session["ItemTypes"];
if (result == null)
{
HttpContext.Current.Session["ItemTypes"] = result =
(from items in new MyEntities().StoreItems
where items.IsDeleted == false && items.StoreID == storeID
select new BusinessItemViewModel()
{
ItemDescription = items.Description,
StoreID = items.StoreID,
StoreItemID = items.StoreItemID
}).ToList();
}
return result;
However I get this error
Unable to create a constant value of type 'MyMVC.ViewModels.BusinessItemViewModel'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I know its this line of code as if I comment it out it works ok
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
How can I map ItemDescription against my list of itemTypes?
Any help would be great :)
This line has a problem:
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription,
Since you are using FirstOrDefault you will get null as default value for a reference type if there is no item that satifies the condition, then you'd get an exception when trying to access ItemDescription - either use First() if there always will be at least one match or check and define a default property value for ItemDescription to use if there is none:
ItemDescription = itemTypes.Any(r=>r.StoreItemID==item.StoreItemID)
? itemTypes.First(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription
: "My Default",
If itemTypes is IEnumerable then it can't be used in your query (which is what the error message is telling you), because the query provider doesn't know what to do with it. So assuming the that itemTypes is based on a table in the same db as TransactionEntities, then you can use a join to achieve the same goal:
using (var entities = new MyEntities())
{
HttpContext.Current.Session["Transactions"] = result =
(from item in new entities.TransactionEntries
join itemType in entities.ItemTypes on item.StoreItemID equals itemType.StoreItemID
select new TransactionViewModel()
{
ItemDescription = itemType.ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
CustomerName = rewards.CardID//TODO: Get customer name
}).ToList();
}
I don't know the structure of your database, but hopefully you get the idea.
I had this error due a nullable integer in my LINQ query.
Adding a check within my query it solved my problem.
query with problem:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.Equals(y.OBJ_ID));
query with problem solved:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.HasValue && s.Obj_Id.Value.Equals(y.OBJ_ID));

Resources