Multiple fields have the same columnName Android Room - mysql

I have 3 tables ruser, accounts, accountgroup. Each one has a same column called rsuerId.
I created a POJO class with 3 Embedded objects as below.
class GroupChatItem(
#Embedded
val rUserDto: RUserDto,
#Embedded
val account: AccountDto,
#Embedded
val accountGroup: AccountGroupDto
)
Now, i want to make a query that fetches a GroupChatItem with a given rUserId and accountGroupId like the following.
#Query("""
Select ruser.*, accounts.*, accountgroup.*
from ruser
inner join accounts on accounts.rUserId = ruser.rUserId and accounts.active = 1
inner join accountgroup on accountgroup.rUserId = :rUserId and accountGroup.accountGroupId = :accountGroupId
where ruser.rUserId = :rUserId
""")
suspend fun getGroupChatItem(rUserId: Long, accountGroupId: Int): GroupChatItem
Unfortunately i get the following error.
Multiple fields have the same columnName: rUserId. Field names: rUserDto > rUserId, account > rUserId, accountGroup > rUserId.
I have tried to add a prefix to each embedded object but i get also an error. I dont want to retrieve columns one-by-one because there are many of them.
Is there anything that i missed...??
Thank you

Alternatively you can use the prefix attribute of the Embedded anotation:
class GroupChatItem(
#Embedded(prefix = "user_")
val rUserDto: RUserDto,
#Embedded(prefix = "acc_")
val account: AccountDto,
#Embedded(prefix = "accgr_")
val accountGroup: AccountGroupDto
)
and then alias all the columns of each entity in your SQL query.
I think the prefix attribute is s recent update but I am not sure

I don't believe you have any option other than to have/use :-
a) have distinct columns names across the tables that are to be included in joins (then there is no need to prefix the column names),
or
b) to rename the columns using AS when extracting the values along with a prefix when embedding the entity ensuring that the names match.
I believe that a) would be the simpler option as there is a reduction in the chance of inadvertently using the wrong column name.
As I understand it, the column names have to match for Room to be able to know how to be able to copy a value from the underlying result set, which has no indication of what table a value came from to the value in the returned object or objects.
This is an example of the generated code of a similar scenario 3 embedded entities (User, Office and Places) where some of the column names are the same. They each have and id column and User and Places both have a columns named name.
#Override
public UserOfficePlacesCombined getAllUserOfficePlacesCombined() {
final String _sql = "SELECT user.id AS userid, user.name AS username, office.id AS officeid, office.address AS officeaddress, places.id AS placesid, places.name AS placesname FROM User JOIN Office ON User.id = Office.id JOIN Places ON User.id = Places.id";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "userid");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "username");
final int _cursorIndexOfId_1 = CursorUtil.getColumnIndexOrThrow(_cursor, "officeid");
final int _cursorIndexOfAddress = CursorUtil.getColumnIndexOrThrow(_cursor, "officeaddress");
final int _cursorIndexOfId_2 = CursorUtil.getColumnIndexOrThrow(_cursor, "placesid");
final int _cursorIndexOfName_1 = CursorUtil.getColumnIndexOrThrow(_cursor, "placesname");
final UserOfficePlacesCombined _result;
if(_cursor.moveToFirst()) {
final User _tmpUser;
if (! (_cursor.isNull(_cursorIndexOfId) && _cursor.isNull(_cursorIndexOfName))) {
final long _tmpId;
_tmpId = _cursor.getLong(_cursorIndexOfId);
final String _tmpName;
_tmpName = _cursor.getString(_cursorIndexOfName);
_tmpUser = new User(_tmpId,_tmpName);
} else {
_tmpUser = null;
}
final Office _tmpOffice;
if (! (_cursor.isNull(_cursorIndexOfId_1) && _cursor.isNull(_cursorIndexOfAddress))) {
final long _tmpId_1;
_tmpId_1 = _cursor.getLong(_cursorIndexOfId_1);
final String _tmpAddress;
_tmpAddress = _cursor.getString(_cursorIndexOfAddress);
_tmpOffice = new Office(_tmpId_1,_tmpAddress);
} else {
_tmpOffice = null;
}
final Places _tmpPlaces;
if (! (_cursor.isNull(_cursorIndexOfId_2) && _cursor.isNull(_cursorIndexOfName_1))) {
final long _tmpId_2;
_tmpId_2 = _cursor.getLong(_cursorIndexOfId_2);
final String _tmpName_1;
_tmpName_1 = _cursor.getString(_cursorIndexOfName_1);
_tmpPlaces = new Places(_tmpId_2,_tmpName_1);
} else {
_tmpPlaces = null;
}
_result = new UserOfficePlacesCombined();
_result.setUser(_tmpUser);
_result.setOffice(_tmpOffice);
_result.setPlaces(_tmpPlaces);
} else {
_result = null;
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
The critical lines are the ones like :-
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "userid")
This is used to search for the column's names in the Cursor (aka result set) and return the offset to the column, the index then being used to get the actual value from the Cursor.
In your scenario the result set will include some like
rUserId rUserId rUserId*
Which one should it use for which? You may know/understand that first is ruser.rUserId, and that the second is account.rUserId and that the third is accountgroup.rUserId but Room, as it stands, will not know when generating the code. So in all 3 instances when getColumnIndex("rUserId") is used, it will return either 0 (the first) it breaks out of the loop, or 2 if it continues rather than breaks out of the loop (I believe it doesn't break out of the loop).

Related

algorithm verifying data from user beween two tables then insert into another table

Greeting I need to get details from users, in those details the user has I have to validate all the User details validate this details with another table and if the date doesn’t match insert on the table but if it does match then don insert anything, this has to be done for all the users, the domains.
User{
String orderNumber
String dealer
Int UserKm
String dateUser
String adviser
Vehicle vehicle
String dateCreated
Date appointmentDate //this date has to be validated with DateNext
appointmentDate from Appointments domain of it doesn’t exit then you can
insert on that table.
}
Appointments{
User user
Date managementDate
Date lasDataApointies
DateNext appointmentDate
Date NextdAteAppointment
Date callDate
String observations
}
def result = User.executeQuery("""select new map(
mmt.id as id, mmt.orderNumber as orderNumber, mmt.dealer.dealer as
dealer, mmt.UserKm as UserKm, mmt.dateUser as dateUser, mmt.adviser as
adviser, mmt.technician as technician, mmt.vehicle.placa as vehicle,
mmt.dateCreated as dateCreated, mmt.currenKm as currenKm) from User as
mmt """)
def result1=result.groupBy{it.vehicle}
List detailsReslt=[]
result1?.each { SlasDataApointing placa, listing ->
def firsT = listing.first()
int firstKM = firsT.UserKm
def lasT = listing.last()
def lasDataApoint = lasT.id
int lastKM = lasT.UserKm
int NextAppointmentKM = lastKM + 5000
int dayBetweenLastAndNext = lastKM - NextAppointmentKM
def tiDur = getDifference(firsT.dateUser,lasT.dateUser)
int dayToInt = tiDur.days
int restar = firstKM - lastKM
int kmPerDay = restar.div(dayToInt)
int nextMaintenaceDays = dayBetweenLastAndNext.div(kmPerDay)
def nextAppointment = lasT.dateUser + nextMaintenaceDays
detailsReslt<<[placa:placa, nextAppointment:
nextAppointment, manageId:lasDataApoint, nextKmUser: NextAppointmentKM]
}
detailsReslt?.each {
Appointments addUserData = new Appointments()
addUserData.User = User.findById(it.manageId)
addUserData.managementDate = null
addUserData.NextdAteAppointment = null
addUserData.observations = null
addUserData.callDate = it.nextAppointment
addUserData.save(flush: true)
}
println "we now have ${detailsReslt}"
}
Based on the details that are not full and looking at the code I can suggest:
no need to do a query to map you can simply query the list of users and check all the properties like user.vehicle. in any case, you need to check each row.
the groupBy{it.vehicle} is not clear but if needed you can do it using createCriteria projections "groupProperty"
Create 2 service method one for iterating all users and one for each user:
validateAppointment(User user){
/* your validation logic */
....
if (validation term){
Appointments addUserData = new Appointments()
...
}
}
validateAppointments(){
List users = User. list()
users.each{User user
validateAppointment(user)
}
}
you can trigger the validateAppointments service from anywhere in the code or create a scheduled job so it will run automatically based on your needs.
if your list of user is big and also for efficiency you can do bulk update - take a look at my post about it: https://medium.com/meni-lubetkin/grails-bulk-updates-4d749f24cba1
I would suggest to create a Custom Validator using a Service, something like this:
class User{
def appointmentService
...
Date appointmentDate
static constraints = {
appointmentDate validator: { val, obj ->
obj.appointmentService.isDateAppointmentValid(obj.appointmentDate)
}
}
}
But keep in mind that validation may run more often than you think. It is triggered by the validate() and save() methods as you’d expect (as explained in the user guide (v3.1.15)). So I'm not sure if this scenario is the best way to validate àppointmentDate` in your domain, so you have to be careful about that.
Hope this help.

HibernateCriteria left join with OR where clause

We have a problem with one of our queries where the query returns an inner join instead of a left join. The query is intended to retrieve all projects that a user has explicit access rights to (one of the usergroups the user is part of is defined as the project "User" Usergroup) OR implicit action (the project has no "User" Usergroup defined).
The relevant code:
// HibernateCriteria is a custom class that has junction, conjunction, disjunction objects (the ones from Hibernate)
ICriteria criteriaInProjectUserGroup = new HibernateCriteria();
criteriaInProjectUserGroup.addEqualTo(Project.USERGROUP+"." +UserGroup.USERGROUPASSOCIATIONS+"."+UserGroupAssociation.USEROID, user.getOid());
ICriteria criteriaNoProjectUserGroup = CriteriaFactory.createCriteria();
criteriaNoProjectUserGroup.addIsNull(Project.USERGROUP);
/*The join (criteriaIn...) must be added to the simple is null criteria (criteriaNo...),
* to result in a right outer join, otherwise an inner join is created by ojb which returns
* an empty result set */
criteriaNoProjectUserGroup.addOrCriteria(criteriaInProjectUserGroup);
IQuery query = QueryFactory.createQueryByCriteria(ProjectBean.class, criteriaNoProjectUserGroup);
Collection projects = broker.getCollectionByQuery(query);
addEqualTo is defined as:
public void addEqualTo(String attribute, Object value) {
if (value!=null && value instanceof String && containsWildcards((String)value)) {
addLike(attribute, value);
}
else {
attribute = createAliasIfNecessary(attribute);
conjunction.add(eq(attribute, value));
}
}
addIsNull is defined as:
public void addIsNull(String attribute) {
attribute = createAliasIfNecessary(attribute);
conjunction.add(isNull(attribute));
}
addOrCriteria is defined as:
public void addOrCriteria(ICriteria criteria) {
if(criteria instanceof HibernateCriteria) {
HibernateCriteria subCriteria = (HibernateCriteria) criteria;
Junction subJunction = subCriteria.resolveJunction();
if(disjunction == null ) {
disjunction = disjunction();
}
disjunction.add(subJunction);
aliasNameSet.addAll(subCriteria.aliasNameSet);
}
}
resolveJunction is defined as:
private Junction resolveJunction() {
if(junction == null) {
if(disjunction == null) { // no 'or' subcriteria have been added
junction = conjunction; // then the main junction is simply the conjunction
} else {
// disjunction != null means an 'or' subcriteria has been already added.
// then the main junction is disjunction with a conjunction as an operand
junction = disjunction;
junction.add(conjunction);
}
}
return junction;
}
getCollectionByQuery extracts the critera into an org.hibernate.criterion.DetachedCriteria object:
try {
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(clazz);
// create aliases if necessary
Set<String> createdAliasSet = new HashSet<String>();
for(String aliasAssociationPath: aliasNameSet) {
String[] nestedPropertiesArray = aliasAssociationPath.split("\\.");
String previousNestedProperty = null;
for(String nestedProperty: nestedPropertiesArray) {
String currentAliasName =
(previousNestedProperty == null ? nestedProperty : previousNestedProperty + "_" + nestedProperty);
if(!createdAliasSet.contains(currentAliasName)) {
String currentAssociationPath =
(previousNestedProperty == null ? nestedProperty : previousNestedProperty + "." + nestedProperty);
detachedCriteria.createAlias(currentAssociationPath, currentAliasName);
createdAliasSet.add(currentAliasName);
}
previousNestedProperty = currentAliasName;
}
}
detachedCriteria.add(resolveJunction());
return detachedCriteria;
} catch (HibernateException e) {
throw new PersistenceBrokerException(e);
}
It then converts this to a org.hibernate.Criteria object and calls Criteria.list() on that function.
The resulting Mysql query is:
select
this_.oid as oid1_41_2_,
this_.archived as archived2_41_2_,
this_.archivedDateTime as archived3_41_2_,
this_.version as version4_41_2_,
this_.adminGroupOid as adminGro5_41_2_,
this_.buildScript as buildScr6_41_2_,
this_.buildToolType as buildToo7_41_2_,
this_.deployScript as deploySc8_41_2_,
this_.deployToolType as deployTo9_41_2_,
this_.description as descrip10_41_2_,
this_.issueTrackingSystemOid as issueTr11_41_2_,
this_.locked as locked12_41_2_,
this_.name as name13_41_2_,
this_.projectType as project14_41_2_,
this_.testScript as testScr15_41_2_,
this_.testToolType as testToo16_41_2_,
this_.userGroupOid as userGro17_41_2_,
this_.vcrOid as vcrOid18_41_2_,
this_.vcrProjectName as vcrProj19_41_2_,
usergroup1_.oid as oid1_65_0_,
usergroup1_.archived as archived2_65_0_,
usergroup1_.archivedDateTime as archived3_65_0_,
usergroup1_.version as version4_65_0_,
usergroup1_.description as descript5_65_0_,
usergroup1_.name as name6_65_0_,
usergroup1_.type as type7_65_0_,
usergroup_2_.oid as oid1_58_1_,
usergroup_2_.archived as archived2_58_1_,
usergroup_2_.archivedDateTime as archived3_58_1_,
usergroup_2_.version as version4_58_1_,
usergroup_2_.scmUserOid as scmUserO6_58_1_,
usergroup_2_.userGroupOid as userGrou5_58_1_
from
.PROJECT this_
inner join
.USERGROUP usergroup1_
on this_.userGroupOid=usergroup1_.oid
inner join
.SCMUSER_USERGROUP usergroup_2_
on usergroup1_.oid=usergroup_2_.userGroupOid
where
(
(
usergroup_2_.scmUserOid=3
)
or (
this_.userGroupOid is null
)
)
The problem is that this only returns the projects that have a related UserGroup, because it's an inner join. This means that we don't get the projects without a usergroup defined.
I have found that the way to fix this is by using a left outer join for both of the joins, but I can't figure out how to make this work. The problem is that I can't touch the addX methods, because those are used absolutely everywhere in the service classes.
How do I adjust this code so the resultant SQL uses a left outer join instead of an inner join?
After doing some more research, I found that the proper way to fix this is by adding a joinType to createAlias():
detachedCriteria.createAlias(currentAssociationPath, currentAliasName, JoinType.LEFT_OUTER_JOIN);
However, this is a pretty involved fix because there is no current place where we use the joinType parameter, which means I need to further discuss this with more senior developers. For now, I will be using separate queries to retrieve the explicit and implicit access rights and joining them together in Java.

JPA caching database results, need to "un-cache"

I'm seeing "caching" behavior with database (MySQL 5) records. I can't seem to see the new data application side w/o logging in/out or restarting the app server (Glassfish 3). This is the only place in the application where db records are "stuck." I'm guessing I'm missing something with JPA persistence.
I've attempted changing db records by hand, there's still some sort of caching mechanism in place "helping" me.
This is editFile() method that saves new data.
After I fire this, I see the data updated in the db as expected.
this.file is the class level property that the view uses to show file data. It shows old data. I attempt to move db data back in to it after I've fired my UPDATE queries with the filesList setter: this.setFilesList(newFiles);
When the application reads it back out though, GlassFish seems to resond with requests for this data w/ old data.
public void editFile(Map<String, String> params) {
// update file1 record
File1 thisFile = new File1();
thisFile.setFileId(Integer.parseInt(params.get("reload-form:fileID")));
thisFile.setTitle(params.get("reload-form:input-small-name"));
thisFile.setTitle_friendly(params.get("reload-form:input-small-title-friendly"));
this.filesFacade.updateFileRecord(thisFile);
//update files_to_categories record
int thisFileKeywordID = Integer.parseInt(params.get("reload-form:select0"));
this.filesToCategoriesFacade.updateFilesToCategoriesRecords(thisFile.getFileId(), thisFileKeywordID);
this.file = this.filesFacade.findFileByID(thisFile.getFileId());
List<File1> newFiles = (List<File1>)this.filesFacade.findAllByRange(low, high);
this.setFilesList(newFiles);
}
Facades
My Facades are firing native SQL to update each of those DB tables. When I check the DB after they fire, the data is going in, that part is happening as I expect and hope.
File1
public int updateFileRecord(File1 file){
String title = file.getTitle();
String title_titleFriendly = file.getTitle_friendly();
int fileID = file.getFileId();
int result = 0;
Query q = this.em.createNativeQuery("UPDATE file1 set title = ?1, title_friendly = ?2 where file_id = ?3");
q.setParameter(1, title);
q.setParameter(2, title_titleFriendly);
q.setParameter(3, fileID);
result = q.executeUpdate();
return result;
}
FilesToCategories
public int updateFilesToCategoriesRecords(int fileId, int keywordID){
Query q = this.em.createNativeQuery("UPDATE files_to_categories set categories = ?1 where file1 = ?2");
q.setParameter(1, keywordID);
q.setParameter(2, fileId);
return q.executeUpdate();
}
How do I un-cache?
Thanks again for looking.
I don't think caching is the Problem, I think it's transactions.
em.getTransaction().begin();
Query q = this.em.createNativeQuery("UPDATE file1 set title = ?1, title_friendly = ?2 where file_id = ?3");
q.setParameter(1, title);
q.setParameter(2, title_titleFriendly);
q.setParameter(3, fileID);
result = q.executeUpdate();
em.getTransaction().commit();
I recommend to surrond your Writings to the DB with Transactions to get them persisted. Unless you commit requests may return results without the changes.
Ok, JTA does the Transactionmanagement.
Why are you doing this, when you are using JPA.
public int updateFileRecord(File1 file){
String title = file.getTitle();
String title_titleFriendly = file.getTitle_friendly();
int fileID = file.getFileId();
int result = 0;
Query q = this.em.createNativeQuery("UPDATE file1 set title = ?1, title_friendly = ?2 where file_id = ?3");
q.setParameter(1, title);
q.setParameter(2, title_titleFriendly);
q.setParameter(3, fileID);
result = q.executeUpdate();
return result;
}
This should work and update the internal State that comes with JPA
public int updateFileRecord(File1 file){
em.persist(file);
}
#daniel & #Tiny got me going on this one, thanks again guys.
I wanted to point out that I used the .merge() method out of the Entity Manager class.
It's important to note that for .merge() to UPDATE the record instead of INSERTing a new one; that the object you're submitting to .merge() must include all properties respective of the fields in the database table (that your DAO knows about) or you will INSERT new database records.
public void updateFileRecord(File1 file){
em.merge(file);
}

Using Cross join in Asp .net Web API getting error

Hi i am Using Cross join through in Asp .net Web API with a mysql database and getting the following error :
Error 1 Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable'. An explicit conversion exists (are you missing a cast?)
This is my controller code
private myappEntities db = new myappEntities();
public IQueryable<comment>GetPicturesandtheirCommnets()
{
var combo=from p in db.picturedetails
from c in db.comments
select new
{
p.iduser,p.idpictures,p.likes,p.nuditylevel,p.picTitle,p.pictime,p.fakeslevel,
c.comment1,c.ctime,c.idcomments,c.spamlevel,c.targetpictureid
};
return combo;
}
Why am i getting this error?? Any help?
Your query (combo) returns an anonymous type, and your method signature says you are returning an IQueryable<comment>. You can't return anonymous types from methods, so you have two options:
Option 1: Select just fields from the Comment table to return.
Option 2: Create a new class that includes details from Comments and PictureDetails, and modify your query to select new CommentAndPictureDetails (or whatever you name your class).
The modified query would look like this:
var combo=from p in db.picturedetails
from c in db.comments
select new CommentAndPictureDetails
{
IdUser = p.iduser,
IdPictures = p.idpictures,
Likes = p.likes,
NudityLevel = p.nuditylevel,
PicTitle = p.picTitle,
PicTime = p.pictime,
FakesLevel = p.fakeslevel,
Comment1 c.comment1,
CTime = c.ctime,
IdComments = c.idcomments,
SpamLevel = c.spamlevel,
TargetPictureId = c.targetpictureid
};
Your class declaration for CommentAndPictureDetails would be like so:
public class CommentAndPictureDetails
{
public string IdUser {get; set;}
// I don't know the data types, so you'll have to make sure
// the .NET type matches the DB type.
}

Explicit construction of entity type '###' in query is not allowed.

Using Linq commands and Linq To SQL datacontext, Im trying to instance an Entity called "Produccion" from my datacontext in this way:
Demo.View.Data.PRODUCCION pocoProduccion =
(
from m in db.MEDICOXPROMOTORs
join a in db.ATENCIONs on m.cmp equals a.cmp
join e in db.EXAMENXATENCIONs on a.numeroatencion equals e.numeroatencion
join c in db.CITAs on e.numerocita equals c.numerocita
where e.codigo == codigoExamenxAtencion
select new Demo.View.Data.PRODUCCION
{
cmp = a.cmp,
bonificacion = comi,
valorventa = precioEstudio,
codigoestudio = lblCodigoEstudio.Content.ToString(),
codigopaciente = Convert.ToInt32(lblCodigoPaciente.Content.ToString()),
codigoproduccion = Convert.ToInt32(lblNroInforme.Content.ToString()),
codigopromotor = m.codigopromotor,
fecha = Convert.ToDateTime(DateTime.Today.ToShortDateString()),
numeroinforme = Convert.ToInt32(lblNroInforme.Content.ToString()),
revisado = false,
codigozona = (c.codigozona.Value == null ? Convert.ToInt32(c.codigozona) : 0),
codigoclinica = Convert.ToInt32(c.codigoclinica),
codigoclase = e.codigoclase,
}
).FirstOrDefault();
While executing the above code, I'm getting the following error that the stack trace is included:
System.NotSupportedException was caught
Message="The explicit construction of the entity type 'Demo.View.Data.PRODUCCION' in a query is not allowed."
Source="System.Data.Linq"
StackTrace:
en System.Data.Linq.SqlClient.QueryConverter.VisitMemberInit(MemberInitExpression init)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitSelect(Expression sequence, LambdaExpression selector)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitFirst(Expression sequence, LambdaExpression lambda, Boolean isFirst)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node)
en System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
en System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
en System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
en System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
en Demo.View.InformeMedico.realizarProduccionInforme(Int32 codigoExamenxAtencion, Double precioEstudio, Int32 comi) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 602
en Demo.View.InformeMedico.UpdateEstadoEstudio(Int32 codigo, Char state) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 591
en Demo.View.InformeMedico.btnGuardar_Click(Object sender, RoutedEventArgs e) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 683
InnerException:
Is that now allowed in LINQ2SQL?
Entities can be created outside of queries and inserted into the data store using a DataContext. You can then retrieve them using queries. However, you can't create entities as part of a query.
I am finding this limitation to be very annoying, and going against the common trend of not using SELECT * in queries.
Still with c# anonymous types there is a workaround, by fetching the objects into an anonymous type, and then copy it over into the correct type.
For example:
var q = from emp in employees where emp.ID !=0
select new {Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.ToList();
List<User> users = new List<User>(r.Select(new User
{
Name = r.Name,
EmployeeId = r.EmployeeId
}));
And in the case when we deal with a single value (as in the situation described in the question) it is even easier, and we just need to copy directly the values:
var q = from emp in employees where emp.ID !=0
select new { Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.FirstOrDefault();
User user = new User { Name = r.Name, EmployeeId = r.ID };
If the name of the properties match the database columns we can do it even simpler in the query, by doing select
var q = from emp in employees where emp.ID !=0
select new { emp.First, emp.Last, emp.ID }
One might go ahead and write a lambda expression that can copy automatically based on the property name, without needing to specify the values explictly.
Here's another workaround:
Make a class that derives from your LINQ to SQL class. I'm assuming that the L2S class that you want to return is Order:
internal class OrderView : Order { }
Now write the query this way:
var query = from o in db.Order
select new OrderView // instead of Order
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
// etc.
};
Cast the result back into Order, like this:
return query.Cast<Order>().ToList(); // or .FirstOrDefault()
(or use something more sensible, like BLToolkit / LINQ to DB)
Note: I haven't tested to see if tracking works or not; it works to retrieve data, which is what I needed.
I have found that if you do a .ToList() on the query before trying to contruct new objects it works
I just ran into the same issue.
I found a very easy solution.
var a = att as Attachment;
Func<Culture, AttachmentCulture> make =
c => new AttachmentCulture { Culture = c };
var culs = from c in dc.Cultures
let ac = c.AttachmentCultures.SingleOrDefault(
x => x.Attachment == a)
select ac == null ? make(c) : ac;
return culs;
I construct an anonymous type, use IEnumerable (which preserves deferred execution), and then re-consruct the datacontext object. Both Employee and Manager are datacontext objects:
var q = dc.Employees.Where(p => p.IsManager == 1)
.Select(p => new { Id = p.Id, Name = p.Name })
.AsEnumerable()
.Select(item => new Manager() { Id = item.Id, Name = item.Name });
Within the book "70-515 Web Applications Development with Microsoft .NET Framework 4 - Self paced training kit", page 638 has the following example to output results to a strongly typed object:
IEnumerable<User> users = from emp in employees where emp.ID !=0
select new User
{
Name = emp.First + " " + emp.Last,
EmployeeId = emp.ID
}
Mark Pecks advice appears to contradict this book - however, for me this example still displays the above error as well, leaving me somewhat confused. Is this linked to version differences? Any suggestions welcome.
I found another workaround for the problem that even lets you retain your result as IQueryale, so it doesn't actually execute the query until you want it to be executed (like it would with the ToList() method).
So linq doesn't allow you to create an entity as a part of query? You can shift that task to the database itself and create a function that will grab the data you want. After you import the function to your data context, you just need to set the result type to the one you want.
I found out about this when I had to write a piece of code that would produce a IQueryable<T> in which the items don't actually exist in the table containing T.
pbz posted a work around by creating a View class inherited from an entity class that you could be working with. I'm working with a dbml model of a table that has > 200 columns. When I try and return the whole table I get "Root Element missing" errors. I couldn't find anyone who wanted to deal with my particular issue so I was looking at rewriting my entire approach. Just creating a view class for the entitiy class worked in my case.
As pbz suggests : Create a view class that inherits from your entity class. For me this is tbCamp so :
internal class tbCampView : tbCamp
{
}
Then use the view class in your query :
using (var dc = ConnectionClass.Connect(Dev))
{
var camps = dc.tbCamps.Select(s => new tbCampView
{
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
SmartTableViewer(camps, dg1);
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
Here is what happens when you try and create an entity class object in the query.
I didn't want to have to use an anonymous type if I could help it because I wanted the type to be tbCamp. Since tbCampView is of type tbCamp the is operator works well. see Brian Hasden's answer Passing a generic List<> in C#
I'm surprised this is even an issue but with larger tables I run into this error so I thought I would just show it here :
When trying to read this table into memory I get the following error. There are < 2000 rows but the columns are > 200 for each. I don't know if that is an issue or not.
If I just want a few columns I need to create a custom class and handle that which isn't that big of a pain. With the approach pbz provided I don't have to worry about it.
Here is the entire project in case it helps someone.
public partial class Form1 : Form
{
private const bool Dev = true;
public Form1()
{
InitializeComponent();
}
private void btnGetAllCamps_Click(object sender, EventArgs e)
{
using (var dc = ConnectionClass.Connect(Dev))
{
IQueryable<tbCampView> camps = dc.tbCamps.Select(s => new tbCampView
{
// Project columns as needed.
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
// pass in as a
SmartTableViewer(camps);
}
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
//var newRecord = record;
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
internal class tbCampView : tbCamp
{
}
}