I have a table with below columns.
CREATE TABLE [dbo].[tbl_QuizQue](
[id] [int] IDENTITY(1,1) NOT NULL,
[question] [nvarchar](max) NOT NULL,
[opt1] [nvarchar](50) NOT NULL,
[opt2] [nvarchar](50) NOT NULL,
[opt3] [nvarchar](50) NOT NULL,
[ans] [nvarchar](50) NOT NULL
)
I am trying to get a random record from this table, but only specific columns Like id, question, opt1, opt2, opt3. Present I am getting random record but it is fetching all columns. I tried below code for fetching a record with specific columns
dbUserEntities obj = new dbUserEntities();
// GET api/values
public IEnumerable<tbl_QuizQue> Get()
{
var result = obj.tbl_QuizQue.OrderBy(r => Guid.NewGuid()).Select(o => new { o.id, o.question, o.opt1, o.opt2, o.opt3 });
return result;
}
Here, i am getting below error while return result;
Cannot implicitly convert type 'System.Linq.IQueryable<AnonymousType#1>' to
'System.Collections.Generic.IEnumerable<studentQuiz.tbl_QuizQue>'. An explicit conversion exists (are you missing a cast?)
Help me where I am doing wrong.
I'd do this:
public IEnumerable<tbl_QuizQue> Get()
{
var result = obj.tbl_QuizQue.OrderBy(r => Guid.NewGuid())
.Select(o => new { o.id, o.question, o.opt1, o.opt2, o.opt3 })
.AsEnumerable()
.Select(q => new tblQuizQue {q.id, q.question, q.opt1, q.opt2, q.opt3);
return result;
}
By first getting back just the columns you are interested in, you can constrain the query. You can then map them into the object you are interested in.
Change the return type of the Get function to
IEnumerable<dynamic>
and your code will execute.
As for whether it's a good idea, I guess you have to defer to the customs of the c# tribe.
I'd be tempted to use it all the time.
Related
I have two database tables for documenting a wound healing progression. Those are joined over the wound_id-Column like this:
So for one wound, I can create many progresses to show the healing of it. This is working fine.
Here is the code for the tables:
Table wound_details:
CREATE TABLE [dbo].[epadoc_mod_wound_details] (
[wound_id] INT IDENTITY (1, 1) NOT NULL,
[wound_type] VARCHAR (500) NULL,
[wound_description] VARCHAR (500) NULL,
[decuGrade] INT NULL,
[wound_comments] VARCHAR (500) NULL,
[wound_timeReal] DATETIME NULL,
[wound_timeGiven] DATETIME NULL,
[casenumber] INT NULL,
[username] VARCHAR (50) NULL,
[infectionstate] VARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([wound_id] ASC)
);
Table wound_progress:
CREATE TABLE [dbo].[epadoc_mod_wound_progress] (
[progress_id] INT IDENTITY (1, 1) NOT NULL,
[wound_length] INT NULL,
[wound_width] INT NULL,
[wound_depth] INT NULL,
[wound_surrounding] VARCHAR (500) NULL,
[wound_consistence] VARCHAR (500) NULL,
[wound_state] VARCHAR (200) NULL,
[wound_painscale] INT NULL,
[wound_itch] INT NULL,
[wound_id] INT NULL,
PRIMARY KEY CLUSTERED ([progress_id] ASC),
CONSTRAINT [FK_epadoc_mod_wound_progress_fk] FOREIGN KEY ([wound_id]) REFERENCES [dbo].[epadoc_mod_wound_details] ([wound_id]) ON DELETE CASCADE
);
Then I wrote a SELECT-Query to show all wounds for specific case number which are documented for the patient:
SELECT DISTINCT
dbo.epadoc_mod_wound_details.wound_id, dbo.epadoc_mod_wound_details.casenumber, dbo.epadoc_mod_wound_details.wound_type, dbo.epadoc_mod_wound_progress.progress_id, dbo.epadoc_mod_wound_details.wound_comments, dbo.epadoc_mod_wound_details.wound_timeReal, dbo.epadoc_mod_wound_details.username
FROM dbo.epadoc_mod_wound_details LEFT JOIN
dbo.epadoc_mod_wound_progress
ON dbo.epadoc_mod_wound_details.wound_id = dbo.epadoc_mod_wound_progress.wound_id
WHERE dbo.epadoc_mod_wound_details.casenumber = #casenr;
This is working fine though, but the problem is that ALL wound progresses are shown in the GridView, here is an example so you can see what I mean:
What I want to do is just show the latest progress of one wound, so for the above example just show the last entry with progressID 65:
33 65 1111111 Dekubitus
34 .. ....... .........
The SELECT DISTINCT approach didn't work and I also tried with MAX(progressID) but I always seem to get errors. I think I have to do something with ORDER BY or a second SELECT-Query before the JOIN.
Thanks for any advice!
You should use GROUP BY combined with MAX in your query.
SELECT
dbo.epadoc_mod_wound_details.wound_id,
dbo.epadoc_mod_wound_details.casenumber,
dbo.epadoc_mod_wound_details.wound_type,
MAX(dbo.epadoc_mod_wound_progress.progress_id) AS progress_id,
dbo.epadoc_mod_wound_details.wound_comments,
dbo.epadoc_mod_wound_details.wound_timeReal,
dbo.epadoc_mod_wound_details.username
FROM
dbo.epadoc_mod_wound_details
LEFT JOIN
dbo.epadoc_mod_wound_progress ON
dbo.epadoc_mod_wound_details.wound_id = dbo.epadoc_mod_wound_progress.wound_id
GROUP BY
dbo.epadoc_mod_wound_details.wound_id,
dbo.epadoc_mod_wound_details.casenumber,
dbo.epadoc_mod_wound_details.wound_type,
dbo.epadoc_mod_wound_details.wound_comments,
dbo.epadoc_mod_wound_details.wound_timeReal,
dbo.epadoc_mod_wound_details.username;
Since you only want the progress_id, The easies way to do it is using a correlated subquery:
SELECT wound_id,
casenumber,
wound_type,
(
SELECT TOP 1 progress_id
FROM dbo.epadoc_mod_wound_progress AS WP
WHERE WP.wound_id = WD.wound_id
ORDER BY progress_id
) As progress_id,
wound_comments,
wound_timeReal,
username
FROM dbo.epadoc_mod_wound_details As WD
WHERE casenumber = #casenr;
I understand you need each record of "epadoc_mod_wound_details" with the latest record of "epadoc_mod_wound_progress".
You can try this:
select wound.wound_id, wound.casenumber, wound.wound_type,
wound.wound_comments, wound.wound_timeReal, wound.username, MAX(progress_id)
from epadoc_mod_wound_details wound
left join epadoc_mod_wound_progress progress on wound.wound_id = progress.wound_id
where wound.casenumber = ''
group by wound.wound_id, wound.casenumber, wound.wound_type,
wound.wound_comments, wound.wound_timeReal, wound.username
My problem: When using this extension it only maps up the ID value from the database to class object.
So I'm curious how do i make it map the other values?
C# Mapper
public class SoftwareReleaseTypeHandle : SqlMapper.TypeHandler<SoftwareRelease>
{
public override SoftwareRelease Parse(object value)
{
if (value == null || value is DBNull)
{
return null;
}
SoftwareRelease rel = new SoftwareRelease();
rel.ID = (Guid)value;
return rel;
}
public override void SetValue(IDbDataParameter parameter, SoftwareRelease value)
{
parameter.DbType = DbType.Guid;
parameter.Value = value.ID.ToString();
}
}
The SQL table looks like this:
CREATE TABLE [dbo].[SoftwareRelease](
[ID] [uniqueidentifier] NOT NULL,
[Type] [tinyint] NOT NULL,
[ReleaseType] [tinyint] NOT NULL,
[Version] [nchar](30) NOT NULL,
[ZipFile] [varbinary](max) NOT NULL,
[Date] [datetime] NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
The reason i used this mapper was so in other places in my code i could do, and it would automaticly get mapped up:
class MyInstallation
{
public string Bla;
public string BlaBla;
SoftwareRelease MyInstallation;
}
And when i then use Dapper to get from a table that looks like.
CREATE TABLE [dbo].[MyInstallation](
[ID] [uniqueidentifier] NOT NULL,
[Bl] [nchar](30) NOT NULL,
[BlaBla] [nchar](30) NOT NULL,
[MyInstallation] [uniqueidentifier] NOT NULL,
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Dapper type handlers are intended to map a single column value in your database to a single member in your POCO. They are used for types that Dapper's core doesn't understand - generally anything that isn't a .NET core type.
You don't say exactly what you need to do but if you're just trying to read SoftwareRelease rows from the database then you can simply do the following:
myDb.Query<SoftwareRelease>("SELECT * FROM SoftwareRelease");
Update
Sounds like what you really want is multi-mapping. If you have the following query:
SELECT a.Bla, a.BlaBla, b.*
FROM MyInstallation a
INNER JOIN SoftwareRelease b ON a.SoftwareReleaseId = b.ID
Then you can use the following to populate the object:
myDB.Query<MyInstallation, SoftwareRelease, MyInstallation>(
sql,
(installation, softwareRelease) => {
installation.SoftwareRelease = softwareRelease;
});
I am trying to figure out the proper query in linq to sql, but I just cant figure out how to do so. Lets say I have a table with the following (this table basically is a one to many relationship)
Id (PK) | SupervisorId | EmployeeId
1 1 5
2 1 6
3 1 7
4 2 5
5 2 6
6 3 7
7 4 7
8 4 8
I want my linq to sql query to find the supervisorId which has for employeeId 5 and 6. The query would return 2 only. I could use 2 where clause, but lets say I would want to input 3 employeeIds, my query would have to modified. If the passed permutation doesnt exist for one matched SupervisorId (ex: 5,6,8 in this case), the result would be null or empty.
The function would look like this:
int FindSuperVisorId(List<int> employeeIds);
I really dont know where to start in linq to sql for this type of scenario.
Thanks
So I'm pretty sure that this query should be converted property to LINQ to SQL, but I'm not completely sure.
So first we group by supervisor so that we have sequences of employees for that supervisor. Then we use Except with the employees you're interested in in both directions. If the the count of both of those Except calls is zero then the sets are exactly equal. There are more efficient ways of determining if two sets are equal in linq-to-objects, but I doubt they would be properly converted to SQL code.
var supervisorId = table.GroupBy(item => item.SupervisorId)
.Select(group => new
{
additionalItems = group.Select(item => item.EmployeeId).Except(employees),
missingItems = employees.Except(group.Select(item => item.EmployeeId)),
group = group
})
.Where(queries => queries.additionalItems.Count() == 0
&& queries.missingItems.Count() == 0)
.Select(queries => queries.group.Key)//gets the supervisorID
.FirstOrDefault();
I had to model your table as a many-many relationship as follows:
CREATE TABLE [dbo].[Employee](
[Name] [nvarchar](50) NOT NULL,
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
CREATE TABLE [dbo].[SupervisorEmployees](
[SupervisorId] [int] NOT NULL,
[EmployeeId] [int] NOT NULL,
CONSTRAINT [PK_SupervisorEmployees] PRIMARY KEY CLUSTERED
(
[SupervisorId] ASC,
[EmployeeId] ASC
)
GO
ALTER TABLE [dbo].[SupervisorEmployees] WITH CHECK ADD CONSTRAINT [FK_SupervisorEmployees_Employee] FOREIGN KEY([SupervisorId])
REFERENCES [dbo].[Employee] ([Id])
GO
ALTER TABLE [dbo].[SupervisorEmployees] CHECK CONSTRAINT [FK_SupervisorEmployees_Employee]
GO
ALTER TABLE [dbo].[SupervisorEmployees] WITH CHECK ADD CONSTRAINT [FK_SupervisorEmployees_Employee1] FOREIGN KEY([EmployeeId])
REFERENCES [dbo].[Employee] ([Id])
GO
ALTER TABLE [dbo].[SupervisorEmployees] CHECK CONSTRAINT [FK_SupervisorEmployees_Employee1]
GO
Then using Entity Framework database first (not Linq to SQL unfortunately) the following LINQPad code works fine:
void Main()
{
FindSupervisorIds( new List<int>{5,6} ).Dump();
}
IEnumerable<int> FindSupervisorIds(List<int> employeeIds)
{
// two Excepts to do 'sequence equals'
var supervisors = Employees.Where (e =>
!e.Employees.Select (em => em.Id).Except(employeeIds).Any()
&& !employeeIds.Except(e.Employees.Select (em => em.Id)).Any()
);
return supervisors.Select (s => s.Id).Distinct();
}
int? FindSupervisorId(List<int> employeeIds)
{
var supervisors = FindSupervisorIds(employeeIds).ToList();
if(supervisors.Count == 1)
{
return supervisors.First ();
}
return null;
}
FindSupervisorIds generates a single SQL query. If you need to check there's only one matching supervisor it's probably best to call ToList() on the returned list of supervisors as in FindSupervisorId.
Trying to do the same thing with LINQ to SQL fails due to the calls to Except with the exception
'NotSupportedException: Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.'
one possibility:
public int FindSuperVisorId(IEnumerable<Employee> employes)
{
var distinctSupervisors = employes.Select(e => e.SuperVisor).Distinct();
var superVisor = distinctSupervisors.Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor))).FirstOrDefault();
return superVisor;
}
and in case you want all matches of same supervisors:
public IEnumerable<int> FindSuperVisorId(IEnumerable<Employee> employes)
{
var distinctSupervisors = employes.Select(e => e.SuperVisor).Distinct();
var equalSupervisors = distinctSupervisors .Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor)));
return equalSupervisors;
}
or directly:
public IEnumerable<int> FindSuperVisorId(IEnumerable<Employee> employes)
{
return employes.Select(e => e.SuperVisor).Distinct()
.Where(supervisor => employes.All(employee => employee.SuperVisor.Equals(supervisor)));
}
I am interested in sorting a query using entity framework and am having trouble sorting using a dynamic order by expression,
I have a parent record (DatasetRecord) with a one to many relationship consisting of a number of associated values (DatasetValues), I would like to be able to sort based on the data stored within the value if it matches a certain field,
So I need to sort by the Value of DatasetValues where it matches a supplied FieldID
public IEnumerable<DatasetRecordDto> FetchData(ModuleSettingsDto settings)
{
var query = _context.DatasetRecords.AsQueryable().Where(r => r.DatasetId == settings.DatasetId && r.IsDeleted == false);
foreach (var filter in settings.Filters)
{
// this works fine, check we have current field and value matches
query = query.Where(i => i.DatasetValues.Any(x => x.DatasetFieldId == filter.DatasetFieldId && x.Value == filter.Value));
}
foreach (var sort in settings.Sort)
{
switch (sort.SortDirection)
{
case "asc":
// THIS IS WHERE I NEED THE DYNAMIC ORDER BY EXPRESSION,
// THIS SHOULD SORT BY DATETIME WHERE FIELD ID MATCHES
query = query.OrderBy(i => i.DatasetValues.Any(x => x.ValueDateTime && x.DatasetFieldId == sort.DatasetFieldId));
break;
}
}
return ShapeResults(query);
}
And here is my database schema:
CREATE TABLE [dbo].[DatasetRecords](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DatasetId] [int] NOT NULL
)
CREATE TABLE [dbo].[DatasetFields](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Label] [varchar](50) NOT NULL,
[DatasetId] [int] NULL,
[DataType] [int] NOT NULL
)
CREATE TABLE [dbo].[DatasetValues](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DatasetRecordId] [int] NOT NULL,
[DatasetFieldId] [int] NOT NULL,
[ValueString] [varchar](8000) NULL,
[ValueInt] [int] NULL,
[ValueDateTime] [datetime] NULL,
[ValueDecimal] [decimal](18, 2) NULL,
[ValueBit] [bit] NULL
)
Please let me know if you need any further info, any help is greatly appreciated,
UPDATE: I am not having an issue with creating a dynamic order by expression, I am trying to apply a conditional sort order for parent records based on the values in a child table
Have you tried running the Linq code on something like LinqPad, it lets you run Linq code against a dataset and shows you the generated SQL. You can take that SQL and run it manually to make sure it isn't some invalid SQL that is causing the error.
I have created the EF model and then in a Class I have written the following Code to retrieve the value form the DataBase. And store the value in another Table. But it gives me the Error "DATAREADER IS INCompatable" as explained Below..
EmpRole empr = new EmpRole();
empr.EmpId = strEmpId;
string str="select RoleId from RoleName where roleName like '"+strDesignation+"'";
var context= DbAccess.Data.ExecuteStoreQuery<RoleName>(str, null); //Here Showing Error
empr.RoleId = Convert.ToInt16(context);
DbAccess.Data.EmpRoles.AddObject(empr);
DbAccess.Commit();
It's showing the error like:
DataTables were:
CREATE TABLE [dbo].[RoleName](
[SNo] [int] IDENTITY(1,1) NOT NULL,
[RoleId] [smallint] NOT NULL,
[RoleName] [varchar](50) NULL,
CONSTRAINT [PK_RoleName] PRIMARY KEY CLUSTERED
(
[RoleId] ASC
)
CREATE TABLE [dbo].[EmpRoles](
[Sno] [int] IDENTITY(1,1) NOT NULL,
[EmpId] [varchar](8) NOT NULL,
[RoleId] [smallint] NOT NULL,
[ReportingToId] [varchar](8) NULL,
CONSTRAINT [PK_EmpRoles] PRIMARY KEY CLUSTERED
(
[Sno] ASC
)
The data reader is incompatible with the specified 'MyOrgDBModel.RoleName'. A member of the type, 'SNo', does not have a corresponding column in the data reader with the same name.
Please tell me the reasons what to do to execute the sqlQuery.
This is because you are not selecting the SNo column in your select query. As you are populating in RoleName and it has property SNo column should be present in data reader. If you just want the RoleId to be in query then create new type with one property RoleId and use this. Create new type like below
public class CustomRoleName
{
int RoleId { get; set; }
}
Now change your code as follow
EmpRole empr = new EmpRole();
empr.EmpId = strEmpId;
string str="select RoleId from RoleName where roleName like '"+strDesignation+"'";
foreach(CustomRoleName rn in DbAccess.Data.ExecuteStoreQuery<CustomRoleName>(str))
{
empr.RoleId = rn.RoleId ;
DbAccess.Data.EmpRoles.AddObject(empr);
DbAccess.Commit();
break;
}