Entity Framework raw sql query not setting values - c#

I have a simple query that I'm trying to execute:
sql = "SELECT DailyGamingID, GamingDate, LocationID FROM tblDailyGaming WHERE GamingDate >= '1/1/23' AND LocationID = 1";
List<TestData> testList = context.Database.SqlQuery<TestData> (sql).ToList ();
The query returns 10 rows.
I also have the following class:
public class TestData
{
public int DailyGamingID;
public DateTime GamingDate;
public int LocationID;
}
testList will have the correct number of elements (10) in the list but none of the values of TestData have been set. They are all default values. Help please!

You need to make those properties - not fields - in your class:
public class TestData
{
public int DailyGamingID { get; set; };
public DateTime GamingDate { get; set; };
public int LocationID { get; set; };
}

Related

Database reading to list<T> for display returning null values for some columns that already have values (asp.net mvc)

I have created a database for saving movie details using an ASP.NET MVC web application. I can add data to the database. Inside the database it is showing values in all columns. But when I retrieve data from the table using this statement
return cnn.Query<T>(sql).ToList();
the retrieved list contains null values for 6 columns (C_Category, FilmName, Filmid, D_Cost, P_Cost) [Poster is also null because I haven't added the code for converting the file upload to bytes. That is to be added] But these 6 columns have their respective values in the database.
Is there any way to correct my code to read from database properly? Can you please suggest anything?
I tried adding a breakpoint at the above return statement and viewed the list, which showed up the error values in it before it is passed to further stages. The project builds without any errors and runs but displays these nulls.
The following is the SQL Server data access class file's code
public static List<T> LoadData<T>(string sql)
{
using (IDbConnection cnn = new SqlConnection(GetConnectionSring()))
{
return cnn.Query<T>(sql).ToList();
}
}
This is my business logic class:
public static List<FilmAddModel> LoadFilm()
{
string sql = #"select * from dbo.Film;";
return SqlDataAccess.LoadData<FilmAddModel>(sql);
}
And this is my model for dB display and write
public class FilmAddModel
{
public int Filmid { get; set; }
public string FilmName { get; set; }
public string Actor { get; set; }
public string Actress { get; set; }
public string Pub_Date { get; set; }
public string Director { get; set; }
public string Producer { get; set; }
public decimal P_Cost { get; set; }
public decimal D_Cost { get; set; }
public string Category { get; set; }
public string C_Category { get; set; }
public byte[] Poster { get; set; }
}
Here's my home controller code to use the above for displaying the database contents:
public ActionResult ViewFilm()
{
ViewBag.Message = "Movies List";
var data = LoadFilm();
List<FilmAddModel> movies = new List<FilmAddModel>();
foreach (var row in data)
{
movies.Add(new FilmAddModel
{
Filmid = row.Filmid,
FilmName = row.FilmName,
Actor = row.Actor,
Actress = row.Actress,
Pub_Date = row.Pub_Date,
Director = row.Director,
Producer = row.Producer,
P_Cost = row.P_Cost,
D_Cost = row.D_Cost,
Category = row.Category,
C_Category = row.C_Category,
Poster = row.Poster
});
}
return View(movies);
}
Following is my database structure
CREATE TABLE [dbo].[Film]
(
[Film_id] INT IDENTITY (1, 1) NOT NULL,
[film_name] VARCHAR(50) NULL,
[actor] VARCHAR(50) NULL,
[actress] VARCHAR(50) NULL,
[pub_date] VARCHAR(50) NULL,
[director] VARCHAR(50) NULL,
[producer] VARCHAR(50) NULL,
[prod_cost] DECIMAL(18) NULL,
[dist_cost] DECIMAL(18) NULL,
[category] VARCHAR(30) NULL,
[cert_category] VARCHAR(20) NULL,
[poster] VARBINARY(MAX) NULL,
PRIMARY KEY CLUSTERED ([Film_id] ASC)
);
The names of your SQL Server table columns and your C# model class don't match - you have e.g. film_name in the table, and FilmName in the C# model class - same goes for cert_category in SQL Server, and C_Category in C#.
If you have such differences in name, you need to decorate your C# model class with data annotations - something like:
public class FilmAddModel
{
[Column("film_id")]
public int Filmid { get; set; }
[Column("film_name")]
public string FilmName { get; set; }
public string Actor { get; set; }
public string Actress { get; set; }
public string Pub_Date { get; set; }
public string Director { get; set; }
public string Producer { get; set; }
[Column("prod_cost")]
public decimal P_Cost { get; set; }
[Column("dist_cost")]
public decimal D_Cost { get; set; }
public string Category { get; set; }
[Column("cert_category")]
public string C_Category { get; set; }
public byte[] Poster { get; set; }
}
With these data annotations, the mapping should be done properly between the values in your SQL Server table, and the C# model class.
As a side note: the LoadFilm method returns a List<FilmAddModel> - so why are you creating a second List<FilmAddModel> in your ViewFilm action method and iterate over the list return to add the same type of data into the result list?? You could just simply return the data returned from LoadFilm - it's already in the "right" shape ....
When you converting data to model inside foreach loop you didn’t use the correct name for those field to retrive from database.
Use
C_Category = row.cert_category
Filmid = row.Film_id
etc.
that means according to fieldname inside your data object.

Is this a good way to set more properties value after query?

This is what I do to set more properties value after query. Please see the method GetMoreData(), it will be executed for every detail loop and starts causing performance issue.
Is there a better way of doing this? I know the class constuctor that execute during object instantiate. But in this case, more properites must be set after database query.
Note: This is a cut-down version of source code focusing on important part only, please accept typo error, if any.
public class OrderHeader
{
// Real database column
public string OrderId { get; set; }
public string ColA { get; set; }
public string ColB { get; set; }
public string PostCode { get; set; }
public decimal TotalWeight { get; set; }
public decimal TotalShipmentFee { get; set; }
}
public class OrderDetail
{
// Real database column
public string OrderId { get; set; }
public string OrderLine { get; set; }
public decimal Weight { get; set; }
public decimal ShipmentFee { get; set; }
// mock-up column for displaly only, not exist in datababse
public string ColA { get; set; }
public string ColB { get; set; }
}
public static List<OrderDetail> GetByItemClass(string itemClass)
{
using (IDbConnection db = new MySqlConnection(connectionStringGoesHere)) {
string sqlCmd = #"SELECT * FROM orderdetail WHERE ItemClass = #ItemClass";
List<OrderDetail> orderDetails = db.Query<OrderDetail>(sqlCmd, new {
#ItemClass = itemClass
}).ToList();
// Get or build or calculate additional properties
for (int i = 0; i < orderDetails.Count; i++) {
orderDetails[i] = GetMoreData(orderDetails);
}
return orderDetails;
}
}
public static List<OrderDetail> GetByItemType(string itemType)
{
using (IDbConnection db = new MySqlConnection(connectionStringGoesHere)) {
string sqlCmd = #"SELECT * FROM orderdetail WHERE ItemType = #ItemType";
List<OrderDetail> orderDetails = db.Query<OrderDetail>(sqlCmd, new {
#ItemType = itemType
}).ToList();
// Get or build or calculate additional properties
for (int i = 0; i < orderDetails.Count; i++) {
orderDetails[i] = GetMoreData(orderDetails);
}
return orderDetails;
}
}
public static OrderDetail GetMoreData(OrderDetail orderDetail)
{
// Performance problem: Need to query order header for every
// single loop even multiple records having the same OrderId
OrderHeader orderHeader = OrderHeaderDal.GetById(orderDetail.OrderId);
// Directly map value
orderDetail.ColA = orderHeader.ColA;
orderDetail.ColB = orderHeader.ColB;
// Calculate value
if (orderHeader.PostCode == "0") {
orderDetail.ShipmentFee = orderDetail.Weight * 1.15;
// More complex to get value from another table
} else {
// Might also cause performance issue for many query loop
Rate rate = RateDal.GetByPostCode(orderHeader.PostCode);
orderDetail.ShipmentFee = orderDetail.Weight * rate.RatePerKg;
}
return orderDetail;
}

How can I combine information in two different classes to show the result in a ListBox?

My program contains two different classes. I want to combine the values of both, only to show the resulting information in a ListBox. The information is stored in a database, the classes in my code have been created by a LINQ to SQL DataClass.
First class (ReactorParameters)
This class contains information about the reactor at a specific time, like the flame temperature, amount of oil, amount of air...
Class definition:
public class ReactorParameters
{
public TimeSpan Time
{ get; set; }
public double Temperature
{ get; set; }
public double Oil
{ get; set; }
public double Air
{ get; set; }
}
Example data:
Second class (ProductInformation)
The ProductInformation class stores information about which product has been produced by the reactor at what time period.
Class definition:
public class ProductInformation
{
public TimeSpan Time_From
{ get; set; }
public TimeSpan Time_To
{ get; set; }
public Product Product
{ get; set; }
}
Example data:
What do I want the result to look like?
What I want to acheive is to combine the reactor parameters with the product that has been produced at the given time.
This is an easy task. Why are you asking?
Of course, I can make a new class, create an instance for each ReactorParameters and store the relevant Product in it. But since this is just for UI purposes (I don't need the extra class for anything else), I'm not sure if there is a better way to reach the goal. I've heard about CompositeCollection and CollectionView, but I'm not sure if this is helpful for me.
So, is there any other way than a separate class to populate my ListBox?
You have to create view class to bind it.
Try following code (I replaced Product with string for test):
public class ReactorParameters
{
public TimeSpan Time { get; set; }
public double Temperature { get; set; }
public double Oil { get; set; }
public double Air { get; set; }
}
public class ProductInformation
{
public TimeSpan Time_From { get; set; }
public TimeSpan Time_To { get; set; }
public string Product { get; set; }
}
public class ReactorView
{
public ReactorParameters Parameters { get; set; }
public ProductInformation Product { get; set; }
}
/// <summary>
/// entry point
/// </summary>
public void Test()
{
Random rnd = new Random(1000);
// random parameters
List<ReactorParameters> parameters = (from i in Enumerable.Range(0, 24)
select new ReactorParameters
{
Time = TimeSpan.FromHours(i),
Temperature = rnd.NextDouble() * 50.0,
Oil = rnd.NextDouble() * 20.0,
Air = rnd.NextDouble() * 30.0,
}).ToList();
// product information
List<ProductInformation> products = (from i in Enumerable.Range(0, 4)
select new ProductInformation
{
Time_From = TimeSpan.FromHours(i * 6),
Time_To = TimeSpan.FromHours(i * 6 + 6),
Product = "Product " + (char)('A' + i),
}).ToList();
// combine
var result = parameters.SelectMany(param => from product in products
where param.Time >= product.Time_From && param.Time <= product.Time_To
select new ReactorView
{
Parameters = param,
Product = product
});
// alternative query
var resultAlt = from param in parameters
from product in products
where param.Time >= product.Time_From && param.Time <= product.Time_To
select new ReactorView
{
Parameters = param,
Product = product
};
// print result
foreach (var item in result)
{
Console.WriteLine("{0,-5} {1,-8:0.00} {2,-8:0.00} {3,-8:0.00} {4,-10}",
item.Parameters.Time, item.Parameters.Temperature, item.Parameters.Air, item.Parameters.Oil, item.Product.Product);
}
}
In my opinion i should go for an new class containing those classes as a property. This way allows you to extend in the furture in case you need some more properties.
public class ProductReactorModel
{
public ReactorParameters ReactorParameters {get;set;}
public ProductInformation ProductInformation {get;set;}
}
Then create a list of the new created class List<ProductReactorModel>. Bind this list to the ListBox. With this class you can access both classes and properties to display in you're ListBox. Binding on the following way inside the ListBox.
{Binding ReactorParameters.Time}

Get value from a dictionary created from a PropertyInfo.GetValue

How to get this to work?
GSD a class that is used to store cache images of SQL tables. GSD has several public static properties representing different "CacheTables", which are Dictionary=long,rowtypeclass= objects, each with a different rowtype class. The rowtype class objects model SQL table rows.
public class GSDataObject
{
private Dictionary<long, GRPListRow> prvGRPList;
private Dictionary<long, TestTableRow> prvTestTable;
//=======================================
public Dictionary<long, GRPListRow> GRPList
{
get { return prvGRPList;}
set { prvGRPList = value; }
}
//=====================================
public Dictionary<long, TestTableRow> TestTable
{
get { return prvTestTable; }
set { prvTestTable = value; }
}
public class TestTableRow{
public int ID { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
public string Field6 { get; set; }
public string Field7 { get; set; }
public string Field8 { get; set; }
}
GSD and its different CacheTable properties work fine when declared hard-coded; I want to access them with reflection.
Specifically, I want to get a particular Row from a particular CacheTable in an instance of GSD, update that row, and then put it back. The instructions below describe the "get the row" phase.
The first three instructions work, and the resulting wrkCacheTableObject is of the correct type Dictionary=long,wrkRowtype=. However, wrkCacheTableObject is not indexed, so I can't retrieve rows from it.
wrkGSD is an instantiation of a class GSD. wrkCacheTableName is a string name of the particular CacheTable property. wrkRowType is the string class name of the row type.
wrkRow = Activator.CreateInstance(wrkRowType);
PropertyInfo wrkTablePropInfo = wrkGSDOType.GetProperty(wrkCacheTableName);
object wrkCacheTableObject = wrkTablePropInfo.GetValue(wrkGSD, null); // <== gives correct CacheTable instance
wrkTableDictObject = (Dictionary<long, object>)wrkCacheTableObject; //<=== fails here
wrkRow = wrkTableDictObject[wrkRowID];
// update wrkRow fields using reflection //<== this works if I retrieve wrkRow via hard code
// put it back into wrkTableDictObject
// put wrkTableDictObject back into wrkGSD
I'm not fixed on this particular set of instructions. And maybe if I can get the first phase above to work, it will show me how to do the other phases.
Found the answer via Experts Exchange:
dynamic wrkCacheTableObject = wrkTablePropInfo.GetValue(wrkGSD, null);
//--- get the row using dynamic
dynamic wrkRow = wrkCacheTableObject[(long)varAJR.rowID];
//--- put the row back
wrkCacheTableObject[(long)varAJR.rowID]= wrkRow;

LINQ ExecuteQuery

I want to use ExecuteQuery() to get IEnumerable of type Entities.UserLevel. Following code works well.
using (CDataContext context = data.GetDataContext())
{
string q = "SELECT *FROM UserLevel";
IEnumerable<Entities.UserLevel> res = context.ExecuteQuery<Entities.UserLevel>(q);
}
public class UserLevel
{
public int userID { get; set; }
public int levID { get; set; }
}
But the problem is that I must use property names in UserLevel class same as in database, otherwise I do not get values. I wonder is there any way to get values irrespective of class/property name? For example, I want to use following UserLevel class instead of above:
public class UserLevel
{
public int UserIdentity { get; set; }
public int LevelIdentity { get; set; }
}
Sure just write.
var q = "SELECT userID as UserIdentity, levID as LevelIdentity FROM UserLevel";
var res = context.ExecuteQuery<UserLevelDTO>(q);
But why not use Linq instead? You could write:
var res = from u in context.UserLevel
select new UserLevelDTO()
{
UserIdentity = u.userID,
LevelIdentity = u.levID
};
You would need to create your own DTO class.
public class UserLevelDTO
{
public int UserIdentity { get; set; }
public int LevelIdentity { get; set; }
}

Categories

Resources