C# Object Oriented Programming, Inheritance, Foreign Keys and DB - c#

the title might be too generic, but I have a very specific question about how to design C# classes based on a relational database.
Let's say we have a table called TPerson and a table called TComment, both tables have one column in common (PERSON_ID, PK on TPerson and FK in TComment).
Let's say we have a web app where we are displaying a list showing all comments from everyone, in this list we are showing the comment and the first name and last name of the author (TPerson) and the date the comment was created as well. I think it is not appropriate to use inheritance (Base class TPerson, derived class TComment) because I don't need the alias for example, I don't want to drag the other columns with me if I only need first name and last name (column TPerson might have lots of columns).
I want a class design that is able to:
Add Person objects and save to DB
Add Comment objects and save to DB
Retrieve the list of comments with first name and last name of the person
Is there a way to create re-usable code by only retrieving or using parts of an object ?
The only way to do this would be to use inheritance and every time I retrieve a Comment, I would also create the Person object that goes with it, but in some parts it would be overkill to retrieve the entire thing when I only need certain parts of it...
If I were to create classes to represent the data, I would go with something like this, Any ideas ?, thanks for your help !:
class Person
{
int personId;
string firstName;
string lastName;
string alias;
DateTime creationDate;
public int PersonId
{
get { return personId; }
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
public string Alias
{
get { return alias; }
set { alias = value; }
}
public DateTime CreationDate
{
get { return creationDate; }
}
//for adding new person object
public Person(string first_name, string last_name, string alias)
{
}
//internal usage
public Person()
{
}
public void Save()
{
//save new person object in DB or update...
}
public static Person GetPerson(int personId)
{
Person p = null;
//call sproc and load from database...
p = new Person();
p.personId = 10;
p.firstName = "First Name";
p.lastName = "Last Name";
p.alias = "Alias";
return p;
}
}
class Comment
{
int commentId;
int personId;
string comment;
DateTime creationDate;
public int CommentId
{
get { return commentId; }
set { commentId = value; }
}
public int PersonId
{
get { return personId; }
set { personId = value; }
}
public string Comment1
{
get { return comment; }
set { comment = value; }
}
public DateTime CreationDate
{
get { return creationDate; }
set { creationDate = value; }
}
public Comment(int person_id, string comment)
{
}
public Comment()
{
}
public void Save()
{
//save or update to DB
}
public static List<Comment> GetComments()
{
List<Comment> comments = null;
//get data from db and load...
comments = new List<Comment>();
comments.Add(new Comment() {
commentId = 1,
personId = 10,
comment = "this is one comment",
CreationDate = DateTime.Now });
comments.Add(new Comment() {
commentId = 1,
personId = 11,
comment = "this is another comment",
CreationDate = DateTime.Now });
return comments;
}
}

Actually why not use entity framework? It treats your database as objects.
Edit:
Basically you can use entity framework to creates database objects for you.
It can maps your table into objects, TComments, TPerson objects etc.
And creates relationship between them with your foregin key.
i.e In your case, because your TPerson table contains Foregin Keys of TComments.
The TPerson object created by Entity Framework will contains a collection of TComment for each TPerson. You will only have to write LINQ to take them out.
And when you modify those object, you only need to execute the Save() method of the entity container and it will save to the database.

This is definitely not a classic case of inheritance - typically, you want to use inheritance to reflect an "is a" relationship - "car is a vehicle", for instance. There's no logical way in which you might say "comment is a person"; you could, of course, say that both comment and person are "things you can store into a database".
Whilst I agree with both King Chan and ken2k that you would probably want to use an existing ORM tool for this, it would help to read up on the underlying concepts of OO design first. I'd recommend Craig Larman's book "Applying UML and patterns" - it's technology-agnostic, but has a great description of how to map objects to a database.

This appears to be a one-to-many association. I wouldn't use inheritance here.
What I would use is an ORM framework. I recommend using Entity Framework.
Step 1: create a database with the schema you provided (actually you could also start from code or model but that's another topic)
Step 2: import your database in Visual Studio and create an entity model from it. This will create classes (called "entities") to represent your objects. This is a mapping between the tables of your database and the objects you manipulate in your application.
Step 3: that's all. Now you can retrieve all your comments from your DB using something like the following lines:
foreach (Comment comment in MyContext.Comments)
{
// Here you have access to the associated Person
Person = comment.Person;
}
Also it'll be easy to get all the comments associated with a person thanks to the one-to-many association:
var comments = Person.Comments;

Related

Need to assign object id to field when creating object

I've got an object that has a property which, on creation, should always be populated with the object's id(primary key).
How do I do that?
I've tried assigning the id to the field just after the db savechanges(), before returning to the view, but that creates two records on my db.
Also tried loading a new instance of the same, assign the id to the field and save it, but also creates two records.
Thanks for any help.
//model
public partial class supplier
{
public int id { get; set; }
public int ref { get; set; }
}
//controller
public ActionResult Create(supplier sup)
{
if (ModelState.IsValid)
{
db.suppliers.add(sup)
db.SaveChanges();
sup.ref = sup.id;
db.SaveChanges();
}
...
}
Hi all, thanks for all your help... I've found a solution... don't really know if it's the correct one, but it does the job.
For whoever is interested here it is:
supplier needToUpdateSupplier = (from s in db.suppliers where s.id.Equals(supplier.id) select s).ToList().FirstOrDefault();
if (needToUpdateSupplier != null)
{
needToUpdateSupplier.ref = supplier.id;
db.suppliers.Attach(needToUpdateSupplier);
db.Entry(needToUpdateSupplier).Property(e => e.ref).IsModified = true;
db.SaveChanges();
}
Create the object
Save it (SaveChanges). This will give you the id.
Assign the id to your property, using the existing object (you don't need to load it again)
Save again
Notes: The property must be nullable, because up on creation it will be empty.
// A Test object
class MyObject
{
public int ID {set;get;}
public int? MyProperty {set;get;}
}
void Foo()
{
var test = new MyObject();
db.MyObjects.Add(test);
db.SaveChanges();
// at this moment test has the Id set. You can assign it.
test.MyProperty = test.Id;
db.SaveChanges();
}
With EF it should set the ID for you:
db.Posts.Add(post);
db.SaveChanges();
// post.ID should be now set

Merge 2 POCO into 1 Class and execute CRUD

I am using PetaPoco as an ORM and want to combine 2 POCO into 1 class and then execute CRUD operations with this class.
Right now I got something like this:
*Table Person has an fk address_id.*
public class Person
{
public PersonPOCO Person { get; set; }
public AddressPOCO Address { get; set; }
public Person(string sql)
{
Person = Db.SingleOrDefault<PersonPOCO>(sql);
Address = Db.SingleOrDefault<PersonPOCO>("SELECT * FROM addresses WHERE id = #0, PersonPoco.address_id");
}
public void Save()
{
var addressId = Db.Save(Address); // Returns inserted ID
Person.address_id = addressId;
Db.Save(Person);
}
}
This is working fine so far.
But it gets really annoying and repetive doing this for every needed Combination.
Especially saving is a pain, since I have to map the inserted ID to the dependent object.
Are there better ways to achieve this?
PetaPoco is designed to be fast and lightweight, so, you won't find this kind of complex mapping like Linq-to-sql or EF have in it.

How to save class with master/detail

This is my class Record which can be master/detail/details
Records of teachers and their students
class Record
{
public string RecorNo { get; set; }
private List<Teacher> _Student = new List<Teacher>();
public List<Teacher> Teacher
{
get { return _Teacher; }
set { _Teacher=value; }
}
}
class Teacher
{
private string _TeacherName;
public Teacher(string teachername)
{
_TeacherName = teachername;
}
public string TeacherName
{
get { return _TeacherName; }
set { _TeacherName = value; }
}
private List<Students> _students = new List<Students>();
public List<Students> Students
{
get { return _students;}
set { _students = value;}
}
}
class Details
{
private string _studentname;
public Details(string studentsname)
{
_studentsname = studentsname;
}
I have bind the Teacher/Student in 2 datagridview like so:
TeacherBindingSource.DataSource = record.Teacher;
StudentBindingSourcce.DataSource = TeacherBindingSource;
StudentBindingSourcce.DataMember = "Student";
dataGridView1.DataSource =TeacherBindingSource;
dataGridView2.DataSource = StudentBindingSourcce;
Now Im lost how to save this record.
How can I save this to the database?
I want to have a.
record.Save(); method that will save the whole object.
I can iterate through the list and insert it one by one but the record may be
existing already in the database so i had to know which record is added, updated or deleted?
Also how to make a method that will fill the teacher/student list like:
records.LoadTeachers();
records.Teacher("Smith").LoadStudents();
you might want to have a look at ORM (object-relational mapping) libraries like Microsofts Entity Framework or (my favorite) NHibernate. Their sole reason to exist is to make persisting objects to a relational database easier. They can create new rows, can track object updates and relations between objects. You could create the logic needed to persist your objects on your own, but the established ORM libraries are matured, widely used and probably much better designed than anything a single developer could come up with in a reasonable amount of time.

Complex mapping with tables without IDs

Short version:
Is it possible in NHibernate to create a mapping for a class that has no corresponding table in the database and specify for each property where the data should come from?
Long version:
I have the following class:
public class TaxAuditorSettings
{
private readonly IList<Month> _allowedMonths = new List<Month>();
private readonly IList<Company> _allowedVgs = new List<Company>();
public IList<Month> AllowedMonths
{
get { return _allowedMonths; }
}
public IList<Company> AllowedVgs
{
get { return _allowedVgs; }
}
}
The class Company is a normal entity that is mapped to a table.
The class Month is a simple class without ID or existing mapping (Constructor and error checking removed for brevity):
public class Month
{
public int MonthNumber { get; set; }
public int Year { get; set; }
}
My database has the following two tables:
Table TAX_AUDITOR_ALLOWED_COMPANIES has only one column COMPANY_ID that is a FK to the table COMPANY and has a UNIQUE index.
Table TAX_AUDITOR_ALLOWED_MONTHS has two columns MONTH_NUMBER and YEAR. There is a UNIQUE index spanning both columns.
I would like to map TaxAuditorSettings such that I can ask my NHibernate session for an object of this type and NHibernate then should put the contents of TAX_AUDITOR_ALLOWED_MONTHS into the list TaxAuditorSettings.AllowedMonths and the companies referenced in TAX_AUDITOR_ALLOWED_COMPANIES into the list TaxAuditorSettings.AllowedCompanies.
Is this even possible? If so, how? If not, how would you do it instead?
Please note: I can change the database if necessary.
not quite what you requested for but here goes
public TaxAuditorSettings GetAuditorSettings(ISession session)
{
// assuming there is a ctor taking the enumerables as parameter
return new TaxAuditorSettings(
session.CreateSQLQuery("SELECT MONTH_NUMBER, YEAR FROM TAX_AUDITOR_ALLOWED_MONTHS")
.SetResultTransformer(new MonthResultTransformer())
.Future<Month>(),
session.CreateCriteria<Company>()
.Add(NHibernate.Criterion.Expression.Sql("Id IN (SELECT COMPANY_ID FROM TAX_AUDITOR_ALLOWED_COMPANIES)"))
.Future<Company>())
}
class MonthResultTransformer : IResultTransformer
{
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
return new Month
{
MonthNumber = (int)tuple[0],
Year = (int)tuple[1],
}
}
}
Update: saving
public void SaveOrUpdate(ISession session, TaxAuditorSettings settings)
{
using (var tx = session.BeginTransaction())
{
// get whats in the database first because we dont have change tracking
var enabledIds = session
.CreateSqlQuery("SELECT * FROM TAX_AUDITOR_ALLOWED_COMPANIES")
.Future<int>();
var savedMonths = session
.CreateSQLQuery("SELECT MONTH_NUMBER, YEAR FROM TAX_AUDITOR_ALLOWED_MONTHS")
.SetResultTransformer(new MonthResultTransformer())
.Future<Month>();
foreach (var id in settings.AllowedVgs.Except(enabledIds))
{
session.CreateSqlQuery("INSERT INTO TAX_AUDITOR_ALLOWED_COMPANIES Values (:Id)")
.SetParameter("id", id).ExecuteUpdate();
}
foreach (var month in settings.AllowedMonths.Except(savedMonths))
{
session.CreateSqlQuery("INSERT INTO TAX_AUDITOR_ALLOWED_MONTHS Values (:number, :year)")
.SetParameter("number", month.Number)
.SetParameter("year", month.Year)
.ExecuteUpdate();
}
tx.Commit();
}
}
Note: if you can change the database it would be much easier and performant to sanitise the tables
I would do it this way.
public class MonthMap : ClassMap<Month>{
public MonthMap(){
CompositeId()
.KeyProperty(x=>x.MonthNumber,"MONTH_NUMBER")
.KeyProperty(x=>x.Year);
Table("TAX_AUDITOR_ALLOWED_MONTHS");
}
}
Add a column to the COMPANY table called TaxAuditable and map it to a bool property. Update the column to be 1 where a matching row is found in TAX_AUDITOR_ALLOWED_COMPANIES. Then remove the table TAX_AUDITOR_ALLOWED_COMPANIES as it serves no real purpose.
Now you have a Company with an appropriate property on it you can query Company's where TaxAuditable is true and pass them into a method along with the Months to do your work/calculations etc... Something like this perhaps?
var taxAuditableCompanies = session.QueryOver<Company>()
.Where(x=>x.TaxAuditable==true).Future();
var months=session.QueryOver<Month>().Future();
var myService = new MyService();
myService.DoSomeWork(taxAuditableCompanies, months);

Clarification of Dapper Example Code

I'm trying to grok Dapper and seem to be missing something very fundamental, can someone explain the following code taken from the Dapper home page on Google code and explain why there is no From clause, and the second param to the Query method (dynamic) is passed an anonymous type, I gather this is somehow setting up a command object, but would like an explanation in mere mortal terminology.
Thank you,
Stephen
public class Dog {
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
public int IgnoredProperty {
get { return 1; }
}
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });
dog.Count().IsEqualTo(1);
dog.First().Age.IsNull();
dog.First().Id.IsEqualTo(guid);
The first two examples just don't do any "real" data access, probably in order to keep them simple.
Yes, there is a connection used (connection.Query(...)), but only because that's the only way to call Dapper's methods (because they extend the IDbConnection interface).
Something like this is perfectly valid SQL code:
select 'foo', 1
...it just does "generate" its result on the fly, without actually selecting anything from a table.
The example with the parameters and the anonymous type:
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });)
...just shows Dapper's ability to submit SQL parameters in the form of an anonymous type.
Again, the query does not actually select anything from a table, probably in order to keep it simple.

Categories

Resources