I'm trying to create a method signature that takes multiple properties of various type using
I would call it something like this:
AllPropertiesExcept(() => Property1, () => Property2)
This method almost work, except that the type of the properties have to be the same. I'm only going to use the property name, but want to use lambda expression to enable easy refactoring.
public static string MyMethod<T>(params Expression<Func<T>>[] propertyExpression)
I would use AllPropertiesExcept(params Expression<Func<object>>[] properties), you can still get the property names out of it, but it doesn't matter what type the property is.
Edit: However, I would tend to use it the other way round - instead of excluding properties I don't want to see, I would include properties I want to see. The reason is simple - to make your way work, you still need reflection - with my way, you could easily use the Func you get to get the actual data directly.
Edit 2 (getting the property name out of an expression):
Expression<Func<object>> obj = something; // you get this in your method
((obj.Body as UnaryExpression).Operand as MemberExpression).Member.Name
I can really advise you to use LinqPad for such things, you can easily drill down objects via Dump(), which displays the objects very user friendly. Just recreate a small example and experiment.
Does the method AllPropertiesExcept() return anything? Otherwise you could make a fluent interface (using method chaining):
AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3);
Even if the AllPropertiesExcept() method returns something, you can defer the execution until you invoke a method at the end of the method chain:
var foo = AllPropertiesExcept(() => Property1)
.And(() => Property2)
.And(() => Property3)
.DoSomeThing();
I think what you need is to understand the ModelMetadata class documented here:
http://msdn.microsoft.com/en-us/library/system.web.mvc.modelmetadata.aspx
This class is used in ASP.NET MVC in situations like Html.LabelFor(x -> x.Name)
An expression is passed to the ModelMetadata.FromLambdaExpression Method documented here:
http://msdn.microsoft.com/en-us/library/ee428393.aspx
After understanding how it is used in MVC, you could create your own code with some informed knowledge of how it was applied elsewhere.
Related
I'm trying to create validation rules for Pocos in a generic fashion. I'm fairly confident with Reflection, but I don't really want to pick apart the entire call chain to be able to do what I want.
I came up with the idea of detecting all of the types in use and calling a generic function that could setup the rest of the rule, but I can't find anywhere in the chain that exposes the actual object.
This is a nested structure where panels in my form are based on a property whose type is 'object', I want to know at which point I can get the actual value of the instance. A rule chain for me looks like follows:
RuleForEach(p => p.level1s)
.ChildRules(l1 => {
x.RuleForEach(p => p.level2s)
.ChildRules(l2 => {
//ToDo: Iterate Properties on Type # level1s[].level2s[].poco
//How can I find type of poco for this specific executing of this lambda?
//For each property, I'd have to do reflective code for this kind of thing
//ToDo: l2.RuleFor<~~TProperty~~>(x => x.poco~~property~~).NotEmpty()
// .When(x => x.poco~~property~~ has the RequiredAttribute)
// .WithMessage("{PropertyName} is required");
});
});
So, if you can tell from my pseudo code above, I would need to be able to extract the type of the Poco, and the type of the properties within the last ChildRules expression, but all that I have to hand is an InlineValidator<~~l2type~~, object> - so how/where can the current instance type of that 'object' be determined?
Thanks.
Mark
After looking at this for a while and thinking about it from several angles it occurred to me that I should be able to just pass through a validator for my Poco, but a problem occurred when trying to pick up the validator from my variable.
In my first/outer validator, I ended up with the following code:
//ddeach is just the output of a RuleForEach
ddeach.ChildRules(kvconfig =>
{
//Note - Fluent Validation requires that each rule is stated in full at this point starting with a RuleFor, so it's not
//possible to merge all of these following code blocks
//Chain Child Validator
kvconfig.RuleFor(kv => kv.Value.RefPoco)
.SetValidator(dict => (IValidator<object>)dict.Value.PocoValidator);
});
My property at dict.Value.PocoValidator is just an 'IValidator' but FluentValidation doesn't supply an easy way to use one of those, it has to be a strong type to support the fluent API. You can't give it a IValidator, because you don't know the type and LINQ can't infer it, also it won't automatically cast to IValidator.
Eventually, I found that I could allow my validator class to be used by implementing IValidator. My validator code then looks like:
public class Validator : AbstractValidator<MyPocoType>, IValidator<object>
{
public Validator()
{
RuleFor(x => x.Name).NotEmpty();
}
FluentValidation.Results.ValidationResult IValidator<object>.Validate(object instance)
{
return this.Validate(instance as MyPocoType);
}
async Task<FluentValidation.Results.ValidationResult> IValidator<object>.ValidateAsync(object instance, CancellationToken cancellation)
{
return await this.ValidateAsync(instance as MyPocoType, cancellation);
}
}
Mark
Starting with an object mapping that was working perfectly fine for SqlLite, I tried to use the same code with Oracle, and found out that, by default, all of my string fields were mapped to VARCHAR2(255). While that works out fine for most of the fields, it's way too small for one field.
I thought it would be a simple matter to make the field bigger. Hah!
I tried this:
Property(prop => prop.BigField, map =>
{
map.Column("BIG_FIELD");
map.Length(65535);
map.Type<NHibernate.Type.StringType>();
});
This produces this error:
NHibernate.MappingException : Could not instantiate IType StringType: System.MissingMethodException: No parameterless constructor defined for this object.
A few of my search results have told me that I should use NHibernateUtil.String instead of NHibernate.Type.StringType. But all that gets me is this:
What's really weird is that I get String as an autocompletion suggestion, but then I can't use it. I tried to see if the code would compile in spite of the red squiggly line, but it doesn't. I also tried StringClob, with the same results.
Any helpful suggestions? All I want to do is make this string bigger than 255 characters. I didn't expect something so simple to fight back so hard.
To use types provided inside NHibernateUtil class use overload taking persistent type as parameter and not inside generic definition:
Property(prop => prop.Property, map =>
{
map.Type(NHibernateUtil.String);
map.Length(length);
});
In you case it seems it makes sense to map it to NHibernateUtil.StringClob:
Property(prop => prop.BigField, map =>
{
map.Column("BigField");
map.Type(NHibernateUtil.StringClob);
});
And there is nothing weird that code is not compiling inside generic for map.Type<NHibernateUtil.String>(); - generic definition expects type (like string) and not instance of type (like "value") which you are trying to supply
This isn't a full answer (so I'm still curious why I had the above problems), but this workaround is good for now:
Property(prop => prop.BigField, map =>
{
map.Column("BigField");
map.Length(65535);
});
The schema shows that the field becomes a CLOB object, and that's just fine for me for this application.
So, I have my own mapper (NOT AutoMapper) which maps models to each other. You give the model you'd like to map to, and with the Map method you push an object in. Beside of this I wrote the Extend method which functions as a override for the Map method to, for example, add properties which are not available in the object being mapped.
Problem:
The problem hereby is that my public Mapper<T> Extend(Func<T, T> func) method doesn't like the different types.
Possible solutions:
There are 2 solutions I'm thinking of:
Ignore the error and map the value within my Extend method. Which isn't possible as far as I know due to the expression being executed immediately.
Create a LINQ method which maps the value for me. Eg; q => q.Ownership = obj.Ownerships.First().Map().
Question:
How can I resolve this error and achieve what I want?
I am currently trying to set a field which I need in business logic which in this case is Lazy.
(yes not the property, it is necessary to set the field)
I get the error that Lazy can not be converted to Lazy
as you can see:
Object of type
'BusinessLogic.Lazy1[System.Object]'
cannot be converted to type
'BusinessLogic.Lazy1[BusinessLogic.ArtikelBLL]
I use this line to get a dynamic repository.
dynamic repository = Activator.CreateInstance(typeof(GenericRepository<>).MakeGenericType(typeArgs));
Then I try to set the value of the field but it fails:
fInfo.SetValue(obj, Lazy.From(() => repository.GetDataById(id)));
I have tried to solve it many different ways.
Somehow I have to cast repository.GetDataById(id) to the Entity it is looking for, which in this case is ArtikelBLL (which i can get through pInfo.PropertyType).
But by doing (ArtikelBLL)repository.GetDataById(id) it will not remain object orientated.
Can anybody please help me with this?
The simplest way would be to just use a cast inside the lambda:
fInfo.SetValue(obj, new Lazy<GenericBLL>(
() => (ArtikelBLL) repository.GetDataById(id)));
After all, that's the type the Lazy<T> wants.
EDIT: If you're trying to do this dynamically, I suggest you write a generic method like this:
public Lazy<T> CreateLazyDataFetcher<T>(dynamic repository)
{
return new Lazy<T>(() => (T) repository.GetDataById(id));
}
Then call that method with reflection. (Using MethodInfo.MakeGenericMethod(...).Invoke(...))
I have the following bogus code, but the idea is to return a generic class
public static var getSeaOf(string user)
{
var periodsSettings = from p in sea
where p.add_by == new Guid(user)
select new { p.id, p.name };
return var;
}
I have read here - How to return anonymous type from c# method that uses LINQ to SQL that the best solution for this case is to create a class for the return type.
But my question is if I have hundreds of functions like this does it mean I need to have hundreds of classes?
I hope there is a more generic solution, thanks for your help!!
Edition
I take a look at
Silverlight - LinqToEntities - How Do I Return Anonymous Types
But I cannot specified the class name in the select new, like the article does?
public static IEnumerable<retSea> getBskSeasonsOf(string user)
{
var periodsSettings = from p in sea
where p.add_by == new Guid(user)
select new retSea { p.id, p.name };
return periodsSettings;
}
If I remember correctly, the spec says that the anonymous type generated for that object cannot escape the method it's defined in. Therefore the only method that could ever have variables of that type is the method the object is instantiated in. This gets a bit sketchy when you consider the fact that the LINQ query could get compiled into a bunch of methods, but that's magic.
The object itself, however, can escape the method. The way to make this work is to... return object. You'll have to access it using reflection (or dynamic) though, so you'll lose type safety. You might want to consider whether this is worth it or not. Most likely it's not. And most likely you don't have hundreds of different types of results either - I bet many of your queries return the same type of data. Re-use those classes.
If you really have hundreds of classes just like that, just make a class. Or use something already build in for a key value pair, like KeyValuePair.
Here's a blog post on naming anonymous types in .NET using VS 2010's Generate From Usage functionality.
http://diditwith.net/2009/10/24/NamingAnonymousTypesWithGenerateFromUsage.aspx
If each one of those methods returns an anonymous type that has the same fields as the others, then you only have to create one class and re-use it throughout the methods.
If they each return an anonymous type that has different fields, then yes, you'll have to create a class for each of those methods.
If you're using C# 4.0, you could attempt to take advantage of the dynamic type and see what kind of trouble you could get yourself into there.
Well, you probably want to return periodsSettings.
But, after that, consider enumerating the collection with ToDictionary() within this method, where the key is p.id and the value is p.name.
public static IDictionary<int, string> getSeaOf(string user) {
return (from p in sea
where p.add_by == new Guid(user))
.ToDictionary(p => p.id, p => p.name);
}
This effectively gets you your data without having to make a new class. It enumerates it here, of course, but that might not matter in your case.