Dapper Multiple Results From single query - c#

Hi i am trying to get to grips with Dapper.
My situation is i want to pull two values from a query into two separate strings. Im not sure if i am going about this in the correct way, but this is what i am doing:
string sql = #"Select type, name
FROM ZipData
WHERE Zip = #zip";
using (var multi = conn.QueryMultiple(sql, new { zip = zip }))
{
string result = multi.Read<string>().SingleOrDefault();
}
And i am getting Cannot access a disposed object. Object name: 'GridReader'. when trying to read the second string.The thing is it gets the first value correctly and has both the fields in in the reader i am trying to get. Im sure im misusing the api.
What am i doing wrong here? Ive googled but can find a specific example.

You are mis-using QueryMultiple. That is defined for compound SQL statements that return multiple result sets. Something like:
SELECT Foo FROM MyTable;
SELECT Bar FROM MyOtherTable;
On the other hand, you are trying to get two different columns from a single result set, so you should just use the normal Query method:
var result = conn.Query(sql, new { zip = zip }).Single();
var type = result.type;
var name = result.name;
Query returns an enumerable (because generally a query can return multiple rows). It appears that you only want one row, however, so we invoke .Single at the end to just get that row. From there, the return type is dynamic so you can simply refer to the properies implied by the columns in your SELECT statement: type and name.

Related

How to SELECT dynamic type of objects in SQLiteAsyncConnection-

I'm trying to build a database using SQLiteAsyncConnection where all of it's queries comes from the server.
database = new SQLiteAsyncConnection(dbPath);
The server's queries are divided into two parts[execution & retrieval]
for the execution queries I'm using this line of code
database.ExecuteAsync(serverResponse);
where serverResponse may be a sql CREATE, INSERT or UPDATE statement.
As for the retrieval queries I'm trying to find a way to retrieve dynamic objects from the database and serialize them to send them to the database. I'm trying to make something like this to retrieve the list of objects in database
var ls = await database.QueryAsync<dynamic>(serverResponse);
but this statement returns a List of objects, and I don't know how to use it.
I tried something like this locally
database.ExecuteAsync("CREATE TABLE Persons(PersonID int);");
database.ExecuteAsync("INSERT INTO Persons(PersonID) VALUES (5);");
var ls = await database.QueryAsync<dynamic>("SELECT * FROM Persons");
int x = ls[0].PersonID;
But it gives me this error 'object' does not contain a definition for 'PersonID'; which says that I need to cast the object to a Person class to be able to use it.(Note that I don't have any class called Person that has a public property PersonID; objects are dynamic and I can't tell what the server's query are).
I tried to serialize ls
string str = JsonConvert.SerializeObject(ls);
but it returns a JSON array with empty object [{}].
How can I serialize the result of any query without the need to cast it.
var ls = await database.QueryAsync<object>("SELECT * FROM Persons");
var json = JsonConvert.SerializeObject(ls);
try this ?

how to use LINQ to find an object in csv file

Hy,
I have a csv file like that
user_name1,c:\photo\user_photo.jpg,0,0,0,0
in which every line refers a distinct object with own fileds separated from comma.
How I find a particular object knowing the user name? I use distinct user_names. And after that how I make that object, curent object that I use?
What i have done until now:
StreamReader sc = new StreamReader(#"C:\Player.csv");
String linie = sc.ReadLine();
while (!String.IsNullOrEmpty(linie))
{
string[] stringu = linie.Split(',');
Player player = new Player(stringu[0], stringu[1], int.Parse(stringu[2]), int.Parse(stringu[3]), int.Parse(stringu[4]));
players.Add(player);
linie = sc.ReadLine();
}
sc.Close();
var query = players.Where(a => a.Name == label6.Text);
}
Sincerly,
You could try to use a library, which makes easy the use of CSV files with LINQ queries. Please look here.
Firstly I'd suggest that for parsing a CSV file you don't roll your own field splitter. I'd suggest using something like the TextFieldParser (which can be used in C# by referencing Microsoft.VisualBasic).
Once you've created the parser you can use it to get the array of strings for each record represented by line in your application:
List<Players> players = new List<Players>();
var parser = new TextFieldParser();
while ( !parser.EOF )
{
string [] playerFields = parser.ReadFields();
// Create player from fields
var player = Player.FromFields(playerFields);
players.Add(player);
}
Now it really depends on whether you want to continuously query the players in the file, as if you do then getting an in-memory copy and using LINQ makes sense, else if you only want to query once then I'd simply do the check line by line.
Assuming that you do want to query multiple times then parsing the file and holding the values in a List or similar makes sense (assuming the file isn't ridiculously big).
Finally, if you has distinct user names then you could use the FirstOrDefault method in LINQ to give you the single player back that matches.
var player = players.FirstOrDefault(p => p.Name.Equals(textBox.Text));
Or if you know that you have unique player names you could just store the whole lot in a dictionary....?
if ( players.ContainsKey(textBox.Text) )
{
var player = players[textBox.Text];
}
Anyway, just some thoughts.
The only problem you have at the moment, as far as I can see, it that you say you are looking to fetch "a particular object", but in your last line you are using Where which is going to return an IEnumerable containing one, none or many such objects. If you want to fetch one uniquely named object you should use Single or SingleOrDefault, e.g.
var myPlayer = players.Single(x => x.Name == label6.Text);
The answer you have accepted has merely converted your query from method syntax to the equivalent query syntax and introduced a compilation error by attempting to convert the resulting IEnumerable<Player> to a Player.
In anticipation of a problem you may be about to have, you might also want to look into Microsoft.VisualBasic.FileIO.TextFieldParser (you'll need to add a reference to Microsoft.VisualBasic.dll), which will allow you to parse a CSV in a more robust way, i.e. handle fields containing commas etc., using something like the following:
using (var parser = new TextFieldParser(filename))
{
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
var p = parser.ReadFields();
players.Add(new Player(p[0], p[1], int.Parse(p[2]), int.Parse(p[3]), int.Parse(p[4])));
}
}
Have you tried something like
Player selected_player = from pl in players
where pl.Name == label6.Text
select pl;
?

Trying to update a DataRow in C# with LINQ

I'm trying to find, then update, a specific DataRow in a DataTable. I've tried a few things based on my searches, and the code below seems to be the closest I can get. The linq will return one row. With that row, I'd like to update column values (Status, StopTime, Duration). I can't for the life of me find how to do this.. I've tried casting, but I'm new to linq and don't see how to update these values.
private DataTable downloadProcStatusTable;
void UpdateDataDownloadProcedureList(ProcedureStats ProcStats)
{
var currentStatRow = from currentStat in downloadProcStatusTable.AsEnumerable()
where currentStat.Field<String>("ProcedureName") == ProcStats.ProcName
select currentStat;
}
Your query as it stands actually gives you an IEnumerable<DataRow>. You need to do this to get the actual row:
var currentStatRow = (from currentStat in downloadProcStatusTable.AsEnumerable()
where currentStat.Field<String>("ProcedureName") == ProcStats.ProcName
select currentStat).SingleOrDefault();
You should then be able to use the currentStatRow variable to modify the column values.
Outline
Load the existing entity from the database (unless you have one that you can re-attach, in which case you could avoid this additional query)
Update the properties as needed
Submit the changes back to the database using SubmitChanges()
Implementation
I wasn't exactly sure where your variables are and the names, but this should give you a good start...
void UpdateDataDownloadProcedureList(ProcedureStats ProcStats)
{
var currentStatRow = (from currentStat in downloadProcStatusTable.AsEnumerable()
where currentStat.Field<String>("ProcedureName") == ProcStats.ProcName
select currentStat).FirstOrDefault();
currentStatRow.Status = ProcStats.Status;
currentStatRow.StopTime = ProcStats.StopTime;
currentStatRow.Duration = ProcStats.Duration;
downloadProcStatusTable.SubmitChanges();
}

Using Query datatype in C#

I have a piece of code which returns a Sales Order from AX. In that record im using the querySalesLine method but I'm not sure where I go from there to get all the lines attached to the order below is my code:
AxaptaRecord OrderRecord = (AxaptaRecord)ax.CallStaticClassMethod("OnlineOrder", "getSalesOrder", salesRef);
if(OrderRecord.Found)
{
AxaptaObject Lines = (AxaptaObject)OrderRecord.Call("querySalesLine");
}
How would I then use this Lines object to retrieve all of the items attached to this order? I know that the querySalesLine returns a Query object but not sure what to do next.
You should create a QueryRun object, then use that object to read the lines.
var qLines = (AxaptaObject)OrderRecord.Call("querySalesLine");
var qrLines = ax.CreateAxaptaObject("QueryRun", qLines);
To read the lines use this answer.
The Query is a static description of the query.
The QueryRun uses the query to find the records.

Retrieve an object from entityframework without ONE field

I'm using entity framework to connect with the database. I've one little problem:
I've one table which have one varbinary(MAX) column(with filestream).
I'm using SQL request to manage the "Data" part, but EF for the rest(metadata of the file).
I've one code which has to get all files id, filename, guid, modification date, ... of a file. This doesn't need at all the "Data" field.
Is there a way to retrieve a List but without this column filled?
Something like
context.Files.Where(f=>f.xyz).Exclude(f=>f.Data).ToList();
??
I know I can create anonymous objects, but I need to transmit the result to a method, so no anonymous methods. And I don't want to put this in a list of anonymous type, and then create a list of my non-anonymous type(File).
The goal is to avoid this:
using(RsSolutionsEntities context = new RsSolutionsEntities())
{
var file = context.Files
.Where(f => f.Id == idFile)
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
return new File() {
DataType = file.DataType, DateModification = file.DateModification,
FileId = file.FileId, FileName = file.FileName, Id = file.Id,
MimeType = file.MimeType, Size = file.Size
};
}
(I'm using here the anonymous type because otherwise you will get a NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query.)
(e.g. this code throw the previous exception:
File file2 = context.Files.Where(f => f.Id == idFile)
.Select(f => new File() {Id = f.Id, DataType = f.DataType}).FirstOrDefault();
and "File" is the type I get with a context.Files.ToList(). This is the good class:
using File = MyProjectNamespace.Common.Data.DataModel.File;
File is a known class of my EF datacontext:
public ObjectSet<File> Files
{
get { return _files ?? (_files = CreateObjectSet<File>("Files")); }
}
private ObjectSet<File> _files;
Is there a way to retrieve a List but without this column filled?
Not without projection which you want to avoid. If the column is mapped it is natural part of your entity. Entity without this column is not complete - it is different data set = projection.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
As exception says you cannot project to mapped entity. I mentioned reason above - projection make different data set and EF don't like "partial entities".
Error 16 Error 3023: Problem in mapping fragments starting at line
2717:Column Files.Data in table Files must be mapped: It has no
default value and is not nullable.
It is not enough to delete property from designer. You must open EDMX as XML and delete column from SSDL as well which will make your model very fragile (each update from database will put your column back). If you don't want to map the column you should use database view without the column and map the view instead of the table but you will not be able to insert data.
As a workaround to all your problems use table splitting and separate the problematic binary column to another entity with 1 : 1 relation to your main File entity.
I'd do something like this:
var result = from thing in dbContext.Things
select new Thing {
PropertyA = thing.PropertyA,
Another = thing.Another
// and so on, skipping the VarBinary(MAX) property
};
Where Thing is your entity that EF knows how to materialize. The resulting SQL statement shouldn't include the large column in its result set, since it's not needed in the query.
EDIT: From your edits, you get the error NotSupportedException: The entity or complex type 'ProjectName.File' cannot be constructed in a LINQ to Entities query. because you haven't mapped that class as an entity. You can't include objects in LINQ to Entities queries that EF doesn't know about and expect it to generate appropriate SQL statements.
You can map another type that excludes the VarBinary(MAX) column in its definition or use the code above.
you can do this:
var files = dbContext.Database.SqlQuery<File>("select FileId, DataType, MimeType from Files");
or this:
var files = objectContext.ExecuteStoreQuery<File>("select FileId, DataType, MimeType from Files");
depending on your version of EF
I had this requirement because I have a Document entity which has a Content field with the content of the file, i.e. some 100MB in size, and I have a Search function that I wanted to return the rest of the columns.
I chose to use projection:
IQueryable<Document> results = dbContext.Documents.Include(o => o.UploadedBy).Select(o => new {
Content = (string)null,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
// etc. even with related entities here like:
UploadedBy = o.UploadedBy
});
Then my WebApi controller passes this results object to a common Pagination function, which applies a .Skip, .Take and a .ToList.
This means that when the query gets executed, it doesn't access the Content column, so the 100MB data is not being touched, and the query is as fast as you'd want/expect it to be.
Next, I cast it back to my DTO class, which in this case is pretty much exactly the same as the entity class, so this might not be a step you need to implement, but it's follows my typical WebApi coding pattern, so:
var dtos = paginated.Select(o => new DocumentDTO
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
FileName = o.FileName,
Id = o.Id,
UploadedBy = o.UploadedBy == null ? null : ModelFactory.Create(o.UploadedBy)
});
Then I return the DTO list:
return Ok(dtos);
So it uses projection, which might not fit the original poster's requirements, but if you're using DTO classes, you're converting anyway. You could just as easily do the following to return them as your actual entities:
var dtos = paginated.Select(o => new Document
{
Content = o.Content,
ContentType = o.ContentType,
DocumentTypeId = o.DocumentTypeId,
//...
Just a few extra steps but this is working nicely for me.
For EF Core 2
I implemented a solution like this:
var files = context.Files.AsNoTracking()
.IgnoreProperty(f => f.Report)
.ToList();
The base idea is to turn for example this query:
SELECT [f].[Id], [f].[Report], [f].[CreationDate]
FROM [File] AS [f]
into this:
SELECT [f].[Id], '' as [Report], [f].[CreationDate]
FROM [File] AS [f]
you can see the full source code in here:
https://github.com/aspnet/EntityFrameworkCore/issues/1387#issuecomment-495630292
I'd like to share my attempts to workaround this problem in case somebody else is in the same situation.
I started with what Jeremy Danyow suggested, which to me is the less painful option.
// You need to include all fields in the query, just make null the ones you don't want.
var results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName");
In my case, I needed a IQueryable<> result object so I added AsQueryable() at the end. This of course let me add calls to .Where, .Take, and the other commands we all know, and they worked fine. But there's a caveat:
The normal code (basically context.myEntity.AsQueryable()) returned a System.Data.Entity.DbSet<Data.DataModel.myEntity>, while this approach returned System.Linq.EnumerableQuery<Data.DataModel.myEntity>.
Apparently this means that my custom query gets executed "as is" as soon as needed and the filtering I added later is done afterwards and not in the database.
Therefore I tried to mimic Entity Framework's object by using the exact query EF creates, even with those [Extent1] aliases, but it didn't work. When analyzing the resulting object, its query ended like
FROM [dbo].[TableName] AS [Extent1].Where(c => ...
instead of the expected
FROM [dbo].[TableName] AS [Extent1] WHERE ([Extent1]...
Anyway, this works, and as long as the table is not huge, this method will be fast enough. Otherwise you have no option than to manually add the conditions by concatenating strings, like classic dynamic SQL. A very basic example in case you don't know what I'm talking about:
string query = "SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName";
if (parameterId.HasValue)
query += " WHERE Field1 = " + parameterId.Value.ToString();
var results = context.Database.SqlQuery<myEntity>(query);
In case your method sometimes needs this field, you can add a bool parameter and then do something like this:
IQueryable<myEntity> results;
if (excludeBigData)
results = context.Database.SqlQuery<myEntity>("SELECT Field1, Field2, Field3, HugeField4 = NULL, Field5 FROM TableName").AsQueryable();
else
results = context.myEntity.AsQueryable();
If anyone manages to make the Linq extensions work properly like if it was the original EF object, please comment so I can update the answer.
I'm using here the anonymous type because otherwise you will get a
NotSupportedException: The entity or complex type 'ProjectName.File'
cannot be constructed in a LINQ to Entities query.
var file = context.Files
.Where(f => f.Id == idFile)
.FirstOrDefault() // You need to exeucte the query if you want to reuse the type
.Select(f => new {
f.Id, f.MimeType, f.Size, f.FileName, f.DataType,
f.DateModification, f.FileId
}).FirstOrDefault();
And also its not a bad practice to de-normalize the table into further, i.e one with metadata and one with payload to avoid projection. Projection would work, the only issue is, need to edit any time a new column is added to the table.
I tried this:
From the edmx diagram (EF 6), I clicked the column I wanted to hide from EF and on their properties you can set their getter and setter to private. That way, for me it works.
I return some data which includes a User reference, so I wanted to hide the Password field even though it's encrypted and salted, I just didn't want it on my json, and I didn't want to do a:
Select(col => new {})
because that's a pain to create and maintain, especially for big tables with a lot of relationships.
The downside with this method is that if you ever regenerate your model, you would need to modify their getter and setter again.
Using Entity Framework Power Tools you can do the following in efpt.config.json:
"Tables": [
{
"ExcludedColumns": [
"FileData"
],
"Name": "[dbo].[Attachment]",
"ObjectType": 0
}
]

Categories

Resources