I have a query regarding generating SQL insert statement using c# classes.
So I have a class called students.
There is a function which gets list of students and dump that in database.
Student Model
public class Student
{
public string ID { get; set; } = ""; // DB column name is studentID
public string Name { get; set; } = ""; // DB column name is studentName
public string address { get; set; } // DB column name is studentAddress
}
Function to dump Data
public async Task<Error> DumpStudentAsync()
{
List<Student> students = new List<Student>();
StringBuilder query = new StringBuilder();
query = query.Append("INSERT INTO Student(studentID,studentName,studentAddress) VALUES");
string columnList = "(#studentID{0},#studentName{0},#studentAddress{0})";
for (int i = 0; i < students.Count; i++)
{
query.AppendFormat($"{columnList},", i);
}
query = query.Replace(',', ';', query.Length - 1, 1);
SqlCommand cmd = new SqlCommand
{
CommandText = query.ToString(),
};
for (int i = 0; i < students.Count; i++)
{
cmd.Parameters.AddWithValue($"#studentID{i}", students[i].ID);
cmd.Parameters.AddWithValue($"#studentName{i}", students[i].Name);
cmd.Parameters.AddWithValue($"#studentAddress{i}", students[i].address);
}
SQLWrapper db = new SQLWrapper(_ctx, "", DSConfig.SQLConnectionKey);
return await db.ExecuteStatementAsync(cmd, "");
}
So here I want to make this function generic in such a way that if I add a new field in my student object there should be no code change done in my function.
I tried searching the answers but I didn't get anything.
Here firstly I'm appending the insert format in query where I have hard-coded.
Secondly I'm appending variables for command parameters on the basis of students count which is also hard-coded.
Also the class properties and database columns names are different how can I make use of class properties as DB column names?
Can I do something like this ?
StringBuilder studentQuery = new StringBuilder();
string columns = "";
// Add column list on the basis of students properties
foreach (var property in Student.Properties)
{
columns += "property.ID,"; // It should be studentID
}
// Add variables on the basis of students count
// Add command parameters value on the basis of students count
FYI: I'm using ADO.NET code to perform DB activities not Entity framework.
This kind of automatic is not easily done. .NET is strongly typed. Unless you go into stuff like reflection or dynamic code to do it. And I would not advise it. Strong Typisation is your friend. Without it you end up in the JavaScript and PHP examples for this comic. Hint: JS does the wrong thing in both cases.
For me at least, having to do some minor changes on the frontend for changes on the Database is acceptable work. If anything that is the smalest, least dangerous part of the whole process. So I can only advise against trying this.
However for databases and only databases, stuff like Entity Framework might be a good idea. It can generate your classes from the Database.
Related
Sometimes, we would like to change order details by adding, removing, and editing orders by customer's request or depends on stock quantity.
So now want get some list and update including remove, edit, add rows, then save on database
What's the best efficiently way as C#, EntityFramework?
public class OrderDetail
{
public int Id { get; set; }
public int OrderId {get; set; }
public int Qty{ get; set; }
public string ItemName { get; set; }
}
/// Dummy db, OrderDetail Table
{
{1, 1000, 24,"A"},
{2, 1000, 12,"B"}
}
public void Update()
{
using(var db = new xxEntities())
{
// Get All orders, OrderId==1000, total 2rows
List<OrderDetails> list = db.OrderDetails.Where(x=> x.OrderId==1000).ToList();
// remove some row or rows
var temp1 = list.First(x=> x.Id==1);
list.Remove(temp);
// edit some row or rows
var temp2 = list.First(x=> x.Id==2);
temp2.Qty=100;
// add some row or rows
list.Add(new OrderDetail{ Id=3, OrderId=1000, Qty=2, ItemName="C"});
list.Add(new OrderDetail{ Id=4, OrderId=1000, Qty=2, ItemName="D"});
// Apply all changes
db.SaveChanges();
}
}
Additional Question
public void UpdateOrder(int orderId, List<OrderDetail> newOrders)
{
var result = db.OrderDetails.Where(x=>x.OrderId==orderId).ToList();
result = newOrders;
// it does not work
//db.OrderDetails.Update(result);
db.OrderDetails.RemoveRange(result);
db.OrderDetails.AddRange(newOrders);
db.SaveChange();
}
is it right approach to update multiple rows?
As mentioned in another answer... EF will create individual statements for each of the changes that are detected (i.e., updates, inserts, deletes) and submit them inside a single transaction. Gets the job done but is potentially very "chatty". Benefit is that you don't need to worry about the details of how it's getting done. Pretty easy to just modify the data object and call SaveChanges.
If you can consider not using EF for updates such as this... one way we do this kind of update is by creating a System.Data.DataTable and using that as a table-valued parameter into a stored procedure (if your datastore supports it).
Meta-code:
var dt = new DataTable();
var newRow = dt.NewRow();
newRow["column1"] = newdata;
dt.Rows.Add(newRow);
Then just use dt as your input parameter and let the stored proc determine the insert/update/delete operations.
If you want to Add / Remove / Update rows from your tables in Entity Framework, you have to Add / Remove / Update the items in your DbSet, not in fetched data.
using (var dbContext = new OrderContext())
{
// Add one Order
Order orderToAdd = new Order
{
// fill required properties; don't fill primary key
}
var addedOrder = dbContext.Orders.Add(orderToAdd);
// note: addedOrder has no Id yet.
// Add several Orders
IEnumerable<Order> orders = ...
dbContext.Orders.AddRange(orders);
dbContext.SaveChanges();
// now they've got their id:
Debug.Assert(addedOrder.Id != 0);
Debug.Assert(orders.All(order => order.Id != 0);
}
To Remove, you'll first have to fetch the complete Order
int orderIdToDelete = ...
using (var dbContext = new OrderContext())
{
Order orderToDelete = dbContext.Orders.Find(orderIdToDelete);
dbContext.Orders.Remove(orderToDelete);
var ordersToDelete = dbContext.Orders
.Where(order => order.Date.Year < 2000)
.ToList();
dbContext.Orders.RemoveRange(ordersToDelete);
// the orders are not deleted yet.
dbContext.SaveChanges();
}
To Update, you first have to get the value:
int orderIdToUpdate = ...
Order orderToUpdate = dbContext.Orders.Find(orderIdToUpdate);
orderToUpdate.Date = DateTime.Today;
var today = Datetime.Today;
var dateLimit = today.AddDays(-28);
var nonPaidOrders = dbContext.Orders
.Where(order => !order.Paid && order.Date < dateLimit)
.ToList();
foreach (var order in nonPaidOrders)
{
this.SendReminder(order);
order.ReminderDate = today;
}
dbContext.SaveChanges();
There is no "most efficient" way outside of making all changes then calling SaveChanges. upon which Ef will issue a lot of SQL Statements (one per operation).
There is most efficient way because there is no way to change the way Ef works and there is exactly one way Ef does its updates. They do NOT happen at the same time. Period. They happen in one transaction, one after the other, when you call SaveChanges.
using System;
using System.Collections.Generic;
using ConsoleApp3;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Customers customers = new Customers();
List<Customers> names = new List<Customers>()
{
new Customers {Names = "Tanveer"},
new Customers {Names = "Nabila"},
new Customers {Names = "Suraj"}
};
foreach (int i = 0; i < names.Count; i++)
{
Console.WriteLine(customers.Names.Length);
}
}
class Customers
{
public string Names { get; set; }
}
}
}
I want to create the list of Customers and write it on the Console. But the Customers.Names is null. I am new to programming so please help.Thanks
First, let us fix up that naming. Naming is very important, but also one of the more annoying parts.
//Singular for the class
class Customer
{
//Also Singular, as this can only take 1 name
public string Name { get; set; }
}
//Plural, because it is a collection of Customer Instances.
List<Customer> Customers = new List<Customer>()
{
new Customer {Name = "Tanveer"},
new Customer {Name = "Nabila"},
new Customer {Name = "Suraj"}
};
Then you itterate over it using:
//Use a proper foreach, no need to deal with Indexes here
foreach (Customer current in Customers){
Console.WriteLine(current.Name);
}
If you do want to have a running counter, this is how the loop would look:
for(int i = 0; i < Customers.Count;i++){
//I you get any Exceptions, you want to split it up over 2 lines using a temporary variable
Console.WriteLine(Customers[i].Name);
}
What you had in your code was a bastardisation of for and foreach syntax, wich I doubt compiled.
Console.WriteLine(names[i].Names);
customers.Names is null because customers is an object which you haven't populated with any data, and has no obvious purpose. names is the actual list of customers with useful info in it.
names does not have a Names property directly either, though. The objects within the list do. So you need to refer to a specific object within the specific list.
And since you're in a loop for doing just that, names[i].Names.Length is no doubt what you intended.
N.B. However it needs to be a for rather than foreach in the loop definition - the syntax used with foreach is different. This shouldn't have compiled in order to allow you to even see the null output, so perhaps this is just a typo in your posted code.
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine(names[i].Names.Length);
}
should be closer to what you need (although I'm not convinced you really are intending to print the length of each name...but that's up to you).
P.S. You should probably amend your naming convention so you've got Customer as the type and Name as the property. It's much more readable and comprehensible if they're singular rather than plural.
I have a console application connected to a SQL Server database with several tables and views. To get the entire table I so something like:
myAppDataset dsTemp = new myAppDataset();
myAppDataset.AppLogDataTable dtLog = dsTemp.AppLog;
myAppDataset.AppUserDataTable dtUser = dsTemp.AppUser;
Then when I need to filter I create a DataView:
DataView dvLog = dtLog.DefaultView;
dvLog.RowFilter = "DeptID = 1";
DataView dvUser = dtUser.DefaultView;
dvUser.RowFilter = "DeptID = 1";
That all works fine.
What I'd like to know is if there is a way modify the DataSet (or something else) so that I don't need to create the DataViews? In other words, I want every AppLogDataTable, AppUserDataTable, etc that gets created to be filtered for DeptID = 1. Essentially what I want to achieve is to be able to pass a parameter to my data class constructor that will automatically filter all of the data tables so that when they are used I don't have to worry about creating a DataView and filtering the table every time (which also necessitates the passing of the original filtering parameters).
I tried creating a DataView and overwriting the original object, but got an error that the DataTable couldn't be casted or something to that effect:
myAppDataset dsTemp = new myAppDataset();
myAppDataset.AppLogDataTable dtLog = dsTemp.AppLog;
DataView dvLog = dtLog.DefaultView;
dvLog.RowFilter = "DeptID = 1";
dtLog = (myAppDataset.AppLogDataTable)dvLog.ToTable();
Any help is greatly appreciated.
some possible suggestions:
use Linq2Object, and not DataView at all
var filterStep1 = dtUser.Where(x => x.DeptID == 1);
var filterStep2 = filterStep1.Where(x => x.XYZ < 40);
Console.WriteLine(filterStep2);
is equivalent to:
var filter = dtUser.Where(x => x.DeptID == 1 && x => x.XYZ < 40);
Console.WriteLine(filter);
edit the sql query
you can filter in the sql query.
in TypedDataSet case, double-click on myAppDataset in the solution-explorer, click on the Fill, GetData() that appears under the table-box.
in property window (F4), click double-click on Select Command property. the query-designer shown. add the filter to query (if you not want write the 1 but parameter, type ? in the criteria - it create parameter for command automaticaly).
use List for criteria
another solution is create a list of criteria, and join them to RowFilter:
var criteria = new List<string>();
criteria.Add("DeptID = 1");
criteria.Add("XYZ < 40");
dvUser.RowFilter = string.Join(" AND ", criteria);
You really shouldn't be reading data from the database if you don't intend to use it. Filter your SQL query.
select somecolumns from sometable where DeptID = 1
However, let's pretend for a moment that you're reading all the data into memory for caching purposes or something like this. Don't put that into a DataSet, DataTable, or DataView. Those are outdated constructs. They're slow, inefficient, and give you none of the benefits of binding to a strongly typed object.
Instead, create a type to represent your data. Since you don't give much context, I'm going to pretend you're dealing with Students that have a many to one relationship with Departments.
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; }
}
Now you have some choices. You can use ADO.NET to get the data.
public class StudentSqlServerRepository
{
private readonly string _connectionString;
public StudentSqlServerRepository(string connectionString)
{
_connectionString = connectionString;
}
public List<Student> GetStudentsByDepartmentId(int departmentId)
{
var students = new List<Student>();
using(var connection = new SqlConnection(_connectionString))
using(var command = new SqlCommand("select Id, Name, DepartmentId from students where DepartmentId = #DepartmentId", connection))
{
command.Parameters.Add(new SqlParameter("DepartmentId", SqlDbType.Int).Value = departmentId);
connection.Open();
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
var student = new Student();
student.Id = (int)reader["Id"];
student.Name = (string)reader["Name"];
student.DepartmentId = (int)reader["DepartmentId"];
students.Add(student);
}
}
}
return students;
}
}
But that's a lot of yucky code. Fortunately, Dapper, a micro ORM, can make this a lot cleaner.
public class StudentSqlServerRepository
{
private readonly string _connectionString;
public StudentSqlServerRepository(string connectionString)
{
_connectionString = connectionString;
}
public List<Student> GetStudentsByDepartmentId(int departmentId)
{
using(var connection = new SqlConnection(_connectionString))
{
var students = connection.Query<Student>("select Id, Name, DepartmentId from students where DepartmentId = #DepartmentId", new { DepartmentId = departmentId}).AsList();
return students;
}
}
}
Now getting your students is as easy as
var studentRepository = new StudentSqlServerRepository(ConfigurationManager.ConnectionStrings["StudentDatabase"].ConnectionString);
var students = studentRepository.GetStudentsByDepartmentId(1);
//let's pretend this is Web Forms and we want to bind to a control
StudentsGridView.DataSource = students;
StudentsGridView.DataBind();
//let's pretend it's MVC and we want to return a View
return View(students);
Compare the memory usage, amount of network traffic, query execution time, and overall ease of this approach as opposed to using a DataTable and filtering in memory.
My team have read-access to a database maintained by a different team. We have a number of scripts that only we run, and so they've never been added as sprocs to the database (nor do we want them to be).
In Entity Framework 6, is it possible to include a file in my model which contains a stored procedure, so that we can leverage the code generation in EF?
We'd much rather have our code look like:
using (var db = new DatabaseEntities())
{
var properlyTypedResult = db.GetEntriesThatStartWith(firstName);
}
than:
using (var db = new DatabaseEntities())
{
var rawResult = db.Database.ExecuteSqlCommand("SELECT * FROM dbo.Person WHERE FirstName LIKE '#p0%'", firstName);
var properlyTypedResult = CastAppropriately(rawResult);
}
The functionality appears to be missing, but I thought I'd check regardless, I'd expect it to be in the designer view,
right-click, Add New -> Function Import...
... but this only allows adding sprocs to the model that are already in the database.
I think you're forgetting about LINQ - the second example would be something like...
List<People> people = (List<People>)db.Person.Where(f => f.FirstName.StartsWith(firstname)).ToList();
This should be close to what you're looking for. Linq is your friend.
I couldn't find exactly what I was after. I decided to simply write my own code generation, and leverage as much of Entity Framework as I could.
With query string in hand, execute against the database appropriately, using a SqlDataAdapter, with a DataTable
e.g.,
using (var context = new DbContext())
{
var dataTable = new DataTable();
var connection = (SqlConnection)context.Database.Connection;
if (connection != null && connection.State == ConnectionState.Closed)
connection.Open();
using (var adapter = new SqlDataAdapter(queryString, connection))
adapter.Fill(dataTable);
}
The DataTable contains the resulting column names along with all their types, now all we have to do is generate the code for the object.
i.e.,
var objectBuilder = new StringBuilder();
objectBuilder.AppendLine("public class QueryResult");
objectBuilder.AppendLine("{");
foreach (DataColumn column in dataTable.Columns)
{
objectBuilder.AppendLine(String.Format("public {0} {1} { get; set; }", column.DataType.Name, column.ColumnName));
}
objectBuilder.AppendLine("}");
Finally, create an extension method on the context object:
i.e.,
private static string GetQueryString(string firstName)
{
return String.Format($"SELECT * FROM dbo.Person WHERE FirstName LIKE '{firstName}%'", firstName);
}
public static partial class DbContextExtensions
{
public static List<QueryResult> GetEntriesThatStartWith(this DbContext context, string firstName)
{
return context.Database.SqlQuery<QueryResult>(GetQueryString(firstName)).ToList();
}
}
Now, we can use this as a regular sproc call:
using (var db = new DbContext())
{
var properlyTypedResult = db.GetEntriesThatStartWith(firstName);
}
We are using an extractor application that will export data from the database to csv files. Based on some condition variable it extracts data from different tables, and for some conditions we have to use UNION ALL as the data has to be extracted from more than one table. So to satisfy the UNION ALL condition we are using nulls to match the number of columns.
Right now all the queries in the system are pre-built based on the condition variable. The problem is whenever there is change in the table projection (i.e new column added, existing column modified, column dropped) we have to manually change the code in the application.
Can you please give some suggestions how to extract the column names dynamically so that any changes in the table structure do not require change in the code?
My concern is the condition that decides which table to query. The variable condition is
like
if the condition is A, then load from TableX
if the condition is B then load from TableA and TableY.
We must know from which table we need to get data. Once we know the table it is straightforward to query the column names from the data dictionary. But there is one more condition, which is that some columns need to be excluded, and these columns are different for each table.
I am trying to solve the problem only for dynamically generating the list columns. But my manager told me to make solution on the conceptual level rather than just fixing. This is a very big system with providers and consumers constantly loading and consuming data. So he wanted solution that can be general.
So what is the best way for storing condition, tablename, excluded columns? One way is storing in database. Are there any other ways? If yes what is the best? As I have to give at least a couple of ideas before finalizing.
Thanks,
A simple query like this helps you to know each column name of a table in Oracle.
Select COLUMN_NAME from user_tab_columns where table_name='EMP'
Use it in your code :)
Ok, MNC, try this for size (paste it into a new console app):
using System;
using System.Collections.Generic;
using System.Linq;
using Test.Api;
using Test.Api.Classes;
using Test.Api.Interfaces;
using Test.Api.Models;
namespace Test.Api.Interfaces
{
public interface ITable
{
int Id { get; set; }
string Name { get; set; }
}
}
namespace Test.Api.Models
{
public class MemberTable : ITable
{
public int Id { get; set; }
public string Name { get; set; }
}
public class TableWithRelations
{
public MemberTable Member { get; set; }
// list to contain partnered tables
public IList<ITable> Partner { get; set; }
public TableWithRelations()
{
Member = new MemberTable();
Partner = new List<ITable>();
}
}
}
namespace Test.Api.Classes
{
public class MyClass
{
private readonly IList<TableWithRelations> _tables;
public MyClass()
{
// tableA stuff
var tableA = new TableWithRelations { Member = { Id = 1, Name = "A" } };
var relatedclasses = new List<ITable>
{
new MemberTable
{
Id = 2,
Name = "B"
}
};
tableA.Partner = relatedclasses;
// tableB stuff
var tableB = new TableWithRelations { Member = { Id = 2, Name = "B" } };
relatedclasses = new List<ITable>
{
new MemberTable
{
Id = 3,
Name = "C"
}
};
tableB.Partner = relatedclasses;
// tableC stuff
var tableC = new TableWithRelations { Member = { Id = 3, Name = "C" } };
relatedclasses = new List<ITable>
{
new MemberTable
{
Id = 2,
Name = "D"
}
};
tableC.Partner = relatedclasses;
// tableD stuff
var tableD = new TableWithRelations { Member = { Id = 3, Name = "D" } };
relatedclasses = new List<ITable>
{
new MemberTable
{
Id = 1,
Name = "A"
},
new MemberTable
{
Id = 2,
Name = "B"
},
};
tableD.Partner = relatedclasses;
// add tables to the base tables collection
_tables = new List<TableWithRelations> { tableA, tableB, tableC, tableD };
}
public IList<ITable> Compare(int tableId, string tableName)
{
return _tables.Where(table => table.Member.Id == tableId
&& table.Member.Name == tableName)
.SelectMany(table => table.Partner).ToList();
}
}
}
namespace Test.Api
{
public class TestClass
{
private readonly MyClass _myclass;
private readonly IList<ITable> _relatedMembers;
public IList<ITable> RelatedMembers
{
get { return _relatedMembers; }
}
public TestClass(int id, string name)
{
this._myclass = new MyClass();
// the Compare method would take your two paramters and return
// a mathcing set of related tables that formed the related tables
_relatedMembers = _myclass.Compare(id, name);
// now do something wityh the resulting list
}
}
}
class Program
{
static void Main(string[] args)
{
// change these values to suit, along with rules in MyClass
var id = 3;
var name = "D";
var testClass = new TestClass(id, name);
Console.Write(string.Format("For Table{0} on Id{1}\r\n", name, id));
Console.Write("----------------------\r\n");
foreach (var relatedTable in testClass.RelatedMembers)
{
Console.Write(string.Format("Related Table{0} on Id{1}\r\n",
relatedTable.Name, relatedTable.Id));
}
Console.Read();
}
}
I'll get back in a bit to see if it fits or not.
So what you are really after is designing a rule engine for building dynamic queries. This is no small undertaking. The requirements you have provided are:
Store rules (what you call a "condition variable")
Each rule selects from one or more tables
Additionally some rules specify columns to be excluded from a table
Rules which select from multiple tables are satisfied with the UNION ALL operator; tables whose projections do not match must be brought into alignment with null columns.
Some possible requirements you don't mention:
Format masking e.g. including or excluding the time element of DATE columns
Changing the order of columns in the query's projection
The previous requirement is particularly significant when it comes to the multi-table rules, because the projections of the tables need to match by datatype as well as number of columns.
Following on from that, the padding NULL columns may not necessarily be tacked on to the end of the projection e.g. a three column table may be mapped to a four column table as col1, col2, null, col3.
Some multi-table queries may need to be satisfied by joins rather than set operations.
Rules for adding WHERE clauses.
A mechanism for defining default sets of excluded columns (i.e. which are applied every time a table is queried) .
I would store these rules in database tables. Because they are data and storing data is what databases are for. (Unless you already have a rules engine to hand.)
Taking the first set of requirements you need three tables:
RULES
-----
RuleID
Description
primary key (RuleID)
RULE_TABLES
-----------
RuleID
Table_Name
Table_Query_Order
All_Columns_YN
No_of_padding_cols
primary key (RuleID, Table_Name)
RULE_EXCLUDED_COLUMNS
---------------------
RuleID
Table_Name
Column_Name
primary key (RuleID, Table_Name, Column_Name)
I've used compound primary keys just because it's easier to work with them in this context e.g. running impact analyses; I wouldn't recommend it for regular applications.
I think all of these are self-explanatory except the additional columns on RULE_TABLES.
Table_Query_Order specifies the order in which the tables appear in UNION ALL queries; this matters only if you want to use the column_names of the leading table as headings in the CSV file.
All_Columns_YN indicates whether the query can be written as SELECT * or whether you need to query the column names from the data dictionary and the RULE_EXCLUDED_COLUMNS table.
No_of_padding_cols is a simplistic implementation for matching projections in those UNION ALL columns, by specifying how many NULLs to add to the end of the column list.
I'm not going to tackle those requirements you didn't specify because I don't know whether you care about them. The basic thing is, what your boss is asking for is an application in its own right. Remember that as well as an application for generating queries you're going to need an interface for maintaining the rules.
MNC,
How about creating a dictionary of all the known tables involved in the application process up front (irrespective of the combinations - just a dictionary of the tables) which is keyed on tablename. the members of this dictionary would be a IList<string> of the column names. This would allow you to compare two tables on both the number of columns present dicTable[myVarTableName].Count as well as iterating round the dicTable[myVarTableName].value to pull out the column names.
At the end of the piece, you could do a little linq function to determine the table with the greatest number of columns and create the structure with nulls accordingly.
Hope this gives food for thought..