Select query from db using linq - c#

I have a entity class called
[Serializable]
Public sealed class LayEntity : EntityBase
{
Public string Name {get;set;} // Getting mapped to the same column name in db table
}
In my .cs file I have a query as :
LayEntity lay = null;
Using(var context = new DBContext())
{
lay = context.LayTable.where(x=>x.id >1).Select (y=>y.Name).FirstOrDefault();
}
My intention is to store retrieve the name filed on to the entityclass.
But I am getting a compile time error as ‘Cannot implicitly convert string to Entity’.What is that I need to do?

You can most probably do this:
lay = context.LayTable
.Where(x=>x.id >1)
.Select (y=>new LayEntity (){Name=y.Name})
.FirstOrDefault();
Or this:
lay = context.LayTable.where(x=>x.id >1).FirstOrDefault();
Or even this:
lay = context.LayTable.FirstOrDefault(x=>x.id >1);
If you expect a single value I would do this:
lay = context.LayTable.SingleOrDefault(x=>x.id >1);

Error message is clear, you cannot assign string value to object of type LayEntity
Probably, what you need is
LayEntity lay = null;
Using(var context = new DBContext())
{
lay = context.LayTable.FirstOrDefault(x=>x.id >1);
if(lay != null)
{
lay.Name; // use name here.
}
}
Note, I've removed Where method as it is redundant in this case.

Related

Switch Statement To Dynamic for filling in parameters

I am trying to create a more dynamic approach to pulling data for a view than a switch statement. Right now I have several different options and more could be added anytime. The tables that will be pulled from are all the same in format except for the name of the table and the name of their ID field.
public List<listoftables> BuildListOfTables(string router)
{
var listOfViewModels = new List<FormatOfTables>();
using var context = new TableContext();
switch (router)
{
case "firstTable":
listOfViewModels = context.Set<firstTable>().Select(x => new FormatOfTables
{
UniqueID = x.FirstTableID,
Value = x.Value,
}).ToList();
break;
case "secondTable":
listOfViewModels = context.Set<secondTable>().Select(x => new FormatOfTables
{
UniqueID = x.SecondTableID,
Value = x.Value,
}).ToList();
break;
case "thirdTable":
listOfViewModels = context.Set<ThirdTable>().Select(x => new FormatOfTables
{
UniqueID = x.ThirdTableID,
Value = x.Value,
}).ToList();
break;
return listOfViewModels;
}
I'm trying to find a way to do this more dynamically. So as long as the option in router matches a table name, each table that gets put into the model just fills the UniqueID and Value to be whatever their ID and value happens to be rather than having to match the column names. So if a fourth table came in I would only have to worry about if router matched the table name rather than having to add an entirely new switch per entry.
The tables that will be pulled from are all the same in format except for the name of the table and the name of their ID field.
The problem here is passing the typename to the Set<T>() function. But we'll get to that. First, let's make some of this easier by adding an interface:
public interface IMyTable
{
string TableName {get;}
int UniqueID {get;}
}
Then each of your firstTable, secondTable, ThirdTable types must implement this interface:
public class firstTable : IMyTable
{
// existing class stuff here
public int UniqueID { get { return FirstTableId;} }
public string TableName { get { return "FirstTable"; } }
}
And now the method can look like this:
public IEnumerable<listoftables> BuildListOfTables(string router)
{
using var context = new TableContext();
DBSet tableSet = null; //I'm making an assumption about the Set() function here. You may need to change the type.
switch (router)
{
case "firstTable":
tableSet = context.Set<firstTable>();
break;
case "secondTable":
tableSet = context.Set<secondTable>();
break;
case "thirdTable":
tableSet = context.Set<ThirdTable>();
break;
}
if (tableSet != null)
{
return tableSet.Select(x => new FormatOfTables
{
UniqueID = x.UniqueID,
Value = x.Value
});
}
return null;
}
This reduces the repeated boilerplate down to just as much as is necessary to call the generic Set<>() function.
From here we can further reduce the code by changing how the function is designed, including how you expect to call it:
public IEnumerable<listoftables> BuildListOfTables<T>() where T : IMyTable
{
using var context = new TableContext();
return context.Set<T>()
.Select(x => new FormatOfTables
{
UniqueID = x.UniqueID,
Value = x.Value
});
}
But all this really does is push where you have to put the switch() statement up to the call site. However, that might be worth it if the call site happens to have the type information already available.
Note for ALL of these examples I converted the method to return IEnumerable instead of a List. Calling .ToList() can be notoriously bad for performance. If you really need a list (hint: you usually don't) you can still put the ?.ToList() after the function call. You might be able to improve things even further in this case by returning IQueryable, which could let later code continue the expression tree before executing anything on the server.

LINQ and creating NON anonymous return values

I think I understand returning records of an anonymous type from But in this I want to create NEW CatalogEntries, and set them from the values selected. (context is a Devart LinqConnect database context, which lets me grab a view).
My solution works, but it seems clumsy. I want to do this in one from statement.
var query = from it in context.Viewbostons
select it;
foreach (GPLContext.Viewboston item in query)
{
CatalogEntry card = new CatalogEntry();
card.idx = item.Idx;
card.product = item.Product;
card.size = (long)item.SizeBytes;
card.date = item.Date.ToString();
card.type = item.Type;
card.classification = item.Classification;
card.distributor = item.Distributor;
card.egplDate = item.EgplDate.ToString();
card.classificationVal = (int)item.ClassificationInt;
card.handling = item.Handling;
card.creator = item.Creator;
card.datum = item.Datum;
card.elevation = (int)item.ElevationFt;
card.description = item.Description;
card.dirLocation = item.DoLocation;
card.bbox = item.Bbox;
card.uniqID = item.UniqId;
values.Add(card);
}
CatalogResults response = new CatalogResults();
I just tried this:
var query2 = from item in context.Viewbostons
select new CatalogResults
{ item.Idx,
item.Product,
(long)item.SizeBytes,
item.Date.ToString(),
item.Type,
item.Classification,
item.Distributor,
item.EgplDate.ToString(),
(int)item.ClassificationInt,
item.Handling,
item.Creator,
item.Datum,
(int)item.ElevationFt,
item.Description,
item.DoLocation,
item.Bbox,
item.UniqId
};
But I get the following error:
Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a
collection initializer because it does not implement
'System.Collections.IEnumerable' C:\Users\ysg4206\Documents\Visual
Studio
2010\Projects\CatalogService\CatalogService\CatalogService.svc.cs 91 25 CatalogService
I should tell you what the definition of the CatalogResults is that I want to return:
[DataContract]
public class CatalogResults
{
CatalogEntry[] _results;
[DataMember]
public CatalogEntry[] results
{
get { return _results; }
set { _results = value; }
}
}
My mind is dull today, apologies to all. You are being helpful. The end result is going to be serialized by WCF to a JSON structure, I need the array wrapped in a object with some information about size, etc.
Since .NET 3.0 you can use object initializer like shown below:
var catalogResults = new CatalogResults
{
results = context.Viewbostons
.Select(it => new CatalogEntry
{
idx = it.Idx,
product = it.Product,
...
})
.ToArray()
};
So if this is only one place where you are using CatalogEntry property setters - make all properties read-only so CatalogEntry will be immutable.
MSDN, Object initializer:
Object initializers let you assign values to any accessible fields or properties of an
object at creation time without having to explicitly invoke a constructor.
The trick here is to create a IQueryable, and then take the FirstOrDefault() value as your response (if you want a single response) or ToArray() (if you want an array). The error you are getting (Error 79 Cannot initialize type 'CatalogService.CatalogResults' with a collection initializer because it does not implement 'System.Collections.IEnumerable') is because you're trying to create an IEnumerable within the CatalogEntry object (by referencing the item variable).
var response = (from item in context.Viewbostons
select new CatalogEntry()
{
idx = item.Idx,
product = item.Product,
size = (long)item.SizeBytes,
...
}).ToArray();
You don't have to create anonymous types in a Linq select. You can specify your real type.
var query = context.Viewbostons.Select( it =>
new CatalogEntry
{
idx = it.idx,
... etc
});
This should work:
var query = from it in context.Viewbostons
select new CatalogEntry()
{
// ...
};

How to improve this code about EntityCollections<TEntity>?

This is the code which I'm not convinced. Please check how I'm passing as parameter the entity Collection.
public ExamProduced GetExamProduced(XElement xml)
{
var examProduced = new ExamProduced
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed"),
//Exercises = GetExercises(xml)
};
GetExercises(xml, examProduced.Exercises);
return examProduced;
}
public void GetExercises(XElement xml, EntityCollection<Exercise> entityCollection)
{
var objs =
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
let id2 = (Objective)entityService.Objectives.Where(o => o.ObjectiveID == id).FirstOrDefault()
select new Exercise
{
Objective = id2,
MakeUp = ...
Quantify = ...
Score = ...
};
foreach (var exercise in objs)
{
entityCollection.Add(exercise);
}
}
If not, I'll receiving an error. Like this with this code.
public ExamProduced GetExamProduced(XElement xml)
{
var examProduced = new ExamProduced
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed"),
Exercises = GetExercises(xml)
};
return examProduced;
}
public EntityCollection<Exercise> GetExercises(XElement xml)
{
var objs =
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
select new Exercise
{
ExerciseID = id,
MakeUp = (bool)objective.Attribute("MakeUp"),
Quantify = (byte)(int)objective.Attribute("Quantify"),
Score = (float)objective.Elements().Last().Attribute("Result")
};
var entityCollection = new EntityCollection<Exercise>();
foreach (var exercise in objs)
entityCollection.Add(exercise);
return entityCollection;
}
The error I am getting is below:
InvalidOperationException was unhandled.
The object could not be added to the EntityCollection or
EntityReference. An object that is attached to an ObjectContext cannot
be added to an EntityCollection or EntityReference that is not
associated with a source object.
I hope that from the comments I'm understanding you correctly... If not, I'll update this answer.
Firstly, your EntityCollection<Exercise> GetExercises(XElement xml) is not going to work. As the error message says, you cannot construct a random EntityCollection like that: the EntityCollection needs the object to be attached to the context because it automatically synchronises its list with the context. And since you aren't saying what to attach it to anywhere, it won't work. The only way to make it work would be to avoid creating an EntityCollection in the first place.
Your void GetExercises(XElement xml, EntityCollection<Exercise> entityCollection) procedure could work. However, you need to make sure to actually have an EntityCollection<Exercise> instance to be able to call it. The way you're creating your ExamProduced object, it isn't attached to the context by the time you return from GetExamProduced, so it isn't possible to have a EntityCollection<Exercise> property for it at that point.
Perhaps the easiest way to get things working would be to pass your context to the GetExamProduced method, and let them get attached to the context automatically. I'm assuming it's a common ObjectContext, but you can update it as needed:
public ExamProduced GetExamProduced(XElement xml, YourContext context)
{
var examProduced = new ExamProduced()
{
ExamProducedID = (int)xml.Attribute("ExamID"),
Date = (DateTime)xml.Attribute("Date"),
Seed = (int)xml.Attribute("Seed")
};
context.ExamsProduced.Attach(examProduced);
LoadExercises(xml, context, examProduced);
// examProduced.Exercises should be available at this point
return examProduced;
}
public void LoadExercises(XElement xml, YourContext context, ExamProduced examProduced)
{
foreach (var exercise in
from objective in xml.Descendants("Objective")
where (bool)objective.Attribute("Produced")
let id = (int)objective.Attribute("ID")
let id2 = (Objective)entityService.Objectives.Where(o => o.ObjectiveID == id).FirstOrDefault()
select new Exercise
{
ExamProduced = examProduced,
Objective = id2,
MakeUp = ...
Quantify = ...
Score = ...
}))
{
context.Exercises.Attach(exercise);
}
}
I don't know if these are new objects that should be added in the database, or if these objects are expected to exist in the database already. I've assumed the latter. If the former, .Attach should be updated to .AddObject in two places.
Is this what you're looking for, or did I misunderstand?

how to convert int to string in Linq to entities

My Db column in a string (varchar) and i need to assign it to a int value.
I am using linq to query.Though the code compiles am getting an error at the run time .
Thanks in advance.
PFB my query :
var vlauesCap = from plan in entities.PA_RTM_CAP_Group
select new Business.PartnerProfile.LookUp
{
Id =Convert.ToInt32(plan.cap_group_code),
//(Int32)plan.cap_group_code,
Value = plan.cap_group_name
};
return vlauesCap.ToList();
The EF provider does not know how to translate Convert.ToInt() into SQL it can run against the database. Instead of doing the conversion on the server, you can pull the results back and do the conversion using linq to objects:
// the ToList() here causes the query to be executed on the server and
// the results are returned in a list of anonymous objects
var results = (from plan in entities.PA_RTM_CAP_Group
select new
{
Code = plan.cap_group_code,
Name = plan.cap_group_name
}).ToList();
// the conversion can now be done here using Linq to Objects
var vlauesCap = from r in results
select new Business.PartnerProfile.LookUp
{
Id = Convert.ToInt32(r.Code),
Value = r.Name
};
return vlauesCap.ToList();
You can't do this directly, what you can do is declare a private variable to handle your "mapped" value, and expose the unmapped property...
[Column(Name = "cap_group_code", Storage = "m_cap_group_code")]
private string m_cap_group_code;
public int cap_group_code {
get
{
return Int32.Parse(m_cap_group_code);
}
set
{
m_cap_group_code = value.ToString();
}
}
Try this:
var vlauesCap = from plan in entities.PA_RTM_CAP_Group
select new Business.PartnerProfile.LookUp
{
Id =Convert.ToInt32(plan.cap_group_code),
Convert.ToInt32(plan.cap_group_code),
Value = plan.cap_group_name
};
return vlauesCap.ToList();
Why aren't you using casting for such a purpose, which is a more effective way of achieving this.
Just replace Convert.ToInt32(plan.cap_group_code) with (int)plan.cap_group_code
Do remember, there should be a value in the string and is int, else it will show Exception. If you are not sure about it, then you can further expand the casting to use null coalesciting operator

Entity Framework LINQ projection into custom type results in missing data

I have a many to many relationship between Contractors and SafetyCouncils. They are joined by a bridge table ContractorsSafetyCouncils which consists of ContractorId and SafetyCouncilId. These 2 columns form a composite key. This relationship is mapped correctly in EF4. The Contractor entity has the property:
public virtual ICollection<SafetyCouncil> SafetyCouncils
{
get;
set;
}
And the SafetyCouncil entity has the property:
public virtual ICollection<Contractor> Contractors
{
get;
set;
}
When accessing these properties via lazy loading from a single Contractor or SafetyCouncil entity, they work exactly as expected. But when accessing this relationship in a query:
from c in ContractorRepository.All()
where c.PQFs.Count() > 0
let psmAudits = c.PQFs.SelectMany(pqf => pqf.Audits)
let psmAudit = psmAudits.FirstOrDefault(audit => audit.CompletedDate == psmAudits.Max(a => a.CompletedDate))
let scsAudits = c.PQFs.SelectMany(pqf => pqf.SCSAudits)
let scsAudit = scsAudits.FirstOrDefault(audit => audit.CompletedDate == scsAudits.Max(a => a.CompletedDate))
select new MasterListItem()
{
AdministratorNotes = c.AdminFlags.Where(f => f.IsActive && f.ForPQF).Select(f => f.Text),
CanViewInfo = false,
ContractorName = c.ContractorName,
ContractorId = c.Id,
ContractorTaxId = c.TaxId,
SafetyCouncilIds = c.SafetyCouncils.Select(sc => sc.Id),
PQFSubmitted = c.PQFs.Max(p => p.PQFInfo.SubmittedDate.Value),
PSMAuditId = psmAudit.Id,
PSMAuditComplete = psmAudit.CompletedDate,
PSMAuditStatus = psmAudit.Status.Description,
SCSAuditId = scsAudit.Id,
SCSAuditComplete = scsAudit.CompletedDate
};
The problem occurs with:
SafetyCouncilIds = c.SafetyCouncils.Select(sc => sc.Id),
For every record the SafetyCouncilIds collection has 0 members, when based on the data in the database every record should have at least 1 SafetyCouncilId associated with it.
If I run the same query, but project into an anonymous type instead of the MasterListItem type, it works correctly. Why can't I project this query into my custom type?
Update:
My MasterListItem POCO contained the following properties:
public string SafetyCouncilIdsString
{
get;
set;
}
public IEnumerable<int> SafetyCouncilIds
{
set
{
StringBuilder sb = new StringBuilder(",");
foreach (var id in value)
{
sb.Append(id);
sb.Append(",");
}
this.SafetyCouncilIdsString = sb.ToString();
}
}
The SafetyCouncilIds property was the cause of the problem. I changed this to an automatic property and built the string elsewhere and projecting onto the POCO worked like a charm.
public IEnumerable<int> SafetyCouncilIds
{
set
{
StringBuilder sb = new StringBuilder(",");
foreach (var id in value)
{
sb = sb.Append(id).Append(","); // <-- try this
// *or sb = sb.AppendFormat("{0},", id);*
}
this.SafetyCouncilIdsString = sb.ToString();
}
}
I have two suggestions:
Try to isolate the the problem by removing any extra parts of the query.
Compare the two sql queries generated and find the differences.
Unfortunately, without access to your code or schema, I can't provide a better answer.
The SafetyCouncilIds property was the cause of the problem. I changed this to an automatic property and built the string elsewhere and projecting onto the POCO worked like a charm.

Categories