I'm using MVC 3 and attempting to get fields left blank to be sent to the database as zero-length strings, instead of nulls. Is this possible to do with data annotation attributes?
If not, what is the most appropriate place to convert from nulls? Is it during model validation?
While not ideal, this is the best way I know of: [DisplayFormat(ConvertEmptyStringToNull = false)] above the property. It keeps the logic in the model, which is good practice, and it directly addresses the issue. It's just a bummer that this is necessary.
private string _summary = "";
[Required]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public virtual string Summary
{
get { return _summary; }
set { _summary = value; }
}
I wouldn't do this in a validator, but probably in model binding instead (or even in the model itself).
Often, in my model classes, I set my string properties to default to an empty string, and in their setters, I convert nulls to empty strings.
It's kindof a pain to write this repetitive stuff over and over, but it's so much nicer to not have to deal with nulls.
Set the property equal to string.empty in the constructor.
Or, though this is a little more costly you could make an extension method that does the following and just call it in the constructor:
var stringPropertyInfos = GetType()
.GetProperties(BindingFlags.Instance|BindingFlags.Public)
.Where(p => p.PropertyType == typeof(string));
foreach(var propertyInfo in stringPropertyInfos){
propertyInfo.SetValue(this,string.Empty,null);
}
Related
Using Orchard CMS, I am dealing with a record and a part proxy, but cannot figure out how to save it into the DB. In fact, I confess I don't even know how to get the items I'm trying to save into this paradigm. I was originally using enum's for choices:
MyEmum.cs:
public enum Choices { Choice1, Choice2, Choice3, Choice4 }
MyRecord.cs:
public virtual string MyProperty { get; set; }
MyPart.cs:
public IEnumerable<string> MyProperty
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyProperty)) return new string[] { };
return Record
.MyProperty
.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set { Record.MyProperty = value == null ? null : String.Join(",", value); }
}
Now, in my service class, I tried something like:
public MyPart Create(MyPartRecord record)
{
MyPart part = Services.ContentManager.Create<MyPart>("My");
...
part.MyProperty = record.MyProperty; //getting error here
...
return part;
}
However, I am getting the following error: Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable<string>'
Essentially, I am trying to save choices from a checkboxlist (one or more selections) as a comma-separated list in the DB.
And this doesn't even get me over the problem of how do I use the enum. Any thoughts?
For some background:
I understand that the appropriate way to handle this relationship would be to create a separate table and use IList<MyEnum>. However, this is a simple list that I do not intend to manipulate with edits (in fact, no driver is used in this scenario as I handle this on the front-end with a controller and routes). I am just capturing data and redisplaying it in the Admin view for statistical/historical purposes. I may even consider getting rid of the Part (considering the following post: Bertrand's Blog Post.
It should be:
part.MyProperty = new[] {"foo", "bar"};
for example. The part's setter will store the value on the record's property as a comma-separated string, which will get persisted into the DB.
If you want to use enum values, you should use the Parse and ToString APIs that .NET provide on Enum.
SLIGHT UPDATE BELOW
I am trying to use the [Description] data annotation attribute with enums in order to display a friendly name. I've searched around a lot and cannot get anything implemented. Right now I have code that will display an enum as a string (using an extension), but I am not liking ThisIsAnEnum as an enum name (which is spaced out by the string extension) and it prohibits me from having longer names (which I need to maintain) such as for a radio button item. My goal is to have longer descriptions for radio button items without having to write really long enums. An extension/helper will probably be the right way to go, but I need to "fit" it into the code I am using, which is where I failed using the many examples out there.
The code I am using is generic, in that depending upon some logic either a radio button list, check box list, drop down list, select list or regular text boxes are displayed. For multi-item lists enum's are used, and the enum name is what is displayed (after using the string extension).
Here is the particular code that displays the enum:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = name.ProperCase(),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
ProperCase is the class that changes the enum to something readable.
I found something that almost worked:
public static string GetEnumDescription<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
return attributes[0].Description;
else
return value.ToString();
}
in which case I changed code from Text = name.ProperCase(), to Text = name.GetEnumDescription(...) but if I put value in the parenthesis I get a "does not exist in the current context" message (which I tried fixing but just made the problem worse). If I leave it blank I get the "No overload for ... takes 0 arguments" (again, understandable - but I don't know how to fix). And if I put name in the parenthesis the code compiles but upon viewing the page I get the "Object reference not set..." error on this line:
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes
(typeof(DescriptionAttribute), false);
I've spent a lot of time on this and know that my stumbling block is the
Text = name.ProperCase(),
code. Any ideas/help? Thanks in advance.
UPDATE:
If I do:
Text = GetEnumDescription(selectedValue),
I actually DO get the [Description] text, however, it just displays for the first enum. So, if I have 5 enums all with different [Description]'s the code just repeats the [Description] for the first enum 5 times instead of displaying differently for each. I hope that makes sense and gets to narrow down the problem.
I'd recommend you the Display attribute:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>(T selectedValue = default(T)) where T : struct
{
return
from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = name == selectedValue.ToString()
};
}
public static string GetEnumDescription(string value, Type enumType)
{
var fi = enumType.GetField(value.ToString());
var display = fi
.GetCustomAttributes(typeof(DisplayAttribute), false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (display != null)
{
return display.Name;
}
return value;
}
and then you could have:
public enum Foo
{
[Display(Name = "value 1")]
Value1,
Value2,
[Display(Name = "value 3")]
Value3
}
And now you could have:
var foo = Foo.Value2;
var values = GetItemsFromEnum(foo);
Also notice that I have modified the Selected clause in the LINQ expression as yours is not correct.
This being said, personally I would recommend you staying away from enums on your view models as they don't play nicely with what's built-in ASP.NET MVC and you will have to reinvent most of the things.
I need a checkbox but the underlying data is of type smallint in the database. Not sure how to make the #Html.Checkbox with that datatype. It complains saying the following:
Cannot implicitly convert type 'short?' to 'bool'
Here is the code that I have:
#Html.CheckBoxFor(model => model.HasCycle)
I was having the same problem than you. We use smallint to map boolean values in our database, and we cannot change that.
I am developing a new ASP.NET MVC app, based on our existing database, so I have to deal with this issue.
The solution I adopted, was to create a not mapped boolean property to convert from and to my mapped (smallint / short) property. Like follows:
public short AllowMailing { get; set; }
[NotMapped]
public bool AllowMailingBool
{
get { return AllowMailing == 1? true : false; }
set { AllowMailing = value ? (short)1 : (short)0; }
}
It works fine.
If you are storing a boolean value in the database, then you should use the DB type 'bit' instead of smallint (where 0 will be false and 1 will be true).
Otherwise, you will need to first convert model.HasCycle to a bool. Also, since it is of type short? (nullable), you will need to handle null values too. You will probably want to handle this in the model itself, and publicly expose HasCycle from the model as a bool instead of a short. Still, you may run into some problems going back and forth, and the right way to do it is to change the database type.
To convert from a short? to a bool you can do something like:
bool hasCycleBool = false; //if HasCycle is null, this will remain false
if(model.HasCycle != null)
{
hasCycleBool = Convert.ToBoolean(model.HasCycle);
}
a checkbox is a boolean value, meaning true or false. if you are expecting true/false (1,0) you probably should set the database type to a bool. if you don't want to do this, you will have to convert the int value to a bool (1,0)
I know there is a way to use Expressions and Lambdas to accomplish this but I having a hard time piecing it all together. All I need is a method that will dynamically query an Entity Framework DBSet object to find the row where the propery with the given name matches the value.
My context:
public class MyContext : DbContext
{
public IDbSet<Account> Accoounts{ get { return Set<Account>(); } }
}
The method that I'm looking to write:
public T Get<T>(string property, object value) : where T is Account
{...}
I would rather not have to use Dynamic SQL to accomplish this so no need to suggest it because I already know it's possible. What I'm really looking for is some help to accomplish this using Expressions and Lambdas
Thanks in advance, I know it's brief but it should be pretty self-explanatory. Comment if more info is needed
I'm trying to avoid dynamic linq as much as possible because the main point of linq is strongly typed access. Using dynamic linq is a solution but it is exactly the oppose of the linq purpose and it is quite close to using ESQL and building the query from sting concatenation. Anyway dynamic linq is sometimes real time saver (especially when it comes to complex dynamic ordering) and I successfully use it in a large project with Linq-to-Sql.
What I usually do is defining some SearchCriteria class like:
public class SearchCriteria
{
public string Property1 { get; set; }
public int? Property2 { get; set; }
}
And helper query extension method like:
public static IQueryable<SomeClass> Filter(this IQueryable<SomeClass> query, SearchCriteria filter)
{
if (filter.Property1 != null) query = query.Where(s => s.Property1 == filter.Property1);
if (filter.Property2 != null) query = query.Where(s => s.Property2 == filter.Property2);
return query;
}
It is not generic solution. Again generic solution is for some strongly typed processing of classes sharing some behavior.
The more complex solution would be using predicate builder and build expression tree yourselves but again building expression tree is only more complex way to build ESQL query by concatenating strings.
Here's my implementation:
public T Get<T>(string property, object value) : where T is Account
{
//p
var p = Expression.Parameter(typeof(T));
//p.Property
var propertyExpression = Expression.Property(p, property);
//p.Property == value
var equalsExpression = Expression.Equal(propertyExpression, Expression.Constant(value));
//p => p.Property == value
var lambda = Expression.Lambda<Func<T,bool>>(equalsExpression, p);
return context.Set<T>().SingleOrDefault(lambda);
}
It uses EF 5's Set<T>() method. If you are using a lower version, you'll need to implement a way of getting the DbSet based on the <T> type.
Hope it helps.
Dynamic Linq may be an option. Specify your criteria as a string and it will get built as an expression and ran against your data;
An example from something I have done;
var context = new DataContext(ConfigurationManager.ConnectionStrings["c"].ConnectionString);
var statusConditions = "Status = 1";
var results = (IQueryable)context.Contacts.Where(statusConditions);
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I have a linq query to a XML dataset, which when executed is generating a NullReferenceException.
XDocument dataDoc = XDocument.Load(new StringReader(e.Result));
var Genres = from genre in dataDoc.Descendants("genres")
where (!genre.Element("ID").IsEmpty)
select (string)genre.Element("id").Value + ',' + (string)genre.Attribute("name").Value + ',' + (string)genre.Attribute("url").Value;
foreach (string myGenre in Genres)
{
}
When executed, the Linq query works fine, but when the code attempts to iterate through the foreach loop, the NullReferenceException occurs.
Now, i think that the issue has to do with the XML data I am reading, which looks like the following:
<genres>
<translated>true</translated>
<genre name="1">
<id>28</id>
<url>http://url1</url>
</genre>
<genre name="2">
<id>12</id>
<url>http://url2</url>
</genre>
</genres>
Is the first child node, which is different in structure, causing the issue?
My class behind this shouldn't be an issue, but is the following (just in case):
public class Genre
{
public string ID { get; set; }
public string Name { get; set; }
public string URL { get; set; }
}
genre.Attribute("url") returns null, since there is no url attribute.
You need to call Element, not Attribute.
EDIT: Calling dataDoc.Descendants("genres") returns the single <genres> element, which is not what you want.
You need to call Descendants("genre") (singular) to get the individual <genre ...> elements.
You could also call dataDoc.Descendants("genres").Elements to get the elements inside the <genres> element.
SLaks has pointed out the mistake in using Attribute rather than Element, but there's another improvement you can make in your code. Currently you're using the Value property and then redundantly casting to string. If you just cast an XAttribute or XElement to string, then if the original reference is null, the result will be null as well, rather than an exception being thrown. There's no point in using Value and casting.