Trying to save comma-separated list - asp.net-mvc-3

Trying to save selections from a CheckBoxList as a comma-separated list (string) in DB (one or more choices selected). I am using a proxy in order to save as a string because otherwise I'd have to create separate tables in the DB for a relation - the work is not worth it for this simple scenario and I was hoping that I could just convert it to a string and avoid that.
The CheckBoxList uses an enum for it's choices:
public enum Selection
{
Selection1,
Selection2,
Selection3
}
Not to be convoluted, but I use [Display(Name="Choice 1")] and an extension class to display something friendly on the UI. Not sure if I can save that string instead of just the enum, although I think if I save as enum it's not a big deal for me to "display" the friendly string on UI on some confirmation page.
This is the "Record" class that saves a string in the DB:
public virtual string MyCheckBox { get; set; }
This is the "Proxy", which is some sample I found but not directly dealing with enum, and which uses IEnumerable<string> (or should it be IEnumerable<Selection>?):
public IEnumerable<string> MyCheckBox
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyCheckBox)) return new string[] { };
return Record
.MyCheckBox
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set
{
Record.MyCheckBox = value == null ? null : String.Join(",", value);
}
}
To save in the DB, I am trying to do this in a create class:
proxy.MyCheckBox = record.MyCheckBox; //getting error here
but am getting the error:
Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable'
I don't know, if it's possible or better, to use Parse or ToString from the API for enum values.
I know that doing something like this will store whatever I put in the ("") into the DB, so it's just a matter of figuring out how to overcome the error (or, if there is an alternative):
proxy.MyCheckBox = new[] {"foo", "bar"};
I am not good with this stuff and have just been digging and digging to come up with a solution. Any help is much appreciated.

You can accomplish this using a custom user type. The example below uses an ISet<string> on the class and stores the values as a delimited string.
[Serializable]
public class CommaDelimitedSet : IUserType
{
const string delimiter = ",";
#region IUserType Members
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xSet = x as ISet<string>;
var ySet = y as ISet<string>;
if (xSet == null || ySet == null)
{
return false;
}
// compare set contents
return xSet.Except(ySet).Count() == 0 && ySet.Except(xSet).Count() == 0;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new HashSet<string>();
}
else
{
var splitArray = outValue.Split(new[] {Delimiter}, StringSplitOptions.RemoveEmptyEntries);
return new HashSet<string>(splitArray);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as ISet<string>;
object setValue = inValue == null ? null : string.Join(Delimiter, inValue);
NHibernateUtil.String.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
// return new ISet so that Equals can work
// see http://www.mail-archive.com/nhusers#googlegroups.com/msg11054.html
var set = value as ISet<string>;
if (set == null)
{
return null;
}
return new HashSet<string>(set);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.String)}; }
}
public Type ReturnedType
{
get { return typeof(ISet<string>); }
}
public bool IsMutable
{
get { return false; }
}
#endregion
}
Usage in mapping file:
Map(x => x.CheckboxValues.CustomType<CommaDelimitedSet>();

Related

remove tuple from list using linq

Hello I have a list of a custom object (its really just a tuple), I'm trying to remove from one list where the same object is in another list. But I'm having difficulty doing so.
Here is the code:
public class FileNameTuple
{
public FileNameTuple(string origFileName, string newFileName)
{
OrigFileName = origFileName;
NewFileName = newFileName;
}
public string OrigFileName
{
get;
set;
}
public string NewFileName
{
get;
set;
}
}
List<FileNameTuple> fileListing = new List<FileNameTuple>();
List<FileNameTuple> failedFileListing = new List<FileNameTuple>();
//doesn't work, has compliation error
fileListing.RemoveAll(i => failedFileListing.Contains(i.OrigFileName));
Contains compares the object itself, you want to compare a property. Use Any:
fileListing
.RemoveAll(i => failedFileListing.Any(fnt => i.OrigFileName == fnt.OrigFileName));
If you want to use Contains you need to override Equals (+ GetHashCode):
public class FileNameTuple
{
//...
public override bool Equals(object obj)
{
if(object.ReferenceEquals(obj, this) return true;
FileNameTuple t2 = obj as FileNameTuple;
if(t2 == null) return false;
return OrigFileName == t2.OrigFileName;
}
public override int GetHashCode()
{
return (OrigFileName ?? "").GetHashCode();
}
}
Now you can compare FileNameTuples:
fileListing.RemoveAll(i => failedFileListing.Contains(i));
If you just want a filtered list instead of changing an existing list, you can use the Except LINQ operator.
If you can't change your class like Tim Schmelter and don't want to build a custom equality comparer, you can always use my PredicateEqualityComparer.
fileListing.Except(
failedFileListing,
new PredicateEqualityComparer((f1, f2) => f1.OriginalName == f2.OriginalName));

A List of Field Names as a String Array - LINQ Expressions

Hello MVC and LINQ Experts,
I have a Model that looks like this:
public class SomeClass : IValidatableObject
{
public string SomeString { get; set; }
public string SomeString2 { get; set; }
public int SomeInteger { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
//... IF there is some error...THEN
yield return new ValidationResult("Some Error Message.", GetFieldNames(() => new []{ this.SomeString }));
}
}
As you can see, I am calling GetFieldNames that takes an expression, and returns to you the expression members as a string array. According to a book I read recently, the way to link an error to a field is to pass it as a string as follows:
yield return new ValidationResult("Some Error Message.", new []{ "SomeString" }));
But I wanted to be Strongly Typed, so here is the method that I wrote:
public static string[] GetFieldNames(Expression<Func<object[]>> exp)
{
//Build a string that will in the end look like this: field1,field2,field3
//Then we split(',') it into an array and return that string array.
string fieldnames = "";
MemberExpression body = exp.Body as MemberExpression;
if (body == null)
{
NewArrayExpression ubody = (NewArrayExpression)exp.Body;
foreach(MemberExpression exp2 in ubody.Expressions)
{
fieldnames += exp2.Member.Name + ",";
}
fieldnames = fieldnames.TrimEnd(',');
}
if(fieldnames.Length > 0)
return fieldnames.Split(',');
else
return new string[]{};
}
Current Usage:
GetFieldNames(() => new[] { this.SomeString , this.SomeString2 });
Output:
{ "SomeString" , "SomeString2" }
This works fine.
The problem is that if I use it as follows, it gives me an error (compile time):
GetFieldNames(() => new[] { this.SomeString , this.SomeInteger });
Error:
No best type found for implicitly-typed array
My Desired Output:
{ "SomeString" , "SomeInteger" }
I can't pass in an array of object because int is not a complex type.
How can I pass the function an expression array with int and string?
You could try passing an array of objects (which is what your expression expects) instead of trying to use an array initializer syntax:
GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger });
This allows you to pass arbitrary object types.
You could define an interface IFieldName that enables usage in your list, and then implement it in different classes (int, error, string, etc.) for the actual types that occur in your processing.
This is roughly equivalent to defining an rray of object, but restores type-safety.
With the help of Darin Dimitri (the idea to pass a new object[] instead of new []
The following code will make sure that your IValidatableObject can now be strongly typed instead of just an array of strings.
public static string[] GetFieldNames(Expression<Func<object[]>> exp)
{
string fieldnames = "";
MemberExpression body = exp.Body as MemberExpression;
if (body == null)
{
NewArrayExpression ubody = (NewArrayExpression)exp.Body;
foreach (Expression exp2 in ubody.Expressions)
{
if (exp2 is MemberExpression) {
fieldnames += ((MemberExpression)exp2).Member.Name + ",";
}
else {
var op = ((UnaryExpression)exp2).Operand;
fieldnames += ((MemberExpression)op).Member.Name + ",";
}
}
fieldnames = fieldnames.TrimEnd(',');
}
if(fieldnames.Length > 0)
return fieldnames.Split(',');
else
return new string[]{};
}
Usage:
GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger }));
Usage for MVC Validation:
yield return new ValidationResult("Some Error.", GetFieldNames(() => new object[] { this.SomeString, this.SomeInteger }));

How do I apply a default IComparable<T> in a Linq OrderBy clause

I have a type which has a default sort order as it implements IComparable<T> and IComparable. I'm not getting the results I expect from LINQ , basically it looks as if the IComparable<T> which the type implements is not being applied.
I thought I would get the result I want with an expression in the form:
var result = MyEnumerable<T>.OrderBy(r => r);
where T itself implements IComparable<T>. It's not happening.
I can see related questions where specific IComparable<T> classes are specified for the sort, but I can't find one which uses the default IComparable<T> implemented by T itself.
My syntax is clearly incorrect. What is the correct syntax please?
Thanks in advance.
OrderBy uses the default comparer Comparer<T>.Default which in turn will default to use the IComparable<T> implementation for T, or the non-generic IComparable if the former does not exist.
This code works:
public class Program
{
static void Main(string[] args)
{
var list = new List<Stuff>
{
new Stuff("one"),
new Stuff("two"),
new Stuff("three"),
new Stuff("four")
};
var sorted = list.OrderBy(x => x);
foreach (var stuff in sorted)
{
Console.Out.WriteLine(stuff.Name);
}
}
}
public class Stuff : IComparable<Stuff>
{
public string Name { get; set; }
public Stuff(string name)
{
Name = name;
}
public int CompareTo(Stuff other)
{
return String.CompareOrdinal(Name, other.Name);
}
}
public static class GenericSorter
{
public static IOrderedEnumerable<T> Sort<T>(IEnumerable<T> toSort, Dictionary<string, SortingOrder> sortOptions)
{
IOrderedEnumerable<T> orderedList = null;
foreach (KeyValuePair<string, SortingOrder> entry in sortOptions)
{
if (orderedList != null)
{
if (entry.Value == SortingOrder.Ascending)
{
orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenBy");
}
else
{
orderedList = orderedList.ApplyOrder<T>(entry.Key, "ThenByDescending");
}
}
else
{
if (entry.Value == SortingOrder.Ascending)
{
orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderBy");
}
else
{
orderedList = toSort.ApplyOrder<T>(entry.Key, "OrderByDescending");
}
}
}
return orderedList;
}
private static IOrderedEnumerable<T> ApplyOrder<T>(this IEnumerable<T> source, string property, string methodName)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x");
Expression expr = param;
foreach (string prop in property.Split('.'))
{
expr = Expression.PropertyOrField(expr, prop);
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), expr.Type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);
MethodInfo mi = typeof(Enumerable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), expr.Type);
return (IOrderedEnumerable<T>)mi.Invoke(null, new object[] { source, lambda.Compile() });
}
}

EmitMapper and List

It's the first time that I use EmitMapper.
I have a list of object ex: Customer and I would like to map this list in a ienumerable of CustomerDTO how can I do that?
Tnx
It's straightforward if you have a list and want to convert it to list of DTOs:
var mapper = ObjectMapperManager.DefaultInstance.GetMapper<Customer, CustomerDTO>();
IEnumerable<CustomerDTO> dtos = listOfCustomer.Select(mapper.map);
The preblem is when the list is in another object, for example User and UserDTO:
class User {
public List<Customer> Customers { get; set; }
}
class UserDTO {
public IEnumerable<CustomerDTO> Customers { get; set; }
}
It seems that EmitMapper does not support conversion from List to Enumerable. A way to support it would be:
var customerMapper = ObjectMapperManager
.DefaultInstance.GetMapper<Customer, CustomerDTO>();
var mapper = ObjectMapperManager.DefaultInstance
.GetMapper<User, UserDTO>(
new DefaultMapConfig()
.ConvertUsing<List<Customer>, IEnumerable<CustomerDTO>>(
a => a.Select(customerMapper.Map))
);
This can be done creating a custom class, implementing the interface "ICustomConverterProvider" and adding a ConvertGeneric to the "DefaultMapConfig".
Looking on the source code of EmitMapper, i found a class named "ArraysConverterProvider", which is the default generic converter from ICollections to Arrays.
Adapting the code from this class to work with IEnumerable collections:
class GenericIEnumerableConverterProvider : ICustomConverterProvider
{
public CustomConverterDescriptor GetCustomConverterDescr(
Type from,
Type to,
MapConfigBaseImpl mappingConfig)
{
var tFromTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(from);
var tToTypeArgs = DefaultCustomConverterProvider.GetGenericArguments(to);
if (tFromTypeArgs == null || tToTypeArgs == null || tFromTypeArgs.Length != 1 || tToTypeArgs.Length != 1)
{
return null;
}
var tFrom = tFromTypeArgs[0];
var tTo = tToTypeArgs[0];
if (tFrom == tTo && (tFrom.IsValueType || mappingConfig.GetRootMappingOperation(tFrom, tTo).ShallowCopy))
{
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_OneTypes<>),
ConverterClassTypeArguments = new[] { tFrom }
};
}
return new CustomConverterDescriptor
{
ConversionMethodName = "Convert",
ConverterImplementation = typeof(GenericIEnumerableConverter_DifferentTypes<,>),
ConverterClassTypeArguments = new[] { tFrom, tTo }
};
}
}
class GenericIEnumerableConverter_DifferentTypes<TFrom, TTo> : ICustomConverter
{
private Func<TFrom, TTo> _converter;
public IEnumerable<TTo> Convert(IEnumerable<TFrom> from, object state)
{
if (from == null)
{
return null;
}
TTo[] result = new TTo[from.Count()];
int idx = 0;
foreach (var f in from)
{
result[idx++] = _converter(f);
}
return result;
}
public void Initialize(Type from, Type to, MapConfigBaseImpl mappingConfig)
{
var staticConverters = mappingConfig.GetStaticConvertersManager() ?? StaticConvertersManager.DefaultInstance;
var staticConverterMethod = staticConverters.GetStaticConverter(typeof(TFrom), typeof(TTo));
if (staticConverterMethod != null)
{
_converter = (Func<TFrom, TTo>)Delegate.CreateDelegate(
typeof(Func<TFrom, TTo>),
null,
staticConverterMethod
);
}
else
{
_subMapper = ObjectMapperManager.DefaultInstance.GetMapperImpl(typeof(TFrom), typeof(TTo), mappingConfig);
_converter = ConverterBySubmapper;
}
}
ObjectsMapperBaseImpl _subMapper;
private TTo ConverterBySubmapper(TFrom from)
{
return (TTo)_subMapper.Map(from);
}
}
class GenericIEnumerableConverter_OneTypes<T>
{
public IEnumerable<T> Convert(IEnumerable<T> from, object state)
{
if (from == null)
{
return null;
}
return from;
}
}
This code is just a copy with a minimum of adaptation as possible and can be applyed to objects with many levels of hierarchy.
You can use the above code with the following command:
new DefaultMapConfig().ConvertGeneric(
typeof(IEnumerable<>),
typeof(IEnumerable<>),
new GenericIEnumerableConverterProvider());
This saved my day and I hope to save yours too! hehehe

Creating linq expression with a subtype restriction

I have this list of type IEnumerable<MyBaseType> for which I am trying to create an extra where-clause to retrieve a specific item in the list. The specific value does only exist on subtype MyFirstType and MySecondType. Not on MyBaseType.
Is it possible to create an expression kind of...
MyList.Where(b => (b is MyFirstType || (b is MySecondType)) && b.SpecificValue == message.SpecificValue);
Above is not working since b is of type MyBaseType and SpecificValue does not exist there. Also note that I do have another subtype MyThirdType that neither has the SpecificValue.
What does work doing what I want is this...
foreach (dynamic u in MyList)
{
if (u is MyFirstType || u is MySecondType)
{
if (u.SpecificValue == message.SpecificValue)
{
//Extracted code goes here
break;
}
}
}
Anyone have an idea how to create an linq expression for the above scenario?
Maybe there is a better solution but as I see it, this could work well enough... If you don't mind performance.
Well then, start by declaring an interface:
public interface IMySpecialType
{
object SpecificValue {get; set;} //you didn't specify what type this is
//all your other relevant properties which first and second types have in common
}
Then, make MyFirstType and MySecondType derive from this interface:
public class MyFirstType : MyBaseType, IMySpecialType
{
//snipet
}
public class MyFirstType : MySecondType, IMySpecialType
{
//snipet
}
Then, filter and cast:
MyList
.Where(b => (b is MyFirstType) || (b is MySecondType))
.Cast<IMySpecialType>()
.Where(b => b.SpecificValue == message.SpecificValue);
//do something
The direct translation of your code to a Linq where clause is
string messageValue = "foo";
var result = baseList.Where(item =>
{
dynamic c = item;
if(item is MyFirstType || item is MySecondType)
{
if( c.SpecificValue == messageValue)
return true;
}
return false;
});
This will require testing the type of the class though and using dynamic - so you might as well cast item to either MyFirstType or MySecondType directly.
An alternative would be using reflection to check if the property exists, using this approach you are not dependent on the actual types of your items as long as they do have the property you are interested in:
string messageValue = "foo";
var result = baseList.Where( item =>
{
var prop = item.GetType().GetProperty("SpecificValue");
if (prop != null && prop.GetValue(item, null) == messageValue)
return true;
else return false;
});
If modifying the class hierarchy is an option you can have you MyFirstType or MySecondType implement an interface that holds the property, then you can use OfType() in your Linq query:
interface ISpecific
{
string SpecificValue { get; set; }
}
class MyFirstType : MyBase, ISpecific
{
public string SpecificValue { get; set; }
}
...
string messageValue = "foo";
var result = baseList.OfType<ISpecific>()
.Where(item => item.SpecificValue == messageValue);
A far more easy way to do that would be to create an interface to mark all your classes having this property SpecificValue. Then it's a child play :
static void Main(string[] args)
{
List<MyBaseType> MyList = new List<MyBaseType>();
ISpecificValue message = new MyFirstType();
MyList.OfType<ISpecificValue>().Where(b => b.SpecificValue == message.SpecificValue);
}
}
class MyBaseType { }
interface ISpecificValue { string SpecificValue { get; set; } }
class MyFirstType : MyBaseType, ISpecificValue
{
public string SpecificValue;
}
class MySecondType : MyBaseType, ISpecificValue
{
public string SpecificValue;
}

Resources