I am working on an assignment for school and trying to implement as much features just for learning sake. Hence I've made a generic mapper that maps databse tables to objects to see what's possible. The Db in this case is local. I know I'm making loads and loads of calls and should go around this very differently but....
Everything works as intended except for when a class has a Collection of another class.
Example:
class Student {
public int Id { get; set; }
public string Name { get; set; }
}
My method for filling a list of all the students in the database.
public List<TModel> MapEntitiesFromDb<TModel>(string tablename, string customquery = "") where TModel : class, new()
{
try
{
sql = ValidateSelectSql(tablename, customquery);
}
catch (AccessViolationException ex) { Console.WriteLine(ex.Message); }
command.CommandText = sql;
command.Connection = conn;
List<TModel> list = new List<TModel>();
try
{
using (conn)
{
Type t = new TModel().GetType();
conn.Open();
using (reader = command.ExecuteReader())
{
if (t.GetProperties().Length != reader.FieldCount)
throw new Exception("There is a mismatch between the amount of properties and the database columns. Please check the input code and try again.");
//Possible check is to store each column and property name in arrays and match them to a new boolean array, if there's 1 false throw an exception.
string columnname;
string propertyname;
//Pairing properties with columns
while (reader.Read())
{
TModel obj = new TModel();
for (int i = 0; i < reader.FieldCount; i++)
{
columnname = reader.GetName(i).ToString().ToLower();
PropertyInfo[] properties = t.GetProperties();
foreach (PropertyInfo propertyinfo in properties)
{
propertyname = propertyinfo.Name.ToLower();
if (propertyname == columnname)
{
propertyinfo.SetValue(obj, reader.GetValue(i));
break;
}
}
}
list.Add(obj);
}
}
}
}
catch (Exception ex) { Console.WriteLine(ex.Message); }
return list;
}
My ValidateSelectSql just returns the sql string that needs to be used in the query.
After calling:
List<Student> = MapEntitiesFromDb<Student>("students");
It will return a list with all the students like intended.
Things go wrong when I add a collection for example:
class Student {
public Student()
{
this.Courses = new List<Course>();
string customsqlquery = ::: this works and is tested! :::
Courses = MapEntitiesFromDb<Course>("", customsqlquery);
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Course> Courses;
}
The courses list returned empty and with some help of the debugger tool I found out at the time of creating the object the Id property is 0 of course. In my query I am filtering on student Id but at the time of executing the method to fill the Courses list in the constructor the Id of student will always be 0 becuase it's set at a later stage and the result will be no courses in the list.
I'm wondering if I should put a check for an ICollection property after the other properties are set and if so execute a method on the object that in return executes the method that's now inside the constructor?
I can't call any methods on TModel, else it would be as simple as finding if TModel has a collection property and call obj.FillCollection(); after the Id property has been assigned in the GetEntitiesFromDb method.
I was also thinking about recursion. Again I'd have to find if obj has a collection property and then call GetEntitiesFromDB but it seems undoable because I also need to find out the type in between <> and I Can't send any customquery from the outside...
Maybe tackle it from a whole other perspective?
I can really use some advice on how to tackle this problem.
The most straightforward way to approach this would be to have the collection property lazy load what it needs. I would additionally recommend that you use IEnumerable<T> instead of ICollection<T> because this represents a read-only view of what's currently in the database, nobody should be modifying it in any way.
public class Student
{
private readonly Lazy<IEnumerable<Course>> courses;
public int Id { get; set; }
public IEnumerable<Course> Courses => this.courses.Value;
public Student()
{
this.courses = new Lazy<IEnumerable<Course>>(LoadCourses);
}
private IEnumerable<Course> LoadCourses()
{
var sql = "custom SQL query that uses this.Id after it's loaded";
return MapEntitiesFromDb(sql);
}
}
I'm only recommending this approach because you mentioned that this is just an academic exercise to help you learn about the tools available to you. In an actual production environment this approach would very quickly become unwieldy and I would recommend using Entity Framework instead (which may be something else that you might want to learn about).
Related
I've searched a lot and I think this is possible, but I feel like I am just blocked from knowing how to properly format it.
I have a class representing a product that is a relation class from our CRM to Magento.
Inside the constructor, I have to do some stuff like this...
public Product(IBaseProduct netforumProduct, MagentoClient client)
{
Product existingMagentoProduct = client.GetProductBySku(netforumProduct.Code);
if (existingMagentoProduct != null)
{
this.id = existingMagentoProduct.id;
this.name = existingMagentoProduct.name;
... many of them ...
this.visibility = existingMagentoProduct.visibility;
this.extension_attributes.configurable_product_links = existingMagentoProduct.extension_attributes.configurable_product_links;
}
else
{
// its a new product, new up the objects
this.id = -1;
this.product_links = new List<ProductLink>();
this.options = new List<Option>();
this.custom_attributes = new List<CustomAttribute>();
this.media_gallery_entries = new List<MediaGalleryEntry>();
this.extension_attributes = new ExtensionAttributes();
this.status = 0; // Keep all new products disabled so they can be added to the site and released on a specific day (this is a feature, not an issue / problem).
this.attribute_set_id = netforumProduct.AttributeSetId;
this.visibility = 0;
}
}
It seems silly to have to initialize all of the properties like that. I could use a mapper but that seems like a bandaid. I have to see if the product exists in magento first, and populate its ID and values, otherwise whenever I save the product it creates an additional one.
I considered doing the class constructor calling a static method, but I couldn't get the syntax right.
It might just be too late and I need to think about something else for awhile.
If you must do it in the constructor, you can get rid of a lot of code by first setting 'default' values to the 'Product' properties. This will remove the need to do them in the constructor. Next, if you wanted to automatically set the class's properties, you can use reflection.
public class Product
{
public int Id { get; set; } = -1;
public List<ProductLink> Product_Links { get; set; } = new List<ProductLink>();
....
public int Visibility { get; set; } = 0;
public Product(IBaseProduct netforumProduct, MagentoClient client)
{
var existingMagentoProduct = client.GetProductBySku(netforumProduct.Code);
if (existingMagentoProduct != null)
{
foreach (PropertyInfo property in typeof(Product).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(this, property.GetValue(existingMagentoProduct, null), null);
}
}
}
}
Though, I would like to point out that you probably shouldn't be using a REST client inside a class constructor, especially to just populate its data (also, you are performing a synchronous operation). It would be cleaner to have another layer that is responsible for populating this class using the client, and then use something like AutoMapper to map the data to it.
I'm using EF(db first) and trying to add new row in table using the next code:
var user = new User();
//Some logic to fill the properties
context.Users.AddObject(user);
context.SaveChanges();
Before saving changes on EF i want to verify that all required (not null and with no default value) properties are filled. How can i get all such fields?
I've tried few ways, but can't achieve needed result. The last try was like that:
var resList = new List<PropertyInfo>();
var properties = type.GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance).Where(p => !p.PropertyType.IsGenericType);
foreach (var propertyInfo in properties)
{
var edmScalarProperty =
propertyInfo.CustomAttributes.FirstOrDefault(
x => x.AttributeType == typeof (EdmScalarPropertyAttribute));
var isNullable = true;
if (edmScalarProperty != null)
{
var arg = edmScalarProperty.NamedArguments.FirstOrDefault(x => x.MemberName == "IsNullable");
if (arg != null)
{
isNullable = (bool) arg.TypedValue.Value;
}
}
if (!isNullable)
{
resList.Add(propertyInfo);
}
}
return resList;
Create a constructor with the required fields as parameters.
I always separate my domain objects from my EF objects (DTO objects). The domain object has only one constructor with the required fields. When I want to save these objects I convert them to DTO objects.
Have you looked at all into DataAnnotations for your model classes? Utilizing these (and using a separate object from the one EF creates for you) you can get pretty significant validation built into your models from the model level. Additionally, as L01NL pointed out, you can have your constructor take in parameters that require data.
Lots of information on Model and Validation can be found, one such example is:
http://msdn.microsoft.com/en-us/library/dd410405(v=vs.100).aspx
(look through this main section and its subsections)
using System.ComponentModel.DataAnnotations
public class Foo
{
public Guid Id { get; private set; }
[StringLength(50),Required]
public string FooName { get; private set; }
[Required]
public int Age { get; private set; }
// etc props
public Foo(string fooName, int age)
{
if (string.IsNullOrEmpty(fooName))
throw new ArgumentException("FooName cannot be null or empty"); // note there is also a "minimum length" data annotation to avoid doing something like this, was just using this as an example.
this.Id = Guid.NewGuid();
this.FooName = fooName;
this.Age = age;
}
}
public class YourController
{
[HttpPost]
public ActionResult Add(Foo foo)
{
if (!ModelState.IsValid)
// return - validation warnings, etc
// Add information to persistence
// return successful add?
}
}
I've already searched through StackOverflow (and other websites) about transforming a DataTable to List with reflection in C#.
My results until now are pretty good: I can reflect 200k lines in 3.5 seconds (0.5 seconds in hardcoded mode).
But my entities (the classes that represent my data, but I think you already know that) follow this pattern:
My database have columns like this (I don't actually do this, but you'll get the idea):
Table: Clients
Columns:
ClientID, ClientName, ClientPhone, CityID[FK]
I'm using SqlConnection (MySqlConnection), so I have to hardcode my entities and transform the database result in a list of this entity. Like:
Select *, cit.* from Clients cli
Inner join Cities cit on (cit.CityID == cli.CityID)
Inner join Countries cou on (cou.CountryID == cit.CountID)
I don't know if this SQL is correct, but I think you got the idea. This should return some fields like this:
ClientID, ClientName, ClientPhone, CityID, CityName, CountryID, CountryName
Shoud result a List<Client>.
Here's the problem: I have 2 inner joins and I represent this data in my entities like this (I like the expression "like this"):
public class Client
{
public int ClientID { get; set; }
public string ClientName { get; set; }
public string ClientPhone { get; set; }
public City ClientCity { get; set; }
}
public class City
{
public int CityID { get; set; }
public string CityName { get; set; }
public Country CityCountry { get; set; }
}
public class Country
{
public int ContryID { get; set; }
public string CountryName { get; set; }
}
So, if I have a Client object, I would get its country name by the expression client.ClientCity.CityCountry.CountryName. I call it a 3-level property acessor.
And I want to reflect it properly. Here is the main method to transform the DataTable into a List. My native language is Portuguese, but I tried to translate my comments to match my description above.
The idea of this code is: I try to find in the main class the column I have to set. If I don't find it, I search the property in the properties that are objects. Like CityName inside ClientCity inside Client. This code is a mess.
public List<T> ToList<T>(DataTable dt) where T : new()
{
Type type= typeof(T);
ReflectionHelper h = new ReflectionHelper(type);
insertPropInfo(tipo); //a pre-reflection work, I cache some delegates, etc..
List<T> list = new List<T>();
DataTableReader dtr = dt.CreateDataReader();
while (dtr.Read())
{
T obj = new T();
for (int i = 0; i < dtr.FieldCount; i++)
{
GetObject(ref obj, tipo, dtr.GetName(i), dtr.GetValue(i));
}
list.Add(obj);
}
return lista;
}
//ref T obj: the object I create before calling this method
//Type classType: the type of the object (say, Client)
//string colName: this is the Database Column i'm trying to fill. Like ClientID or CityName or CountryName.
//colLineData: the data I want to put in the colName.
public void GetObject<T>(ref T obj, Type classType, string colName, object colLineData) where T : new()
{
//I do some caching to reflect just once, and after the first iteration, I think all the reflection I need is already done.
foreach (PropertyInfo info in _classPropInfos[classType])
{
//If the current PropertyInfo is a valuetype (like int, int64) or string, and so on
if (info.PropertyType.IsValueType || info.PropertyType == typeof(string))
{
//I think string.Equals is a little faster, but i had not much difference using "string" == "string"
if (info.Name.Equals(colName)) //did I found the property?
if (info.PropertyType != typeof(char)) //I have to convert the type if this is a Char. MySql returns char as string.
{
_delegateSetters[info](obj, colLineData); //if it isn't a char, just set it.
}
else
{
_delegateSetters[info](obj, Convert.ChangeType(colLineData, typeof(char)));
}
break;
}
else //BUT, if the property is a class, like ClientCity:
{
//I reflect the City class, if it isn't reflected yet:
if (!_classPropInfos.ContainsKey(info.PropertyType))
{
insertPropInfo(info.PropertyType);
}
//now I search for the property:
Boolean foundProperty = false;
object instance = _delegateGetters[info](obj); //Get the existing instance of ClientCity, so I can fill the CityID and CityName in the same object.
foreach (PropertyInfo subInfo in _classPropInfos[info.PropertyType])
{
if (subInfo.Name.Equals(colName))//did I found the property?
{
if (instance == null)
{
//This will happen if i'm trying to set the first property of the class, like CityID. I have to instanciate it, so in the next iteration it won't be null, and will have it's CityID filled.
instance = _initializers[info.PropertyType]();//A very fast object initializer. I'm worried about the Dictionary lookups, but i have no other idea about how to cache it.
}
_delegateSetters[subInfo](instance, colLineData);//set the data. This method is very fast. Search about lambda getters & setters using System.Linq.Expression.
foundProperty = true;
break;//I break the loops when I find the property, so it wont iterate anymore.
}
}
if (foundProperty)//if I found the property in the code above, I set the instance of ClientCity to the Client object.
{
_delegateSetters[info](obj, instance);
break;
}
}
}
}
There is a problem with this code: I can reach the CityID and CityName, and fill it. But CountryID and CountryName wont. Because this code can do a 2-level reflection, I need some recursive-approach to fill many levels I need. I tried to do this BUT i got so many stack overflows and null reference exceptions I almost gave up.
This code would make it much easier to fetch database rows, Did you already find some library or anything that does what I want? If not, how could I achieve a n-level reflection to make a proper List from a DataTable?
Your problem is really common and practically every ORM in circulation addresses this question.
Of course changing an already written application to take advantage of an ORM is often unpractical, but there are some simple ORM that are really easy to add to an existing application and let you replace incrementally the already written code.
One of these ORMs is DAPPER. It consists of just one source file that you can include directly in the same project with your POCO classes and repository methods (Or just reference the compiled assembly). It is really easy to learn and it is incredibly fast considering the complexity of the work to be carried out. Not to mention that the authors of this little gem are regularly on this site answering questions on their work. Just do a search with the #dapper tag
The only nuisances that I have found to date are the mapping one-to-one from your POCO properties and the field names and also the sometime eluding rules between PK and FK when your keys are not named ID. But that's me that I still haven't fully understood these rules.
Consider to use EntityFramework. It will automate all this work.
This is based on you getting a dataset with the 3 tables and creating the proper DataRelation.
On your particular case(200k lines) i dont know how it will perform but shouldnt be that bad :).
Your calling code could be something like this:
List<Clients> clients = Test.CreateListFromTable<Clients>(ds.Tables["Clients"]);
Remember as i said its based in you fettching the dataset and creating the relations.
Next here is the class with the methods in question(ClientsToCity and CityToCountry are the names of the datarelations,you can place your own):
public class Test
{
// function that set the given object from the given data row
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
foreach (DataColumn c in row.Table.Columns)
{
PropertyInfo prop = item.GetType().GetProperty(c.ColumnName);
if (prop != null && row[c] != DBNull.Value)
{
prop.SetValue(item, row[c], null);
}
else
{
if (c.ColumnName == "CityID")
{
object obj = Activator.CreateInstance(typeof(City));
SetItemFromRow<City>(obj as City, row.GetChildRows("ClientsToCity")[0]);
PropertyInfo nestedprop = item.GetType().GetProperty("ClientCity");
nestedprop.SetValue(item, obj, null);
}
else if (c.ColumnName == "CountryID")
{
object obj = Activator.CreateInstance(typeof(Country));
SetItemFromRow<Country>(obj as Country, row.GetChildRows("CityToCountry")[0]);
PropertyInfo nestedprop = item.GetType().GetProperty("CityCountry");
nestedprop.SetValue(item, obj, null);
}
}
}
}
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
T item = new T();
SetItemFromRow(item, row);
return item;
}
// function that creates a list of an object from the given data table
public static List<T> CreateListFromTable<T>(DataTable tbl) where T : new()
{
List<T> lst = new List<T>();
foreach (DataRow r in tbl.Rows)
{
lst.Add(CreateItemFromRow<T>(r));
}
return lst;
}
}
I was looking to map my database query results to strongly type objects in my c# code. So i wrote a quick and dirty helper method on the SqlConnection class which runs the query on the database and uses reflection to map the record columns to the object properties. The code is below:
public static T Query<T>(this SqlConnection conn, string query) where T : new()
{
T obj = default(T);
using (SqlCommand command = new SqlCommand(query, conn))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
obj = new T();
PropertyInfo[] propertyInfos;
propertyInfos = typeof(T).GetProperties();
for (int i = 0; i < reader.FieldCount; i++)
{
var name = reader.GetName(i);
foreach (var item in propertyInfos)
{
if (item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase) && item.CanWrite)
{
item.SetValue(obj, reader[i], null);
}
}
}
}
}
}
return obj;
}
public class User
{
public int id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public DateTime signupDate { get; set; }
public int age { get; set; }
public string gender { get; set; }
}
var user = conn.Query<User>("select id,firstname,lastname from users");
I just wanted a second opinion on my approach above of using reflection to tie the values together, if there's anything i can do better in the code above. Or if there's some other totally different approach i can take to get the same result?
I think i can probably improve the code in the helper method by removing the loop for propertyInfos and using a dictionary instead. Is there anything else that needs to be tweaked?
P.S: i'm aware of Dapper, i just wanted to implement something similar on my own to help me learn better.
What you've done is basically what linq-to-sql or other OR-mappers do under the hood. To learn the details of how it works it's always a good idea to write something from scratch.
If you want more inspiration or want to have something that's ready for production use out-of-the-box I'd recommend reading up on linq-to-sql. It is lightweight, yet competent.
There are a few of things I can think of:
I think that in order to skip the loop you can use:
reader[item.Name]
I've done something similar myself, but I never ran into dapper. I'm not sure if it uses reflection, but it's always a good idea to read someone else's code to sharpen your skill (Scott Hanselman frequently recommends doing so).
You can also look at:
http://www.codeproject.com/KB/database/metaquery_part1.aspx
You can implement an attribute that maps a field to a database column, but that's just for fun.
Edit:
5: You can also skip the while loop over the reader and just take the first row, and document the fact that your query only returns one object, so it doesn't pull a thousand rows if the query returns a thousand rows.
I am working on a C# application which consists of objects Department, Course, and Section. Each Department has many Courses, and each Course has many Sections. Currently I have three classes: Department, Course, and Section. Department contains some properties and then a List Courses, which contains the courses the department offers. Course contains some properties and then a List Sections, which contains the sections of the course. Is this a good way to have the code structured or should I be doing it a different way?
Secondly, when I instantiate a department in my application, I set some properties and then would like to begin adding courses to the List Courses defined in the Department class. However, I seem to be unable to simply do Department.Courses.Add(Course) from the application. What must I do within the Department class so that I may add objects to that list without breaking the principle of encapsulation?
An example of what I have with the list right now is:
class Department
{
// ......
List<Course> Courses = new List<Course>;
}
however Department.Courses is not available in the program code after the class has been instantiated (all other properties of the class are available).
Instantiate the internal Courses list inside the parameterless constructor of your class.
private List<Course> _coursesList;
public Department()
{
_coursesList = new List<Course>();
}
Also, another way to ensure the encapsulation is to provide a method on your Department class to add the courses to it instead of directly exposing the courses list. Something like
public void AddCourse(Course c) { ... }
// or (adding the feature of doing the method calls in a composable way)
public Course AddCourse(Course c) { ... }
// or
public void AddCource(String name, etc) { ... }
I think in your case it is not a good idea do directly exposes the List because the class List, may provide methods like, Add and Remove which could potentially creates an invalid state on your parent class. So if you choose to expose methods to manipulate the internal collections like I suggested, you could expose an array of Courses to your API clients (remember the arrays are read-only) so your API consumers won't be able to the create side effects on your department class.
public Course[] Courses {
get { return _coursesList.ToArray(); }
}
In addition, you could also implement the IEnumerable interface on your Department class. It would enable you to take advantage of the all LINQ extension methods available in C# 3.0.
I hope it helps,
Carlos.
Probably something Similar. There are several ways of soing this. depends upon what your requirements are.
public class Department
{
// Initialize the list inside Default Constructor
public Department()
{ courses = new List<Course>(); }
// Initialize List By Declaring outside and Passing with Dpartment Initilization
public Department(List<Course> _courses)
{ courses = _courses; }
List<Course> courses;
public List<Course> Courses
{
get
{
if (courses == null)
return new List<Course>();
else return courses;
}
set { courses = value; }
}
internal bool AddCourseToCourses(Course _course)
{
bool isAdded = false;
// DoSomeChecks here like
if (!courses.Contains(_course))
{
courses.Add(_course);
isAdded = true;
}
return isAdded;
}
}
public class Course
{
public Course(List<Subject> _subject)
{ subjects = _subject; }
List<Subject> subjects;
public List<Subject> Subjects
{
get { return subjects; }
set { subjects = value; }
}
}
// I do not get what do you mean by course "section", very general.
// used Subject instead, Change as you want just to give an idea
public class Subject
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
int creditHours;
public int CreditHours
{
get { return creditHours; }
set { creditHours = value; }
}
public Subject(string _name, int _creditHours)
{
name = _name;
creditHours = _creditHours;
}
}
public class TestClass
{
public void DoSomething()
{
// Subjects
Subject subj1 = new Subject("C#", 10);
Subject subj2 = new Subject(".Net", 10);
// List of Subjects
List<Subject> advancedPrSubjects = new List<Subject>();
advancedPrSubjects.Add(subj1);
advancedPrSubjects.Add(subj2);
// Course
Course advancedProgramming = new Course(advancedPrSubjects);
// Deliver authoroty to add Course to Department Class itself
Department dept = new Department();
dept.AddCourseToCourses(advancedProgramming);
}
}
There are better ways of doing this. have a look at these tutorials for better insight
http://www.csharp-station.com/Tutorials/Lesson07.aspx
http://www.functionx.com/csharp/index.htm
Hope it helps
As to your second question - without some code or more details its a bit hard - but i'll take a guess.
You're probably not actually creating the list, just declaring it
List<xxxx> _variable;
vs
List<xxxx> _variable = new List<xxxxx>();
You must create a list to be able to add to it (new List());
You sound as if you're on the right track.
Your second problem could be down to many things.
It could be as Ruddy says and that you're not creating the list.
It could also be that your Courses List is not public or that you haven't instanciated a new Course object to add.