Flexible Methods To Add and Update Records - c#

I'm new to Entity Framework (and C# for that matter) so not sure how EF all exactly works. I have it working in my code and have watched videos, but I want to do more. I'm trying to nail down the syntax and organization of my code, but I'm having trouble and would really like some help with it. I'll give you the background.
Each day I parse a text file and sometimes I will add a record to my MySQL database or sometimes update a record. There are 3 tables in the database, but may eventually grow larger. What I first did, just to get it working was create a Records class and created separate methods that would run depending on what the format of that certain section in the text file was. So for example, "A 3284712039875817230987123", would be an Address layout and the program reads the first letter, decides on the parser method to run in the Records object and runs the method. There are about 6 different parser methods, where each parse a different "layout" in the text file. Also in the Records class they each have some helper methods.
Ok, so that's the background. Now for obvious reasons you can see that this is messy. What I would like to do is essentially create an abstract parent class Records, and have its children be each layout in the text file. So have an Address layout, Data layout, which would contain their individual parser but also inherit some methods that I use over and over like adding/updating records and other ones.
The question that I have is: How do I write the formal parameters in a method so that I can pass the EntitySet and the Entity without defining the exact EntitySet and Entity type?
For example something like this:
public void AddRecord(EntitySet entitySet, Entity entity)
{
context.entitySet.Add(entity);
context.SaveChanges();
}
I'm confused on how I should construct the method. Hope this makes sense. Let me know if I need to clarify something.
thanks,
Justin

A simple method would be add those methods to your DbContext. You don't even have to supply the EntitySet, just an entity. This code doesn't take care of the parsing part.
public void AddRecord<T>(params T[] items) where T : class
{
var set = Set<T>();
foreach (var item in items)
set.Add(item);
}
A generic remove is as easy.
public void RemoveRecord<T>(params T[] items) where T : class
{
var set = Set<T>();
foreach(var item in items)
set.Remove(item);
}

Related

How to create the ability to apply a generic data source class

This is maybe something I know how to do or have already done it in the past. For some reason I am drawing a blank on how to wrap my head around it. This is more for learning as well as trying to implement something in my app.
I am using a set of third party controls. These controls offer a lot of functionality which is great. However, I want to be able to create a custom object that handle the logic/properties for the datasource of this control.
For example, there is a spreadsheet like object that I am using. You supply the spreadsheet like object some data and it pulls in your data. The problem here is that you need to set the columns, their data types, and other formatting/events as well as some logic to spit the data back to the user.
List<CustomClassWithProperties> dataSource
The custom class has some properties that will be translated to the columns. Like ProductName, Price, SalesDepartment, DatePurchased etc. This can be done by supplying the spreadsheet the columns and their data types each time. I want to be able to create a helper class that you just supply a list, a visible column list, and an editable column list and the data will fill in without any other issues.
Using the above list, I would imagine something similar to this:
DataHelperClass dtHlpr = new DataHelperClass(List<CustomClassWithProperties> data, List<string> visibleColumns, List<string> editableColumns)
This data helper class will take the data input list as the spreadsheet data source. It would then take the visibleColumns list and use that to set the visible rows, same for editableColumns.
Where I am running into a mental block (long week) is when I want to be able to reuse this. Let's say I have a List that has completely different properties. I would want my constructor for the data helper to be able to handle any List I send to it. Looking at whatever code I can get to for the third party controls, it appears that their data source is of type object.
Could someone point me in the right direction? I am thinking it has to do with generics and some interface implementation. I just honestly cannot think of where to start.
You can make the class itself generic:
public class DataHelperClass<T>
{
public DataHelperClass(List<T> data, ...) { ... }
}
DataHelperClass<CustomClassWithProperties> dtHlpr = new DataHelperClass<CustomClassWithProperties>(List<CustomClassWithProperties> data, List<string> visibleColumns, List<string> editableColumns)
You'd then perform your reflection against typeof(T).
I'd also be tempted to use IEnumerable<T> rather than List<T> if possible, but that's a matter of preference, more or less.
This is similar to using a simple List<object>, except that it enforces that all objects in the list inherit from the same type (which might well be object), so you get some more type-checking than you otherwise would.
You mentioned interfaces, I don't see any reason here to include that (from what you've told us, at least), but you can certainly make a generic interface via the same syntax.

C# Generic Table from DataContext

I'm attempting to create a generic WPF form or page, that when called, will load in data from a LINQ table. Here is the concept:
I have three tables in a LINQ DataContext that are identical (apart from the data within)
TypeID and Type are the columns
I would like to generically pass that data in those tables into my second form depending on which table the user selects (essentially so they can narrow down the list of objects of said Type.
I've seen some responses, (in particular the accepted answer to this one LINQ query with a generic table) that are very close to what I am looking for, but not quite. One issue I have with the above answer is that T must be a reference type.
I've done more searching and found some more answers like:
someClass<T> : <T> where T
But unfortunately these are further from my own context and I am unable to bridge the two concepts of what is happening. Below I have posted what I hope to do.
someDataContext db = new someDataContext();
private void pageLoader<T>(){
newPage n = new newPage(T) //This is where I was hoping I could pass the table(s) to the constructor.
}
And here is the constructor:
newPage(T){
listBox l = new listBox();
l.datasource = T;
}
Any assistance in any direction would be helpful (besides MSDN, please. I've been there and I'm still lost.)
Let start from the top. LINQ is merely an abbreviation for Language Integrated Query. It is interchangeable with Lambda. Different syntax but both accomplish the same task. Querying a collection or datasource. See http://msdn.microsoft.com/en-ca/library/bb397926.aspx
You are referring to the EntityFramework Code First approach of creating a database. LINQ is merely a way to access and manipulate the information within.
With that out of the way, what you are pointing out is a Generic Method and a Generic Class. T is simply a standard naming convention for a generic type. You could use any representation you like. If you are going to be passing in entities, you might use TEntity for example.
See http://www.dotnetperls.com/generic-method
http://www.dotnetperls.com/generic
When you see someClass where T, this is a constraint for type parameters.
And finally, what you have been waiting for...
https://codereview.stackexchange.com/questions/19037/entity-framework-generic-repository-pattern
The following should put you on the right path.
http://blog.gauffin.org/2013/01/repository-pattern-done-right/ <- This would be more of a better starting tutorial

Getting A field or Entity Generically Based on a String Value

I am trying to write a wrapper function for an application.
This application will continually add entities so it would be better if we could write one generic function rather than have to carve out an exception for each item.
For certain reasons we maintain both a GUID and an int key.
When an int key gets updated, we need to update it both in the parent record the child records but since it is possible that at any given time there could be multiple child records with the same int key, we need to get a list of Guids of what we want to update.
Here is some psuedo code of what I am trying to do.
List<string> depenedents = new List<string>();
depenedents.add(table1);
depenedents.add(table2);
depenedents.add(table3);
for(item in depenedents)
{
context.set<type item>();
entities.getguid();
}
Obviously the issue here is with the for loop.
Is it possible to get a list of entities knowing only the string of the entity type? Luckily all of our entities are wrapped to a base class that has a get guid method, but I need to find a way to actually get the entities.
Any help is greatly appreciated.
Something like:
for(item in depenedents)
{
context.GetMethod("Set")
.MakeGenericType(Type.GetType(item))
.Invoke(context, new object[0]);
entities.GetType.GetMethod("GetGuid").Invoke(entities, new object[0]);
}
...should be with roughly what you need. This will invoke the correct type specialisation of the Set generic instance method. Then it will invoke the instance method called "GetGuid" on the entities object.
Or possibly:
foreach(var entity in entities)
{
entity.GetType.GetMethod("GetGuid").Invoke(entities, new object[0]);
}
You'll maybe want to do something with the values returned, but hopefully this answer will point you in the right direction!
(and clearly you could optimise this code substantially to cache reflected types and methods, or to use compiled expressions rather than Invoke() calls)

how to iterate through reader for notnull without repeating code over and over

First off, I am new to programming (especially with C#) and thanks for your help.
I have a static web form with about 150 form objects (most checkboxes). I decided to go 1 record per form submission in the sql db. So, for example, question X has a choice of 5 checkboxes. Each of these 5 checkboxes has a column in the db.
I have the post page complete(working) and am building an edit page where I load the record and then populate the form.
How I am doing this is by passing a stored proc the id and then putting all the returned column values into the according object properties, then setting the asp control object to them.
An example of setting the asp controls to the selected value:
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
As you see, this is very tiresome since there are over 150 of these to do. Also, I just found out if the response is NULL then I get the error that it cant be converted to a string. So, I have added this line of code to get past it:
This is the part where I am populating the returned column values into the object properties (entity is the object):
if (!String.IsNullOrEmpty((string)reader["questionX0"].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX0"];}
So, instead of having to add this if then statement 150+ times. There must be a way to do this more efficiently.
First of all, it seems that you are using string.IsNullOrEmpty(value), but this won’t check for the special DBNull value that is returned from databases when the data is null. You should use something more akin to value is DBNull.
The rest of your problem sounds complex, so please don’t be put off if my answer is complex too. Personally I would use custom attributes:
Declare a custom attribute
The following is a skeleton to give you the idea. You may want to use the “Attribute” code snippet in Visual Studio to find out more about how to declare these.
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class QuestionColumnAttribute : Attribute
{
public string ColumnName { get; private set; }
public QuestionColumnAttribute(string columnName)
{
ColumnName = columnName;
}
}
Use the custom attribute in the entity class
Where you declare your entity class, add this custom attribute to every field, for example where patientUnderMdTreatment is declared:
[QuestionColumn("questionX0")]
public string patientUnderMdTreatment;
Iterate over the fields
Instead of iterating over the columns in the reader, iterate over the fields. For each field that has a QuestionColumnAttribute on it, get the relevant column from the reader:
foreach (var field in entity.GetType().GetFields())
{
var attributes = field.GetCustomAttributes(typeof(QuestionColumnAttribute), true);
if (attributes.Length == 0)
continue;
object value = reader[attributes[0].ColumnName];
if (!(value is DBNull))
field.SetValue(entity, value.ToString());
}
For the first part of your question where you set the ASP controls, you can use a similar strategy iterating over the fields of selectedForm, and this is probably simpler because you don’t need a custom attribute — just take only the fields whose name starts with “questionX”.
this is a quick & easy way of doing it.. there are some suggestions to investigate LINQ, and I'd go with those first.
for (int i = 0; i < 150; i++)
{
if (!String.IsNullOrEmpty((string)reader["questionX" + i.ToString()].ToString()))
{entity.patientUnderMdTreatment = (string)reader["questionX" + i.ToString()];}
}
... though this wouldn't be any good with the
questionX.Items[0].Selected = selectedForm.questionX0
questionX.Items[1].Selected = selectedForm.questionX1
questionX.Items[2].Selected = selectedForm.questionX2
lines
so I hear two questions:
- how to deal with null coming from IDataReader?
- how to deal with multiple fields?
Lets start with simple one. Define yourself a helper method:
public static T IsDbNull<T>(object value, T defaultValue)
{
return (T)(value is DBNull ? defaultValue : value);
}
then use it:
entity.patientUnderMdTreatment = IsDbNull<string>(reader["question"], null);
Now how to map entity fields to the form? Well that really is up to you. You can either hardcode it or use reflection. The difference of runtime mapping vs compile-time is likely to be completely irrelevant for your case.
It helps if your form fields have identical names to ones in the DB, so you don't have to do name mapping on top of that (as in Timwi's post), but in the end you'll likely find out that you have to do validation/normalization on many of them anyway at which point hardcoding is really what you need, since there isn't a way to dynamically generate logic according to the changing spec. It doesn't matter if you'll have to rename 150 db fields or attach 150 attributes - in the end it is always a O(n) solution where n is number of fields.
I am still a little unsure why do you need to read data back. If you need to preserve user's input on form reload (due to validation error?) wouldn't it be easier/better to reload them from the request? Also are entity and selectedForm the same object type? I assume its not a db entity (otherwise why use reader at all?).
Its possible that there are some shortcuts you may take, but I am having hard time following what are you reading and writing and when.
I recommend using the NullableDataReader. It eliminates the issue.

Copying from EntityCollection to EntityCollection impossible?

How would you do this (pseudo code): product1.Orders.AddRange(product2.Orders);
However, the function "AddRange" does not exist, so how would you copy all items in the EntityCollection "Orders" from product2 to product1?
Should be simple, but it is not...
The problem is deeper than you think.
Your foreach attempt fails, because when you call product1.Orders.Add, the entity gets removed from product2.Orders, thus rendering the existing enumerator invalid, which causes the exception you see.
So why does entity get removed from produc2? Well, seems quite simple: because Order can only belong to one product at a time. The Entity Framework takes care of data integrity by enforcing rules like this.
If I understand correctly, your aim here is to actually copy the orders from one product to another, am I correct?
If so, then you have to explicitly create a copy of each order inside your foreach loop, and then add that copy to product1.
For some reason that is rather obscure to me, there is no automated way to create a copy of an entity. Therefore, you pretty much have to manually copy all Order's properties, one by one. You can make the code look somewhat more neat by incorporating this logic into the Order class itself - create a method named Clone() that would copy all properties. Be sure, though, not to copy the "owner product reference" property, because your whole point is to give it another owner product, isn't it?
Anyway, do not hesitate to ask more questions if something is unclear. And good luck.
Fyodor
Based on the previous two answers, I came up with the following working solution:
public static void AddRange<T>(this EntityCollection<T> destinationEntityCollection,
EntityCollection<T> sourceEntityCollection) where T : class
{
var array = new T[sourceEntityCollection.Count()];
sourceEntityCollection.CopyTo(array,0);
foreach (var entity in array)
{
destinationEntityCollection.Add(entity);
}
}
Yes, the usual collection related functions are not there.
But,
1. Did you check CopyTo method?
2. Do you find any problem with using the iterator? You know, GetEnumerator, go through the collection and copy the entities?
The above two can solve your problems. But, I'm sure in .NET 3.0+ there would be compact solutions.
My answers are related to .NET 2.0

Categories

Resources