Is this possible with IList - c#

I have a public class called Profile. Very simple model class currently with 2 properties; string Name and string Fields. As I develop the project the class will expand but it's not particularly important at the moment.
I have a Global static IList of type Profile called Profiles. I am quite new to manipulating the data in these IEnumerable types but I am looking to update one of the properties of a single profile. I have tried the following but I am receiving an object reference not set exception. The following is where I set the property:
Profiles.Single(x => x.Name == listBoxProfiles.Text).Fields = textBoxFieldName.Text;
The debugger is showing the listbox and textbox text properties both have the correct values so I think that it is the way I am using single that is wrong.
If anyone could shed some light I would be grateful.

A simple amendment to make the code more defensive is all that is required:
var profile = Profiles.SingleOrDefault(x => x.Name == listBoxProfiles.Text);
if (profile != null)
{
profile.Fields = textBoxFieldName.Text;
}
else
{
Profiles.Add(new Profile(textBoxFieldName.Text));
}
This code will cope with missing values, SingleOrDefault expects 0 or 1 items to be returned. It will throw an exception if more than 1 items are found.
If you know your code should always have the item you are looking for, then your code will work - but I'd advise against this style of programming in favour of being a little more defensive.

Related

Best way to access attributes

I am working on a framework that uses some Attribute markup. This will be used in an MVC project and will occur roughly every time I view a specific record in a view (eg /Details/5)
I was wondering if there is a better/more efficient way to do this or a good best practices example.
At any rate, I have an a couple of attributes e.g:
[Foo("someValueHere")]
String Name {get;set;}
[Bar("SomeOtherValue"]
String Address {get;set;}
What is the most efficient way/best practice to look for these attributes/Act on their values?
I am currently doing something like this:
[System.AttributeUsage(AttributeTargets.Property)]
class FooAttribute : Attribute
{
public string Target { get; set; }
public FooAttribute(string target)
{
Target = target;
}
}
And in my method where I act on these attributes(simplified example!):
public static void DoSomething(object source)
{
//is it faster if I make this a generic function and get the tpe from T?
Type sourceType = source.GetType();
//get all of the properties marked up with a foo attribute
var fooProperties = sourceType
.GetProperties()
.Where(p => p.GetCustomAttributes(typeof(FooAttribute), true)
.Any())
.ToList();
//go through each fooproperty and try to get the value set
foreach (var prop in fooProperties)
{
object value = prop.GetValue(source, null);
// do something with the value
prop.SetValue(source, my-modified-value, null);
}
}
Attribute.GetCustomAttribute and PropertyInfo/MemberInfo.GetCustomAttribute is the recommended way of getting at attribute objects.
Although, I wouldn't normally enumerate all properties with attributes; you generally want to work a particular attribute so you'd just call GetCustomAttribute directly.If you're looking for attributes on any of your properties, enumerating those properties looking for attributes based on GetCustomAttribute() the way you're doing it, is the best way to do it.
There is not really much choice when dealing with attributes - your code is ok and reasonable as is, it is also unlikley to be your main performance concern. The only immediate thing is to drop ToList call as absolutely unnecessary.
Side notes: performance related question should look approximately
"I've measured my code and portion XXX seems to be taking too much time (YYY) . The time goal for this piece of code is ZZZ. Is my way of doing XXX reasonable/where can I improve it?".
Note that in you case you are missing YYY and ZZZ time portions - so you can't really say if it is slow for your case or not. And you may want to start measurements with DB/other IO bound operations as it more likely to speed up your overall code.
After you figured that this attribute related code is main perfomance issue you can consider some sort of caching of results or even code generation of some sort (either through caching lambdas that would set necessary values or even full blown IL generation).

Understanding how a property of type List<> works

I got to think I don't understand how it works.
My specific question is: Why am I allowed to set the value of a list property element when I have no setter and no backing list variable?
Let me explain. Let's say I have a CustomerTable class with:
public List<string> Name
{
get
{
var names = new List<string>();
foreach (CustomerRow row in Rows)
{
name.Add(row.Name);
}
return names;
}
}
The idea is to have a read-only property show the contents of a column without duplicating data in my class, since I already have a list of rows.
Anyway, my surprise comes when pieces of code like the following one are accepted by Visual Studio without claiming any kind of error (and it even allows me to compile without errors):
Name[0] = "John";
I can't understand why this is legal. My property has no set { }, and it doesn't even have a backing list to modify. What is this piece of code supposed to do?
Shouldn't it work like a method? Is there really a stored list other than the one I generate each time someone "gets" it?
(I can give more details on demand and will also be grateful for any other remarks)
You are not setting the property, rather you are getting the property (which is a list) and then operating on it (in your example, changing its first member). If you were to try:
Name = new List<string>();
You would get the compilation error you were expecting to get. Note that since you are creating a new list every time, your Rows property remains read-only (assuming it is not exposed somewhere else). If you want to make it clear that changes to your returned collection are meaningless, you can change the type of the Name property to IEnumerable<string>:
public IEnumerable<string> Name
{
get
{
return Rows.Select(row => row.name); //LINQ is more elegant here
}
}
In your example you don't set Name (which is read-only, indeed), but you set the first list element contained in Name, which is Name[0], and there's no reason why you could not do that since List<string> is an object type which allows to set elements.

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.

Null Reference Exception in a Dynamic LINQ Expression

I am using the Dynamic Linq Library / Sample from Microsoft to do ordering on a list. So for example I have the following C# code:
myGrid.DataSource=repository.GetWidgetList()
.OrderBy(sortField + " " + sortDirection).ToList();
I have a case where my Object have a 0:1 relationship with another object, which has a property that might be displayed in the grid. When we try and sort this, it works fine so long as all my Widgets have this child. We are ordering by Child.Name for example. When Child is null however, we get the null reference exception.
I have some options here which I know I could select into an anonymous type and bind to that, I could also expose the Child.Name on the parent object and handle this via code (Which I don't like comprising my object model for this).
In an ideal world I'd like to update the library to handle this case. Before I dive into it, I'm wondering if anyone has ran across this or not and has a solution already?
Edit
Looks like I didn't explain well enough. I am using the Dynamic Linq Library which comes with the C# samples. This library adds some nice extensions that let you use a string inplace of a lambda expression So my code is actually something like this:
private void BindGrid(sortField,sortDirection)
{
this.grid.DataSource=....OrderBy("MyField ASC")....
}
Of course the string there is replaced with the parameters. But this allows us to change the sorting dynamically as the user clicks on a grid header. We don't have to if then else logic to handle all the permutations.
My solution as I documented bellow changes my nice clean method into:
private void BindGrid()
{
var sortField=this._sortField;
if (sortField=="Child.Name")
{
sortField="iif(Child==null,null,Child.Name)";
}
this.grid.DataSource=repository.GetWidgetList()
.OrderBy(sortField + " " + this._sortDirection)
.ToList();
}
And while this works, this now means I have to update this code as we add new fields or properties which we want to expose in the grid which are on a child object.
If I understand you correctly, I think you want this:
repository.GetParentObjects()
.OrderBy(p => p.Child == null ? "" : p.Child.Name);
LINQ will be able to generate SQL that mimics this expression.
On solution I've found which in my case is not ideal again would be to detect when the expression is going to access the child, to change the sort expression to be
iif(Child == null,null,Child.Name) ASC
Ideally this logic can be baked into the dynamic library, I'd rather not have to modify each grid all over the place to handle all the cases this will impact.
I had the same issue but the best solution I found was to make your code a little more generic by changing it into this:
private void BindGrid()
{
var sortField = this._sortField;
var splitted_sortField = this._sortField.Split(new char[]{'.'}, StringSplitOptions.RemoveEmptyEntries);
if (splitted_sortField.Length > 1)
{
sortField = "iif("+splitted_sortField[0]+"==null,null,"+sortField+")";
}
this.grid.DataSource = repository.GetWidgetList()
.OrderBy(sortField + " " + this._sortDirection)
.ToList();
}
Not perfect, won't like give access to childs of childs, but saves u from updating your code every time u get a new nullable child.
I don't really understand the problem (maybe because it is friday evening here already...), but can't you sort the list like this:
myGrid.DataSource=repository.GetWidgetList()
.OrderBy(w => w.SortField).ToList();
where SortField is the property you want to sort on.
This should work even when the value is null...
Sorry if it's maybe completely beside the point...

FindAll in a list of custom objects

Well, I got an object called Mamamia and inside of it has some string properties. I created a list of this object and populated it with 150 items.
I'm trying to use List.FindAll but I reaaally don't know how to do it. I've tried this way:
produto = products.FindAll(delegate(Mamamia cv) {return cv.LocalPackage.Remove(1,21) == cmbPackage.SelectedValue};
I don't know why the delegate is there, I just tried to copy from some other code on the internet.
Thanks in advance!
The delegate is there to see whether the value that you're testing is what you're looking for. The call to Remove looks worryingly like it's mutating the value though - that's rarely a good thing when you're looking through the list. I guess if it's a string then it's not too bad, although it may not be what you're after...
What are the types involved, and what are you looking for? Oh, and are you using C# 3 and/or .NET 3.5? That would make it easier (even C# 3 against .NET 2.0 means you could use a lambda expression instead of an anonymous method).
What's happening when you run the code at the moment? If it's just not finding anything, it may just be because you're testing for reference equality (if SelectedValue returns object).
Try this:
produto = products.FindAll(delegate(Mamamia cv) {
return cv.LocalPackage.Remove(1,21).Equals(cmbPackage.SelectedValue);
});
EDIT:
It sounds like you only want a single value, and if you're using .NET 3.5 it would be more idiomatic to use LINQ in the first place. I would use:
string selectedText = (string) cmbPackage.SelectedValue;
Mamamia item = products.FirstOrDefault
(cv => cv.LocalPackage.Remove(1,21) == selectedText);
if (item != null)
{
// Found it; otherwise item will be null
}

Categories

Resources