How to convert Model class object to datatable - c#

I am new to c# ,I want to convert a model class object to DataTable in Web API.
Model Class
public class Employee_Master_Model
{
public string UserType { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Mobile { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
public int EmployeeId { get; set; }
}
Controller:
public async Task<IActionResult> CreateEmployee([FromBody] Employee_Master_Model employee_Master_Model)
{
try
{
// Need to convert the object (employee_Master_Model) to DataTable
}
catch (Exception ex)
{
throw ex;
}
}
trying it for hours can't get it done .Can anyone help me to fix this .

By the following code you can dynamically create the datatable columns:
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(Employee_Master_Model));
DataTable dt = new DataTable();
foreach (PropertyDescriptor p in props)
dt.Columns.Add(p.Name, p.PropertyType);
The rest is just copying entity fields values to datatable columns.

Why can't you just create a datatable and fill it with the model data value like
DataTable dt = new DataTable();
DataColumn c = new DataColumn("FirstName");
dt.Columns.Add(c);
DataRow r = dt.NewRow();
r["FirstName"] = model.FirstName; // model is an instance of Employee_Master_Model

You have a number of issues in your code.
First, EmployeeId has no business being part of your create model. Let the database generate the id for you. I suspect you use Employee_Master_Model for both create and update. If that is the case I would suggest you stop doing that, create a separate model for create and remove the properties you don't need.
Second, do not return a DataTable from your API. A common approach is to return the generated id of the created entity and nothing more. If you really need to get all the data then you can create a GET endpoint, pass it the id you just received and that will give you the data.
Third, since you are using WebAPi, learn to use models and learn to use DTOs ( Data Transfer Objects ). Those are commonly used for returning data. Datatables are part pf a database implementation, you don't want that exposed via the API.

Related

How to set the column order of DTO properties programatically

I am having simple DTO class. I would like to dynamically set the column order of this dto properties when the results are stored into excel.
Public class Student
{
public int Id {get;set;}
public string FirstName {get;set;}
public string LastName {get;set;}
}
I know there is a way using DataAnnotation Column Order Attribute on each property inside DTO. But is there a way to handle this using code to set the column order of DTO.
I am using simple web application and added System.ComponentModel.DataAnnotation Reference.
If there is a work around without using DataAnnotation to set the column order using code that will be great
What about using a static array inside your class or in a different class which list all columns in the order you want, and while saving to excel you loop through this array first to get columns in the correct order.
Something like that:
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<string> OrderedProperties => new List<string> { "LastName", "FirstName", "Id" };
}
public class DataAccess
{
public void SaveStudentToExcel()
{
var student = new Student(); // instead of new this should be your object filled with real data.
foreach (var propName in student.OrderedProperties)
{
var val = student.GetType().GetProperty(propName).GetValue(student, null);
// new cell in excel => with val and propName then save
}
}
}

Creating Complex Automapper

I am trying to use the automapper to map my Database table output to my class object. But the table has 3 rows that are all belong to single employee data which needs to be assigned to a single class object. How we can we create mapper ? Is it possible to create mapper with this table data ?
How can I write Autommapper to populate the class EmployeeDetails
public class EmployeeDetails
{
public string EmpNo { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
public List<Phone> Phone { get; set; }
}
public class Address
{
public string Address_1 { get; set; }
public string City { get; set; }
}
public class Phone
{
public string PhoneType { get; set; }
public string PhoneNo { get; set; }
}
Datatype EmpNo Name Address_1 City PhoneType PhoneNo
Name 1234 Test Test addr Testcity Null Null
Phone 1234 Null Null Null Mobile 123456
Phone 1234 Null Null Null Work 789546
public IEnumerable< EmployeeDetails > GetEmployeeDetails()
{
return ExecuteEmpReader< EmployeeDetails>().ToList();
}
private IEnumerable<T> ExecuteEmpReader <T>()
{
DataTable dt=new Datatable();
//Assume the dt will be loaded as per the above table.
foreach (DataRow item in dt.Rows)
{
yield return _mapper.Map<T>(item)
}
}
I don't believe there is a way to do what you want using solely Automapper. Specifically because of the merging of rows into one. This link shows how you can at least unflatten your object to your desired object structure, but then you would need to write the logic to merge the employees addresses and phone numbers yourself.
http://martinburrows.net/blog/2016/01/18/automatic-unflattening-with-automapper
On the contrary to make your resulting code easier to maintain in future. I would recommend creating some views of your Employee Object, one for Employee, one for Address, and one for Phone. Then you can use EF to map your views straight to your objects.

How to make data annotations work with custom Entity Framework queries

I'm using VS 2017 and EF 6.2. I have the following POCO class where I've added the Display attribute to the OR_NAME property.
public partial class ORDERS
{
public int OR_ROWID { get; set; }
public Nullable<int> OR_REFNO { get; set; }
public string OR_PROD_CODE { get; set; }
[DisplayName("Order Name")]
public string OR_NAME { get; set; }
public Nullable<DateTime> OR_DATE { get; set; }
public virtual CONTACTS_BASE CONTACTS_BASE { get; set; }
public virtual ORDER_TYPES ORDER_TYPES { get; set; }
}
I'm binding my DataGridView to an IQueryable and expect my OR_NAME column header text to be Order Name - however at some cases it does not work and I'm wondering if I'm doing something wrong or if there is a fix for this.
Below are the scenarios which it works and does not work
Scenario 1: works
dataGridView1.DataSource = context.ORDERS.ToList();
Scenario 2: does not work
var query = from a in context.ORDERS
select new
{
a.OR_ROWID,
a.OR_NAME,
a.OR_PROD_CODE
};
dataGridView1.DataSource = query.ToList();
Scenario 3: does not work
dataGridView1.DataSource = context.ORDERS
.Select(x => new { x.OR_NAME })
.ToList();
Any help or suggestions? Thanks in advance
Due to the new keyword used in scenarios 2 and 3, a new object is create of a different type than the class ORDER.
When creating these objects from the dynamic types specified the data annotations are not considered as they are of a different type.
Scenario 1 works as the list of objects are still of type ORDER and therefore the data annotation remains.

select query to get all values based on distinct values using linq

I have n number of services and under each service there will be 2 routes(some times more than 2). Under each route there are n number of stops. I am getting values from db as a table in below column order
ServiceId, Service Name,serviceLine color,RouteId, Route Name, Stop Id, Stop Name, Latitude, Longitude.
I want to convert it an object list of below format
public class Service
{
public string ServiceId { get; set; }
public string ServiceName { get; set; }
public string ServiceLineColor { get; set; }
public List<RouteData> RouteList { get; set; }
}
public class RouteData
{
public string RouteId { get; set; }
public string RouteCode { get; set; }
public string RouteName { get; set; }
public List<StopData> stopList { get; set; }
}
public class StopData
{
public string StopCode { get; set; }
public string StopName { get; set; }
public string Latitude { get; set; }
public string Longitude { get; set; }
public string StopType { get; set; }
}
Is there any easy way in linq to convert data in to below format? I wanted to avoid looping.. since i am getting nearly 1k records from db. Please help me to solve this issue.
Or is it best to use db calls to format data. i didn't prefer that because if there is 50 services i need to do 50 db calls and again have to do data formatting logic.
To avoid looping over the data structure each time, you could build up additional dictionaries that provide fast access of the objects by id:
var myServiceIndex = new Dictionary<string, Service>()
var myRouteDataIndex = new Dictionary<string, RouteData>()
Service service;
RouteData routData;
foreach (var record in databaseRecords)
{
if (myRouteDataIndex.TryGetValue(record.RouteId, out route))
{
// add stop data
}
else if (myServiceIndex.TryGetValue(record.ServiceId, out service)
{
// add route data
// add stop data
}
else
{
// add service
// add route data
// add stop data
}
}
You have a number of stops, and for each stop entry in database you have to map it to a C# object. In this case, looping is inevitable, as far as I see. Linq, and eg. entity framework, use looping internally.
One option is to use entity framework or Linq to SQL. It will give you strong type classes representing each DB table. But you have to change your DB schema and use foreign keys to link service, route, and stops.
C# code would look exactly like yours, which is auto generated by entity framework and in-sync with DB schema.
Second option is to convert manually. Note, your current schema doesn't complient with Third normal form. If you don't want to change your schema, you could read and generate them using group by clause.

C# - The ObjectContext instance has been disposed and can no longer be used for operations that require a connection

I'm sorry for the repetition. I'm heading with a same problem but still could not handle it. I'm using Angularjs framework and Asp.net mvc.
My person class:
public partial class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Telephone { get; set; }
public string Adress { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
}
My User class:
public partial class User
{
public User()
{
this.People = new HashSet<Person>();
}
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string Gender { get; set; }
public virtual ICollection<Person> People { get; set; }
}
My js code:
$http.get('/Data/GetPeople', { params: { UserID: "1" } }).success(function (data) {
$scope.model = data;
});
I'm trying to get records from my database:
public ActionResult GetPeople(int UserID)
{
using (PersonEntities dc = new PersonEntities())
{
var model = new PersonIndexVM();
model.People = dc.People.Where(b => b.UserID == UserID).ToList();
//model.People = dc.People.Include("User").ToList();
return Json(model, JsonRequestBehavior.AllowGet);
}
}
As I see with debugging, I'm getting the right objects from database in GetPeople method. But after that I'm getting this error:
'The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.'
I tried to Eagerly load: model.People = dc.People.Include("User").Where(b => b.UserID == UserID).ToList(); Still getting the same error.
It would be a pleasure if you help me.
The problem is solved. I get help from my friend. Its about unclosed connection. Its my fault. I didn't mention it.
In PersonIndexVM(), I created People = new List<Person>(); Person class is created by entity framework. It is related with database. When I create a model that is an object of PersonIndexVM() in GetPeople() method and return this model object as a json, model object try to reach User class informations after the connection closed. And I'm getting this error. To solve this problem:
Closing the lazy loading to prevent reaching User information. dc.Configuration.LazyLoadingEnabled = false;
Creating another class not related with database and return its object as Json.
You can strongly type your inclusion, which will give you hints as to whether or not your object structure is correctly related. This solves any "magic strings" issues, such as your table being named Users instead of User inside of your EF context, after including DbExtension.
using System.Data.Entity.DbExtension;
model.People = dc.People.Include(c => c.User).ToList();
However, if you are using ObjectContext instead of DbContext, you may be stuck with magic strings. That said, EF will "pluralize" most names, so your table is most likely named "Users". Check to see if the table exists as dc.Users and, if it does, change your magic string to match.
model.People = dc.People.Include("Users").ToList();
It's look like the problem is when you using keyword Using.
Look at this How to solve the error The ObjectContext
In my case, i was passsing all model 'Users' to a column, and it doesn't mapped correctly, so i just pass 'Users.Name' and it fixed it.!!
var data = db.ApplicationTranceLogs
.Include(q=>q.Users).Include(q => q.LookupItems)
.Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), Users = ***q.Users***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data })
.ToList();
--
var data = db.ApplicationTranceLogs
.Include(q=>q.Users).Include(q => q.LookupItems)
.Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), Users = ***q.Users.Name***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data })
.ToList();
In my case I had a bug in the front end, which was causing the same function in the backend to be triggered 3 times, causing a threading issue.
Maybe look into this as a possibility too.

Categories

Resources