I have a scenario where I need to use tabular data which is unlikely to change but which helps implement business logic:
Table
Parent Child Relationship Allowed
A B C True
B A C False
Each row represent a business rule. Based on this table I need to populate a dropdown control with the contents of the "Relationship" column. Can I have some sort of data structure within C# to store this tabular data, or do I need to use a a database table?
Unless you need to persist this data across sessions, you don't need to store it in a database table. From the perspective of Object Oriented design, why not make an object, and therefore class, that represents the structure you need. Something like this:
public class Relationship
{
public string Name { get; set; }
public string Parent { get; set; }
public string Child { get; set; }
public bool Allowed { get; set; }
}
Note of course that I'm using strings where you might want to use further objects of their own 'type'. Additionally, keep in mind access, and what should and shouldn't be allowed to access these properties... This is example is intentionally simple at this point!
I would agree that a full blown database may be overkill if this is all the data you need to store, so you could theoretically hardcode the data and structure in C# if you really wanted to, but not a good idea in almost all cases - even if it is unlikely to change.
At a minimum store the data in a little XML / config file so that IF it does change, you do not need to recompile the application.
Use DataTable: http://msdn.microsoft.com/en-us/library/System.Data.DataTable.aspx
You can create your Table and fill it like this:
// Definition
DataTable table = new DataTable();
DataColumn column = new DataColumn("Parent", typeof(string));
table.Columns.Add(column);
// Data
DataRow row = table.NewRow();
row["Parent"] = "A";
table.Rows.Add(row);
Related
I have already been working with linq in the past and I know how to access a database with SqlConnection and SqlCommand. Today I wanted to work with LinqToSql and see if and how I can make reading from and writing to a database easier. I did this Walkthrough.
Here is the code for the Customer Class (I changed it a bit but it still works perfectly fine):
[Table(Name = "Customers")]
class Customer
{
[Column(IsPrimaryKey = true)]
public string CustomerID { get; set; }
[Column]
public string City { get; set; }
}
And the code from Main:
class Program
{
static void Main(string[] args)
{
DataContext db = new DataContext(#"Data Source=(local)\SQLEXPRESS;Initial Catalog=Northwind;User ID=sa;Password=xxx");
Table<Customer> customers = db.GetTable<Customer>();
IQueryable query = from cust in customers
//where cust.City == "London"
select cust;
foreach (Customer customer in query)
Console.WriteLine("ID:{0}; City={1}", customer.CustomerID, customer.City);
Console.ReadKey();
}
}
It worked and I'm happy since this makes accessing a database so much easier for me. But there are still a couple of things that concern me:
It seems like for every column I want to include I need to create a new property in the Customer class and add a [Column] above it.
I need to create a new Class for every table that I want to read from (for example Customer, Orders and Suppliers in the Northwind DB).
First of all this seems to be quite a lot of unnecessary and repetitive work. Am I doing something wrong here?
Also I want the user to type in the name of a database and a table. This means I don't know yet which database he will pick and I also don't know the structure of the table yet. I can't create the class yet that is supposed to represent the table.
This means I will need to:
Create a type / class / object dynamically. I can't use Table<Customer> customers = db.GetTable<Customer>() because I don't know the type yet. The type will be the dynamically created class.
Mark the type / class / object as a table with [Table(Name="xxx")].
Read the columns from the SqlTable and create for every column a property and mark it with [Column].
After I'm done with this I can get the table with Table<T> table = db.GetTable<T>(), execute the query and display the datarows.
My second (and more important) question is: How can I do this? Does anyone have code examples or links to share? Or is this approach wrong?
You can store the result in dynamic object just like this.
dynamic table = db.GetTable<T>()
and use reflection to get type of the object.
I'm currently working on an app using Asp.Net MVC and C#. One of the requirement is to check what process the item is and then to only show the appropriate div. So I've decided to create a Table in the Db which consist of:
Id ProcessDescription DivOneVisible DivTwoVisible
1 Approved True False
2 Analysis True True
...
NOTE - The Id's and ProcessDescription will never change
Currently the table only holds 10 rows of data but the idea is, in future more rows/columns can be added.
I then go ahead and create the appropriate methods, one for each Div as follows
public bool ShowDivOne(int id)
{
var data = uow.GetRepository<ItemProcess>().GetById(id);
bool showDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
if (showDivOne)
return true;
else
return false;
}
I use the same code as above for ShowdivTwo() method, but match the different column. Then in the view I do
#if(ShowDivOne){//div one code here}
#if(ShowDivTwo){//div two code here}
This works but I was wondering if there is a more generic way where I can write one method which will cover each scenarios even if new columns or rows are added.
The main thing you still need to have a mapping between Database and ViewModel somewhere. At the moment it is hard coded in your methods.
You can make it absolutely generic if you start use reflection and have a mapping array with properties name. But I would nt recommend doing it as it over complication and hard to maintain and change.
(If you want I can go into details of implementation).
For you example I would suggest to have a viewmodel per item, that contains properties of divs to display.
public class ProcessViewModel
{
public int Id {get;set;}
public bool ShowDivOne {get;set;}
public bool ShowDivTwo {get;set;}
ProcessViewModel(){}
ProcessViewModel(ItemProcess data){
Id = data.Id;
ShowDivOne = data.DivOneVisible.HasValue ? data.DivOneVisible.Value : false;
ShowDivTwo = data.DivTwoVisible.HasValue ? data.DivTwoVisible.Value : false;
}
}
You still query for each item individually or query them altogether and pass data to viewmodel to construct it.
And a simple foreach on a view to traverse through the list of viewmodels.
Extending it to contain more properties would be very easy and strait forward, with minimum code to maintain.
I'm using LINQ to SQL and have a database table called Product with 20 columns. The Product table is mapped to the Product class in the LINQ to SQL metadata.
I'd like to use my dbContext and retrieve some product records but only populating 10 columns not all 20 columns.
How would that be possible to specify which columns should be returned/populated with LINQ to SQL (or EF)?
I know one way would be using stored procedures but that's this question is about.
Thanks,
You usually use an anonymous class for that:
db.Products.Where(... filter ...).Select(item => new
{
Field1 = item.Field1,
Field2 = item.Field2,
});
Only the fields you include will be selected. If you intend to pass this data structure to other functions or return it, you need a concrete class definition for sub field set, such as:
class SmallerEntity
{
public something Field1;
public something Field2;
}
And you can initialize this in your Select statement:
db.Products.Where(... filter ...).Select(item => new SmallerEntity
{
Field1 = item.Field1,
Field2 = item.Field2,
});
I don't recommend the practice of half-populating an existing class. That makes your state space unnecessarily complex and allows more bugs in your code. Try to contain smaller subsets of data in their own classes.
I have a C# .NET 3.5 project using a MySQL database.
I have an object Task which I would like to be able to create by pulling it from a series of database tables.
public class Task
{
public Task()
{
Values = new List<string>();
OtherValues = new List<string>();
Requirement = string.Empty;
Minimum = 1;
Children = new List<Foo>();
}
public IList<string> Values { get; set; }
public IList<string> OtherValues { get; set; }
public string Requirement { get; set; }
public int Minimum { get; set; }
public int Maximum { get; set; }
public IList<Foo> Children { get; set; }
}
I'd like to be able to get the tasks from a TaskList which would lazily read elements of the task as they were accessed by an enumerator.
public class TaskList : IEnumerable<Task>
{
/* ... */
public IEnumerator<Task> GetEnumerator()
{
string query = #"SELECT my_task.*, `Order` FROM my_task ORDER BY `Order` DESC";
using (MySqlConnection connection = new MySqlConnection(connection_string_))
using (MySqlCommand command = connection.CreateCommand())
{
command.CommandText = query;
connection.Open();
using (MySqlDataReader reader = command.ExecuteReader())
{
yeild /* ??? */
}
}
}
}
How is this done?
You can serialize it to XML and store it as a string. Add the following function to Task:
public XElement Serialize()
{
return new XElement("Task",
new XElement("Values",from val in Values select new XElement("Item",val)),
new XElement("OtherValues",from val in OtherValues select new XElement("Item",val)),
new XElement("Requirement",Requirement),
new XElement("Minimum",Minimum),
new XElement("Maximum",Maximum)
);
}
You will need to put using System.Linq; and using System.Xml.Linq; in the top of the .cs file.
I didn't write the code to serialize Children because I don't know what the data type Foo looks like, but you should serialize it in a similar manner. After you've done that, you can easily write the XML to the database, and read it back(write a constructor that parses the Xml into a Task object)
EDIT(addition):
Here is an example to a constructors that receives XML(or parse a string as XML):
public Task(string xmlSourceAsString):
this(XElement.Parse(xmlSourceAsString))
{
}
public Task(XElement xmlSource)
{
Values=(from itm in xmlSource.Element("Values").Elements("Item") select itm.Value).ToList();
OtherValues=(from itm in xmlSource.Element("OtherValues").Elements("Item") select itm.Value).ToList();
Requirement=xmlSource.Element("Requirement").Value;
Minimum=int.Parse(xmlSource.Element("Minimum").Value);
Maximum=int.Parse(xmlSource.Element("Maximum").Value);
}
EDIT(explanation):
You can't store your object as is in the database "as is", because it refers to other objects. For example - the list Values doesn't sit in the same place in memory as the rest of the object, befause it's a ref type - it refers to another object that sits in a different place in the memory. In matter of fact, the only parts of your object that are stored in the same place as the main object are the Minimum and Maximum, which are ref types, so if you could somehow store the object as is(laziest solution possible, if it worked), you would get your Minimum and Maximum fields right, but all other fields will point to the memory addresses where those objects where placed when you stored the Task object, which are now most likely invalid pointers(and I say "most likely" because it is also possible(though rare) that they will point to legitimate objects, maybe event of the same type - but they still won't have your data.
If you want the object with all it's data stored in a database(or in a file. or passed to a proccess that runs on another computer via network) you have to serialize it. Performance-wise the best way is to serialize it to binary(C# have some tools for that, but it's still more complex than XML).
Xml also have the adventage of being easily readable from most modern programming languages and database engines. MySQL has some functions to read and write XML, so you can update the object in the database and access it's fields from MySQL queries.
Conclusion
You asked for a solution that is easy(lazy), efficient, and sql-compatible(access to the object's fields from MySQL queries). I say you can only have two of your three requirements, but you can choose which two:
If you want something easy and efficient, even at the price of loosing compatibility, serialize your objects to binary. True, it's not as easy as XML, but .NET has some tools to help you with that.
If you want something efficient and compatible, and willing to do some work for that, you can put your object in MySQL the way databases are meant to be used - use separate tables for the lists that refers to the objects via OIDs, etc. This will require some work, but after you add the tables and code the MySQL functions and the C# functions that handle everything, you should be able to store, retrieve, and access your objects with ease.
If you want something easy and compatible, and you can afford loosing some efficiency, use my solution and serialize your objects to XML. This is the laziest solution - unless someone knows a library that can automatically serialize any object, LINQ to XML is the easiest way to do it, and requires much less code than any other solution.
public interface IRule
{
bool Check(string input);
}
I have an interface that defines a rule. A rule is just a generic business rule or constraint that a user can create. So I have two sample rules:
public class ContainsRule : IRule
{
public string MustContain { get; set; }
public bool Check(string input)
{
return input.Contains(this.MustContain);
}
}
public class LengthRule : IRule
{
public int MaxLength { get; set; }
public bool Check(string input)
{
return input.Length <= this.MaxLength;
}
}
Rules may have more than one property that can be set, but in this example, each of these rules only have one property.
A user can create their own set of rules that should be saved. For example, a user have these three rules:
IRule[] rules = new IRule[]
{
new ContainsRule { MustContain = "foo" },
new ContainsRule { MustContain = "bar" },
new LengthRule { MaxLength = 5}
};
I need to persist this information to a database or some data store for each user. Since each user can have their own set of rules, I'm not sure what the database tables should look like.
User | ClassName | Parameters
-----------------------------------------------
1 | Namespace.ContainsRule | MustContain:foo
1 | Namespace.ContainsRule | MustContain:bar
1 | Namespace.LengthRule | MaxLength:5
My initial guess would be to create a table that looks something like the above, where parameters should a string. This means I would need to parse out the information and use reflection or something to set the properties. I would need to use the activator to create the class using the ClassName column. Another suggestion was instead of creating a delimited string for all the properties, there would be another table. Each of the properties would be its own row that has a foreign key relationship back to a row in the table above.
However, both examples don't seem to be the best way of saving these rules. Is there a better way to do this?
Why not use XML serialisation. The database table would contain the User # and the XML serialisation of the indvidual rule.
I believe that you are retrieving from database.
If so, try my suggestion.
Store the retrieved data into datatable, or convert them to datatable before applying the rule.
So the rule can be put as normal sql in datatable's filter property.
Instead of rule name and parameters, you can change it to a column containing
column like '%foo%'
Then apply this on the datatable's filter.
PS: I have converted List to Datatable and used such filter before
It seems to me, simply put, you need to map your objects to SQL tables (if only there was a technology that could to do this automatically... :)
Based on what you have posted, I envisage at least six SQL tables, more if there are further subclasses of rule e.g.
Rules
MustContainTextRules (subclass of Rules, 1:0..m)
MaxLengthRules (subclass of Rules, 1:0..1)
[Possibily further subclasses of Rules e.g. MinLengthRules?]
Rulesets ("a set of Rules")
Users
RulesetOwnership (relationship table between Users and Rulesets)
Whether they are distinct tables may be influenced by whether the rules are interrelated e.g. if the MinLengthRule value for a Ruleset cannot exceed its MaxLengthRule value then you may find it difficult to write a SQL constraint to validate this when they are in different tables.