I am using Dapper with a stored procedure.
public List<Sifrarnik> ChangeOpisText(string opis)
{
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Cloud"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
}
var response = db.Query<Sifrarnik>("dbo.spChangeOpisText #opis",
new
{
opis = opis
}).ToList();
return response;
}
}
I pass a single variable to the query and retrieve a single column/row from a database. This result is stored in the response variable.
But I don't know how to access the value and place it in a textbox. Any clues?
textbox1.Text = ?
For instance.. doing it for a combobox would be:
comboBox1.DataSource = response;
comboBox1.DisplayMember = "column-name";
comboBox1.ValueMember = "column-name";
When I check the type of the variable in prints List from the Data model I use for Dapper.
If you plan to retrieve a single row, why are you returning a list? Just return a single instance of Sifrarnik.
You can get the instance from the returned enumerable by using Single().
public Sifrarnik ChangeOpisText(string opis)
{
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Cloud"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
{
db.Open();
}
var response = db.Query<Sifrarnik>
(
"dbo.spChangeOpisText #opis",
new
{
opis = opis
}
)
.Single();
return response;
}
}
Related
I wanted get set of data and append to combobox from database using linq.
I have did it already but when i save data. My db saved value looks like this:
{
schid = 1004,
schoolname = St John Bossco
}
I only wanted to save the string value of the school name. how can i approach that?
This is my code:
private void get_combo_vale_list()
{
using (DBEntity db = new DBEntity())
{
var school = db.basicdata_school.Select(x => new { x.schid, x.schoolname });
cmbalschool.DataSource = school.ToList();
cmbalschool.ValueMember = "schid";
cmbalschool.DisplayMember = "schoolname";
cmbalschool.SelectedItem = null
}
}
I am executing a stored procedure using QueryMultiple to return multiple sets of data.
var gridReader = db.QueryMultiple("sp",
parameters,
commandType: CommandType.StoredProcedure);
I can very easily get each set given I know the order they will come back in.
SELECT * FROM dbo.Set1;
SELECT * FROM dbo.Set2;
SELECT * FROM dbo.Set3;
var set1 = gridReader.Read<Set1>();
var set2 = gridReader.Read<Set2>();
var set3 = gridReader.Read<Set3>();
However, I am in a situation where the order they will come back in may change. Another developer could decide to change the order for whatever reason. The stored procedure now becomes this:
SELECT * FROM dbo.Set1;
SELECT * FROM dbo.Set3;
SELECT * FROM dbo.Set2;
How can I handle this?
My initial attempt was to iterate each grid, checking the column names. This seemed to work well at first, but I wasn't able to figure out how to then project the grid into a class, besides manually setting each field. The main reason I'm using Dapper is so it can do this for me.
while (true)
{
var grid = gridReader.Read();
IDictionary<string, object> row = grid.FirstOrDefault();
if (row == null)
break;
if (row.Keys.Contains("Set1_UniqueColumnName"))
{
// Need something like grid.Read<Set1>();
}
else if (row.Keys.Contains("Set2_UniqueColumnName")) { }
else if (row.Keys.Contains("Set3_UniqueColumnName")) { }
}
My second idea was to read each grid into a class, check the unique fields of the class for nulls/default values, and trying the next class if the test failed. This obviously won't work though. .Read() will return the next grid of results. This solution would require me to be able to read the same grid over and over.
Dapper provides an IDataReader.GetRowParser extension method that enables type switching per row. From the Dapper docs here...
Usually you'll want to treat all rows from a given table as the same
data type. However, there are some circumstances where it's useful to
be able to parse different rows as different data types. This is where
IDataReader.GetRowParser comes in handy.
Imagine you have a database table named "Shapes" with the columns: Id,
Type, and Data, and you want to parse its rows into Circle, Square, or
Triangle objects based on the value of the Type column.
var shapes = new List<IShape>();
using (var reader = connection.ExecuteReader("select * from Shapes"))
{
// Generate a row parser for each type you expect.
// The generic type <IShape> is what the parser will return.
// The argument (typeof(*)) is the concrete type to parse.
var circleParser = reader.GetRowParser<IShape>(typeof(Circle));
var squareParser = reader.GetRowParser<IShape>(typeof(Square));
var triangleParser = reader.GetRowParser<IShape>(typeof(Triangle));
var typeColumnIndex = reader.GetOrdinal("Type");
while (reader.Read())
{
IShape shape;
var type = (ShapeType)reader.GetInt32(typeColumnIndex);
switch (type)
{
case ShapeType.Circle:
shape = circleParser(reader);
break;
case ShapeType.Square:
shape = squareParser(reader);
break;
case ShapeType.Triangle:
shape = triangleParser(reader);
break;
default:
throw new NotImplementedException();
}
shapes.Add(shape);
}
}
You'll need to get access to the IDataReader that the GridReader wraps or change your code to use the good old-fashioned ADO.NET SqlConnection & SqlCommand objects like this...
using (command = new SqlCommand("sp", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(parameters);
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
// read row columns
}
}
}
Davmos's answer pointed me in the right direction. Needed to use a combination of ADO.NET and Dapper. Essentially use ADO.NET to retrieve and iterate through the data, but use Dapper to parse the rows into my objects. Note the use of FieldCount in the while loop in case a result set actually does return 0 rows. We want it to move on to the next result set, not break out of the loop.
Set1 set1 = null;
var set2 = new List<Set2>();
Set3 set3 = null;
using (var command = new SqlCommand("sp", conn))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(parameters);
command.Connection.Open();
using (var reader = command.ExecuteReader())
{
while (reader.FieldCount > 0)
{
var set1Parser = reader.GetRowParser<Set1>();
var set2Parser = reader.GetRowParser<Set2>();
var set3Parser = reader.GetRowParser<Set3>();
var isSet1 = HasColumn(reader, "Set1_UniqueColumnName");
var isSet2 = HasColumn(reader, "Set2_UniqueColumnName");
var isSet3 = HasColumn(reader, "Set3_UniqueColumnName");
while (reader.Read())
{
if (isSet1)
{
set1 = set1Parser(reader);
}
else if (isSet2)
{
set2.Add(set2Parser(reader));
}
else if (isSet3)
{
set3 = set3Parser(reader);
}
}
reader.NextResult();
}
}
}
public static bool HasColumn(IDataReader reader, string columnName)
{
for (var i = 0; i < reader.FieldCount; i++)
{
if (reader.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
my problem is very common, but I have not found any solution.
This is my code:
public async Task<QueryResult> RollbackQuery(ActionLog action)
{
var inputParameters = JsonConvert.DeserializeObject<Parameter[]>(action.Values);
var data = DeserailizeByteArrayToDataSet(action.RollBackData);
using (var structure = PrepareStructure(action.Query, action.Query.DataBase, inputParameters))
{
//_queryPlanner is the implementor for my interface
return await _queryPlanner.RollbackQuery(structure, data);
}
}
I need to load DataTable (from whereever) and replace data to database. This is my Rollback function. This function use a "CommandStructure" where I've incapsulated all SqlClient objects. PrepareStructure initialize all objects
//_dataLayer is an Helper for create System.Data.SqlClient objects
//ex: _dataLayer.CreateCommand(preSelect) => new SqlCommand(preSelect)
private CommandStructure PrepareStructure(string sql, string preSelect, DataBase db, IEnumerable<Parameter> inputParameters)
{
var parameters = inputParameters as IList<Parameter> ?? inputParameters.ToList();
var structure = new CommandStructure(_logger);
structure.Connection = _dataLayer.ConnectToDatabase(db);
structure.SqlCommand = _dataLayer.CreateCommand(sql);
structure.PreSelectCommand = _dataLayer.CreateCommand(preSelect);
structure.QueryParameters = _dataLayer.CreateParemeters(parameters);
structure.WhereParameters = _dataLayer.CreateParemeters(parameters.Where(p => p.IsWhereClause.HasValue && p.IsWhereClause.Value));
structure.CommandBuilder = _dataLayer.CreateCommandBuilder();
structure.DataAdapter = new SqlDataAdapter();
return structure;
}
So, my function uses SqlCommandBuilder and DataAdapter to operate on Database.
PreSelectCommand is like "Select * from Purchase where CustomerId = #id"
The table Purchase has one primaryKey on ID filed
public virtual async Task<QueryResult> RollbackQuery(CommandStructure cmd, DataTable oldData)
{
await cmd.OpenConnectionAsync();
int record = 0;
using (var cmdPre = cmd.PreSelectCommand as SqlCommand)
using (var dataAdapt = new SqlDataAdapter(cmdPre))
using (var cmdBuilder = new SqlCommandBuilder(dataAdapt))
{
dataAdapt.UpdateCommand = cmdBuilder.GetUpdateCommand();
dataAdapt.DeleteCommand = cmdBuilder.GetDeleteCommand();
dataAdapt.InsertCommand = cmdBuilder.GetInsertCommand();
using (var tbl = new DataTable(oldData.TableName))
{
dataAdapt.Fill(tbl);
dataAdapt.FillSchema(tbl, SchemaType.Source);
tbl.Merge(oldData);
foreach (DataRow row in tbl.Rows)
{
row.SetModified();
}
record = dataAdapt.Update(tbl);
}
}
return new QueryResult
{
RecordAffected = record
};
}
I Execute the code and I don't have any errors, but the data are not updated.
variable "record" contain the right number of modified (??) record, but..... on the table nothing
can someone help me?
EDIT 1:
With SQL Profiler I saw that no query is executed on DB. Only select query on .Fill(tbl) command.
EDIT 2:
Now I have made one change:
tbl.Merge(oldData) => tbl.Merge(oldData, true)
so I see perform the expected query but, with reversed parameters.
UPDATE Purchase SET price=123 where id=6 and price=22
instead of
UPDATE Purchase SET price=22 where id=6 and price=123
I'm using the below code to fetch the state. I'm getting the error" Method 'System.String GetState(int32)' has no supported translation to SQL".Please let me know where i'm doing a mistake.
public IQueryable<ViewModel> GetResult()
{
IQueryable<ViewModel> result;
if (isDestinationSite)
{
result = (from table1 in this.db.tblTable1
select new ViewModel
{
State= this.GetState(table1.PersonUID),
});
}
private string GetState(int PersonUID)
{
using ( PersonPref pref = new PersonPref ())
{
pref .selectPref(ApplicationCode.MyApp, PersonPref .preference);
if (pref.PesronValue== "True")
{
return "Successfull";
}
else
{
return "Failure";
}
}
}
SQL doesn't know anything about your function so you just need to move it outside of your linq query.
List<ViewModel> result;
var personUID = (from table1 in this.db.tblTable1 select table1.PersonUID).ToList();
foreach (var id in personUID)
{
result.Add(new ViewModel { State = GetState(id) });
}
You can write iterate your query with AsEnumerable and then do the selection like:
result = (from table1 in this.db.tblTable1
.AsEnumerable()
select new ViewModel
{
State= this.GetState(table1.PersonUID),
});
I have an empty data base.
I want to add multi records into data base.
while inserting record to data base i want to check if my product inserted in same date donot add it again(i want to change some it's filed and update it's content).
i used this code but it just add some data into data base (it can't check for existing product.)
var AllData = ClsDataBase.Database.InsertProductTbls;
foreach(item in AllData)
{
//Update
if (Exist(datefa))
{
var query = ClsDataBase.Database.CustomerProductTbls.SingleOrDefault
(data => data.CustomerId == AllData .CustomerId );
int? LastProductTotal = query.CustomerProducTtotal;
query.CustomerProducTtotal = LastProductTotal + ClsInsertProduct._InsertProductNumber;
}
//Insert
else
{
_CustomerProductTbl = new CustomerProductTbl();
_CustomerProductTbl.CustomerId = AllData ._CustomerId;
_CustomerProductTbl.CustomerProductDateFa = AllData.datefa
.
.
.
ClsDataBase.Database.AddToCustomerProductTbls(_CustomerProductTbl);
}
}
}
ClsDataBase.Database.SaveChanges();
if i use ClsDataBase.Database.SaveChanges(); for both update and insert part i will return this error:
An error occurred while starting a transaction on the provider connection. See the inner exception for details.
please help.
I got the solution by opening database conection for each repeat loop:
foreach(item in AllData)
{
using (StorageEntities context = new StorageEntities())
{
//Update
if (Exist(datefa))
{
var query = ClsDataBase.Database.CustomerProductTbls.SingleOrDefault
(data => data.CustomerId == AllData .CustomerId );
int? LastProductTotal = query.CustomerProducTtotal;
query.CustomerProducTtotal = LastProductTotal + ClsInsertProduct._InsertProductNumber;
}
//Insert
else
{
_CustomerProductTbl = new CustomerProductTbl();
_CustomerProductTbl.CustomerId = AllData ._CustomerId;
_CustomerProductTbl.CustomerProductDateFa = AllData.datefa;
ClsDataBase.Database.AddToCustomerProductTbls(_CustomerProductTbl);
}
ClsDataBase.Database.SaveChanges();
}
}