I have list of Objects upto 150000 and I want to bind to razor view at run time for it export as EXCEL but at the time binding i am getting out of memory exception , is there any workaround to overcome this limitation ?
Export Method :
public void ExportToExcel()
{
string viewPath = "~/Modules/Reports/Views/" + TempData["reportName"] + ".cshtml";
string viewPathCopy = "~/Modules/Reports/Views/" + TempData["reportName"] + "2.cshtml";
string serverViewPath = Server.MapPath(viewPath);
string serverViewPathCopy = Server.MapPath(viewPathCopy);
if (System.IO.File.Exists(serverViewPathCopy))
{
System.IO.File.Delete(serverViewPathCopy);
}
System.IO.File.Copy(serverViewPath, serverViewPathCopy);
string viewContents = System.IO.File.ReadAllText(serverViewPathCopy).Replace("thead", "tr");
viewContents += "<style>body{font-size:8px !important;}table {padding:0 !important,margin:0 !important}</style>";
System.IO.File.WriteAllText(serverViewPathCopy, viewContents);
System.IO.File.WriteAllText(viewPathCopy, TemplateFromFile(viewPathCopy, TempData["reportData"]));
FileInfo file = new FileInfo(viewPathCopy);
if (file.Exists)
{
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name + ".xls");
Response.AddHeader("Content-Length", file.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(file.FullName);
Response.End();
}
else
{
Response.Write("This file does not exist.");
}
}
Binding Model To View :
public string TemplateFromFile(string file, dynamic model)
{
string template = "";
TextReader textReader = new StreamReader(HelperMethods.GetFullFilePath(file));
try
{
template = textReader.ReadToEnd();
}
finally
{
textReader.Close();
}
return Razor.Parse(template, model);
}
If i have large amounts of data to export, my goto tool is DoddleReport this lets you create a valid xlsx export. With doddle you can even create multi tab excel sheets (see my example on Github).
Some code
Extension for IEnumerable
public static ExportBuilder<TModel> Export<TModel>(this IEnumerable<TModel> models) where TModel : class
{
return ExportBuilder<TModel>.Create(models);
}
The Export Builder
public class ExportBuilder<TModel> where TModel : class
{
private readonly IEnumerable<TModel> _models;
private readonly ICollection<IExportColumn<TModel>> _columns;
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
private ExportBuilder(IEnumerable<TModel> models)
{
_models = models;
_columns = new List<IExportColumn<TModel>>();
}
public static ExportBuilder<TModel> Create(IEnumerable<TModel> models)
{
return new ExportBuilder<TModel>(models);
}
public ExportBuilder<TModel> Column<TProperty>(Expression<Func<TModel, TProperty>> display)
{
if (!(display.Body is MemberExpression))
throw new ArgumentException(display + " is not a property!");
var memberInfo = ((MemberExpression)display.Body).Member;
if (!memberInfo.HasAttribute<DisplayNameAttribute>())
throw new ArgumentException(display + " does not have a [Display] attribute");
var displayAttribute = ExtensionsForMemberInfo.GetAttribute<DisplayNameAttribute>(memberInfo);
_columns.Add(new ExportColumn<TModel, TProperty>(displayAttribute.DisplayName, display));
return this;
}
public ExportBuilder<TModel> Column<TProperty>(string header, Expression<Func<TModel, TProperty>> property)
{
_columns.Add(new ExportColumn<TModel, TProperty>(header, property));
return this;
}
public IReportSource ToReportSource()
{
if (_models.Any())
{
return DoddleExporter.ToReportSource(_models.Select(model => _columns.ToDictionary(c => c.Header, c => c.Display(model))));
}
var result = _columns
.ToDictionary(a => a.Header, a => string.Empty);
return DoddleExporter.ToReportSource(new[] { result });
}
public Report ToReport([CanBeNull] IEnumerable<KeyValuePair<string, string>> headers, [CanBeNull] IReportWriter writer = null)
{
headers = headers ?? Enumerable.Empty<KeyValuePair<string, string>>();
var report = new Report(ToReportSource(), writer);
//report.TextFields.Footer = string.Format(#"Aangemaakt op: {0}", DateTime.Now.ToString(DataFormatStrings.Date));
var headersArray = headers as KeyValuePair<string, string>[] ?? headers.ToArray();
if (headersArray.Any())
{
report.TextFields.Header = headersArray.Aggregate(string.Empty,
(currentHeaders, header) => string.Format("{0}{3}{1} : {2}", currentHeaders, header.Key, header.Value, Environment.NewLine));
}
return report;
}
public ReportResult ToExcelReportResult([CanBeNull] IEnumerable<KeyValuePair<string, string>> headers)
{
return new ReportResult(ToReport(headers), new ExcelReportWriter(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
}
The Exporter
public static class DoddleExporter
{
/// <summary>
/// Converts an enumerable of dictionaries to a report source
/// </summary>
/// <typeparam name="TValue">
/// The type of values in the dictionaries
/// </typeparam>
/// <param name="elements">
/// An enumerable of elements
/// </param>
/// <returns>The report source that was created from the elements</returns>
public static IReportSource ToReportSource<TValue>(IEnumerable<IDictionary<string, TValue>> elements)
{
var elementsArray = elements.ToArray();
if (!elementsArray.Any())
throw new ArgumentException("Can't export empty list of elements");
return ToReportSource(elementsArray, elementsArray.First().Keys.ToArray(),
(element, key) => element.ContainsKey(key) ? element[key] : default(TValue));
}
/// <summary>
/// Converts an enumerable of XElement to a report source
/// </summary>
/// <param name="rootElements">
/// The xml root elements that contain the values
/// </param>
/// <param name="keys">
/// They keys that can be used to fetch values from each root element
/// </param>
/// <returns>The report source that was created from the elements</returns>
public static IReportSource ToReportSource(IEnumerable<XElement> rootElements, string[] keys)
{
return ToReportSource(rootElements, keys, delegate(XElement element, string key)
{
var value = element.Element(XmlConvert.EncodeLocalName(key));
return value != null ? value.Value : null;
});
}
/// <summary>
/// Converts a list of elements to a report source
/// </summary>
/// <param name="elements">
/// An enumerable of elements
/// </param>
/// <param name="keys">
/// They keys with which the values can be fetched from one element
/// </param>
/// <param name="valueSelector">
/// The function with which one value can be fetched given one key and one element
/// </param>
/// <returns>The report source that was created from the elements</returns>
public static IReportSource ToReportSource<T>(IEnumerable<T> elements, string[] keys,
Func<T, string, object> valueSelector)
{
var expandos = new List<ExpandoObject>();
foreach (var element in elements)
{
var expando = new ExpandoObject();
var expandoDictionary = (IDictionary<string, object>) expando;
foreach (var key in keys)
{
var value = valueSelector(element, key);
expandoDictionary[key] = value;
}
expandos.Add(expando);
}
return expandos.ToReportSource();
}
}
Helper classes
public interface IExportColumn<TModel> where TModel : class
{
string Header { get; }
Func<TModel, Object> Display { get; }
}
public class ExportColumn<TModel, TProperty> : IExportColumn<TModel> where TModel : class
{
private readonly string _header;
private readonly Expression<Func<TModel, TProperty>> _display;
public string Header { get { return _header; } }
public Func<TModel, Object> Display { get { return model => _display.Compile().Invoke(model); } }
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
/// </summary>
public ExportColumn(string header, Expression<Func<TModel, TProperty>> display)
{
_header = header;
_display = display; ;
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
public override string ToString()
{
return string.Format("Header: {0}, Display: {1}", _header, _display);
}
}
Usage
var report = probing.Measurements.Export()
.Column(MeasurementResource.Depth, m => m.Depth)
.Column(MeasurementResource.DepthBelowWater, m => m.DepthBelowWater)
.Column(MeasurementResource.ResistancePoint, m => m.ResistancePoint)
.Column(MeasurementResource.FrictionLateral, m => m.FrictionLateral)
.Column(MeasurementResource.FrictionLocal, m => m.FrictionLocal)
.Column(MeasurementResource.FrictionTotal, m => m.FrictionTotal)
.Column(MeasurementResource.Inclination, m => m.Inclination)
.Column(MeasurementResource.PoreWaterPressure, m => m.PoreWaterPressure)
.Column(MeasurementResource.Speed, m => m.Speed)
.Column(MeasurementResource.CalcAlpha, m => m.CalcAlpha)
.Column(MeasurementResource.CalcGammaDry, m => m.CalcGammaDry)
.Column(MeasurementResource.CalcGammaWet, m => m.CalcGammaWet)
.Column(MeasurementResource.GrainTension, m => m.GrainTension)
.Column(MeasurementResource.CompressionCoefficient, m => m.CompressionCoefficient)
.ToReport(null, new ExcelReportWriter());
var stream = new MemoryStream();
writer.WriteReport(report, stream);
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "application/vnd.ms-excel", string.Format(#"{0}-{1}.xlsx", probing.Project.ProjectNumber, probing.ProbingNumber));
I am trying to get a static field info into metro app and I don't find a way to do that.
I have tried:
- type.GetRuntimeField
- typeInfo.GetDeclaredField in a loop to delve into every parent types
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
... am I missing something or is there anyway to get a static field on a type using reflection in metro app?....
edit:
Well, I am so sorry for those who have waste time on this question, Dependency properties defined in the framework are actualy not readonly static fields, they are static properties... As I usualy declare my dps as field, I didn't consider the fact that form example FrameworkElement.Width could be a property...
So here is the code I used to get fields and property info:
public static class TypeExtensions
{
/// <summary>
/// Gets the field info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="fieldName">The name of the field</param>
/// <returns>The field info if found, null otherwise</returns>
public static FieldInfo GetField(this Type type, string fieldName)
{
var currentType = type;
FieldInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredField(fieldName);
currentType = typeInfo.BaseType;
}
return result;
}
/// <summary>
/// Gets the property info from the specified name
/// </summary>
/// <param name="type">The source type</param>
/// <param name="propertyName">The name of the property</param>
/// <returns>The field info if found, null otherwise</returns>
public static PropertyInfo GetProperty(this Type type, string propertyName)
{
var currentType = type;
PropertyInfo result = null;
while (result == null && currentType != null)
{
var typeInfo = currentType.GetTypeInfo();
result = typeInfo.GetDeclaredProperty(propertyName);
currentType = typeInfo.BaseType;
}
return result;
}
}
public static class DependencyObjectExtensions
{
public static DependencyProperty GetDependencyProperty(this DependencyObject dependencyObject, string propertyName)
{
var dependencyPropertyName = propertyName + "Property";
var type = dependencyObject.GetType();
var fieldInfo = type.GetField(dependencyPropertyName);
if (fieldInfo == null)
{
var propertyInfo = type.GetProperty(dependencyPropertyName);
if (propertyInfo != null)
{
return propertyInfo.GetValue(dependencyObject) as DependencyProperty;
}
}
else
{
var value = fieldInfo.GetValue(dependencyObject);
return value as DependencyProperty;
}
return null;
}
}
Thanks a lot
Regards,
Charles
I want to get the StringLength attribute.
Class code :
var type1 = Type.GetType("MvcApplication4.Models.Sample.SampleMasterModel");
var metadata = ModelMetadataProviders.Current.GetMetadataForType(null, type1);
var properties = metadata.Properties;
var prop = properties.FirstOrDefault(p => p.PropertyName == "Remark");
?? Get StringLength attr?
Model Code:
public class SampleModel
{
[StringLength(50)]
public string Remark { get; set; }
}
Based on wudzik and Habib help. I modified the code.
Final Code:
PropertyInfo propertyInfo = type1.GetProperties().FirstOrDefault(p => p.Name == "Remark");
if (propertyInfo != null)
{
var attributes = propertyInfo.GetCustomAttributes(true);
var stringLengthAttrs =
propertyInfo.GetCustomAttributes(typeof (StringLengthAttribute), true).First();
var stringLength = stringLengthAttrs != null ? ((StringLengthAttribute)stringLengthAttrs).MaximumLength : 0;
}
You can get CustomAttributes through PropertyInfo like:
PropertyInfo propertyInfo = type1.GetProperties().FirstOrDefault(p=> p.Name == "Remark");
if (propertyInfo != null)
{
var attributes = propertyInfo.GetCustomAttributes(true);
}
/// <summary>
/// Returns the StringLengthAttribute for a property based on the property name passed in.
/// Use this method in the class or in a base class
/// </summary>
/// <param name="type">This type of the class where you need the property StringLengthAttribute.</param>
/// <param name="propertyName">This is the property name.</param>
/// <returns>
/// StringLengthAttribute of the propertyName passed in, for the Type passed in
/// </returns>
public static StringLengthAttribute GetStringLengthAttribute(Type type, string propertyName)
{
StringLengthAttribute output = null;
try
{
output = (StringLengthAttribute)type.GetProperty(propertyName).GetCustomAttribute(typeof(StringLengthAttribute));
}
catch (Exception ex)
{
//error handling
}
return output;
} //GetStringLengthAttribute
/// <summary>
/// Returns the StringLengthAttribute for a property based on the property name passed in.
/// Use this method in the class or in a base class
/// </summary>
/// <param name="propertyName">This is the property name.</param>
/// <returns>
/// StringLengthAttribute of the propertyName passed in, for the current class
/// </returns>
public StringLengthAttribute GetStringLengthAttribute(string propertyName)
{
StringLengthAttribute output = null;
try
{
output = (StringLengthAttribute)this.GetType().GetProperty(propertyName).GetCustomAttribute(typeof(StringLengthAttribute));
}
catch (Exception ex)
{
//error handling
}
return output;
} //GetStringLengthAttribute
}
I have the below in my controller and would like to pass x, y, and z of type float to subscribers. I'm having some difficulties catching on however. What would I have to adjust to allow me to pass my floats (x, y, z) as parameters? Thank you.
private void ProcessEvents()
{
if(Input.GetMouseButtonDown(1))
{
// Data to be passed to subscribers
float x = Input.mousePosition.x;
float y = Input.mousePosition.y;
float z = Input.mousePosition.z;
var publisher = new EventPublisher();
var handler = new Handler();
// Void delegate with one parameter
string eventName = "RightClickEvent";
var rightClickEvent = publisher.GetType().GetEvent(eventName);
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<int>(rightClickEvent, i=>Debug.LogError(i + "!")));
publisher.PublishEvents();
}
}
Other sources:
public class ExampleEventArgs : EventArgs
{
public int IntArg {get; set;}
}
Event Publisher:
public class EventPublisher
{
public event EventHandler<ExampleEventArgs> RightClickEvent;
/// <summary>
/// Publishes the events.
/// </summary>
public void PublishEvents()
{
if(RightClickEvent != null)
{
RightClickEvent(this, new ExampleEventArgs{IntArg = 5});
}
}
}
Handler:
public class Handler
{
public void HandleEventWithArg(int arg) { Debug.LogError("Arg: " + string.Format("[{0}]", arg)); }
}
EventProxy:
static class EventProxy
{
// Void delegate with one parameter
static public Delegate Create<T>(EventInfo evt, Action<T> d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg)
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body,parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
// Returns an expression that represents an argument to be passed to the delegate
static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if(eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return Expression.MakeMemberAccess(eventArgs,memberInfo);
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
// Void delegates with no parameters
static public Delegate Create(EventInfo evt, Action d)
{
var handlerType = evt.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, EventArgs x1) => d()
var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x"));
var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"));
var lambda = Expression.Lambda(body,parameters.ToArray());
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
}
Ok, so i read what you said about decoupling application modules in MVC-like style. I normally like to work with strong typed code, even when using reflection, but i'm relatively new to MVC, and don't know the recommended practices. You know your requirements better than i do, so i simply edited Nguyen's solution, because i believe he was using some extensions that were not included in the file, and posted the result here. All credit goes to Nguyen.
namespace Dynamics
{
public static class DynamicHandler
{
/// <summary>
/// Invokes a static delegate using supplied parameters.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this Type targetType, string delegateName, params object[] parameters)
{
return ((Delegate)targetType.GetField(delegateName).GetValue(null)).DynamicInvoke(parameters);
}
/// <summary>
/// Invokes an instance delegate using supplied parameters.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="delegateName">The field name of the delegate.</param>
/// <param name="parameters">The parameters used to invoke the delegate.</param>
/// <returns>The return value of the invocation.</returns>
public static object InvokeDelegate(this object target, string delegateName, params object[] parameters)
{
return ((Delegate)target.GetType().GetField(delegateName).GetValue(target)).DynamicInvoke(parameters);
}
/// <summary>
/// Adds a dynamic handler for a static delegate.
/// </summary>
/// <param name="targetType">The type where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, false);
}
/// <summary>
/// Adds a dynamic handler for an instance delegate.
/// </summary>
/// <param name="target">The object where the delegate belongs to.</param>
/// <param name="fieldName">The field name of the delegate.</param>
/// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AddHandler(this object target, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, false);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="targetType">The type where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this Type targetType, string fieldName,
Func<object[], object> func)
{
return InternalAddHandler(targetType, fieldName, func, null, true);
}
/// <summary>
/// Assigns a dynamic handler for a static delegate or event.
/// </summary>
/// <param name="target">The object where the delegate or event belongs to.</param>
/// <param name="fieldName">The field name of the delegate or event.</param>
/// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
/// <returns>The return value of the invocation.</returns>
public static Type AssignHandler(this object target, string fieldName, Func<object[], object> func)
{
return InternalAddHandler(target.GetType(), fieldName, func, target, true);
}
private static Type InternalAddHandler(Type targetType, string fieldName,
Func<object[], object> func, object target, bool assignHandler)
{
Type delegateType;
var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic |
(target == null ? BindingFlags.Static : BindingFlags.Instance);
var eventInfo = targetType.GetEvent(fieldName, bindingFlags);
if (eventInfo != null && assignHandler)
throw new ArgumentException("Event can be assigned. Use AddHandler() overloads instead.");
if (eventInfo != null)
{
delegateType = eventInfo.EventHandlerType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
eventInfo.GetAddMethod(true).Invoke(target, new Object[] { dynamicHandler });
}
else
{
var fieldInfo = targetType.GetField(fieldName);
//,target == null ? BindingFlags.Static : BindingFlags.Instance);
delegateType = fieldInfo.FieldType;
var dynamicHandler = BuildDynamicHandler(delegateType, func);
var field = assignHandler ? null : target == null
? (Delegate)fieldInfo.GetValue(null)
: (Delegate)fieldInfo.GetValue(target);
field = field == null
? dynamicHandler
: Delegate.Combine(field, dynamicHandler);
if (target != null)
target.GetType().GetField(fieldName).SetValue(target, field);
else
targetType.GetField(fieldName).SetValue(null, field);
//(target ?? targetType).SetFieldValue(fieldName, field);
}
return delegateType;
}
/// <summary>
/// Dynamically generates code for a method whose can be used to handle a delegate of type
/// <paramref name="delegateType"/>. The generated method will forward the call to the
/// supplied <paramref name="func"/>.
/// </summary>
/// <param name="delegateType">The delegate type whose dynamic handler is to be built.</param>
/// <param name="func">The function which will be forwarded the call whenever the generated
/// handler is invoked.</param>
/// <returns></returns>
public static Delegate BuildDynamicHandler(this Type delegateType, Func<object[], object> func)
{
var invokeMethod = delegateType.GetMethod("Invoke");
var parameters = invokeMethod.GetParameters().Select(parm =>
Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
var instance = func.Target == null ? null : Expression.Constant(func.Target);
var convertedParameters = parameters.Select(parm => Expression.Convert(parm, typeof(object))).Cast<Expression>().ToArray();
var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), convertedParameters));
var body = invokeMethod.ReturnType == typeof(void)
? (Expression)call
: Expression.Convert(call, invokeMethod.ReturnType);
var expr = Expression.Lambda(delegateType, body, parameters);
return expr.Compile();
}
}
}
And i also added some code to test the methods, i could have just used simple lambdas when i assigned the callback delegates, but i rather use the "return true" definitions because i set breakpoints to check that the functions are actually called.
class TestClass
{
private void Test()
{
TestInstance();
TestStatic();
}
private void TestInstance()
{
var eventClass = new EventClass();
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AddHandler("InstanceEvent", parameters =>
{
return true;
});
eventClass.AddHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
eventClass.AssignHandler("InstanceEventRaiseDelegate", parameters =>
{
return true;
});
eventClass.InvokeDelegate("InstanceEventRaiseDelegate");
}
private void TestStatic()
{
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AddHandler("StaticEvent", parameters =>
{
return true;
});
typeof(EventClass).AddHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
typeof(EventClass).AssignHandler("StaticEventRaiseDelegate", parameters =>
{
return true;
});
typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate");
}
public class EventClass
{
#region Instance
public EventClass()
{
InstanceEventRaiseDelegate = OnInstanceEvent;
}
public Action InstanceEventRaiseDelegate;
public event EventHandler InstanceEvent;
public void OnInstanceEvent()
{
if (InstanceEvent != null)
InstanceEvent(this, EventArgs.Empty);
}
#endregion
#region Static
static EventClass()
{
StaticEventRaiseDelegate = OnStaticEvent;
}
public static Action StaticEventRaiseDelegate;
public static event EventHandler StaticEvent;
public static void OnStaticEvent()
{
if (StaticEvent != null)
StaticEvent(null, EventArgs.Empty);
}
#endregion
}
}
Sorry for the late response, but it seems you were able to find the solution elsewhere :), goodluck.
I have added some code that will achieve what u require:
I modified the getArgExpression function to this
// Returns a List of expressions that represent the arguments to be passed to the delegate
static IEnumerable<Expression> getArgExpression(ParameterExpression eventArgs, Type handlerArgType)
{
if (eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int))
{
//"x1.IntArg"
var memberInfo = eventArgs.Type.GetMember("IntArg")[0];
return new List<Expression> { Expression.MakeMemberAccess(eventArgs, memberInfo) };
}
if (eventArgs.Type == typeof(MousePositionEventArgs) && handlerArgType == typeof(float))
{
//"x1.X"
var xMemberInfo = eventArgs.Type.GetMember("X")[0];
//"x1.Y"
var yMemberInfo = eventArgs.Type.GetMember("Y")[0];
//"x1.Z"
var zMemberInfo = eventArgs.Type.GetMember("Z")[0];
return new List<Expression>
{
Expression.MakeMemberAccess(eventArgs, xMemberInfo),
Expression.MakeMemberAccess(eventArgs, yMemberInfo),
Expression.MakeMemberAccess(eventArgs, zMemberInfo),
};
}
throw new NotSupportedException(eventArgs + "->" + handlerArgType);
}
The calling function stays the same, because it has an overload for IEnumerable<Expression>.
I then added the EventArgs specific to your MousePosition situation
public class MousePositionEventArgs : EventArgs
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
Further more i wrote a new function in 'EventProxy' that will handle a delegate with 3 parameters of the same type
// Void delegate with three parameters
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate)
{
var handlerType = eventInformation.EventHandlerType;
var eventParams = handlerType.GetMethod("Invoke").GetParameters();
//lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z)
var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray();
var arg = getArgExpression(parameters[1], typeof(T));
var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg);
var lambda = Expression.Lambda(body, parameters);
return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false);
}
Now we can easily add a MouseEvent subscription by using the following code
rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!")));
Design-wise this solution is not the best, it uses the current design you have shown, but i do have strong reservations as to maintaining a scalable and easy to follow architecture with this approach.
I would suggest you create an adapter/converter that will generate the lambda parameters from the EventArgs class, they can be easily specified in the EventArgs by implementing an interface, that way the getArgExpression will only use the converter to generate the appropriate parameters, and you can get rid of the many if (Type == typeof(...)).
It will take me some more time to draw up that solution, but if you are really interested, i can try to write it down, and post it.
I have a DataAnnotationValidator that I created. I am currently trying to test it with a Required Field attribute and I can't get the IsValid property to fail when my property is null. It does work correctly when I create a number with a Range attribute that is outside of the specified Range.
public class TestEntityWithDataAnnotations
{
public Guid Id { get; set; }
[Required(ErrorMessage = "Required")]
public string Name { get; set; }
}
[TestFixture]
public class DataAnnotationValidatorTest
{
[Test]
public void Validate_ReturnsFailure_WhenPropertyValidationIsNotValid()
{
var validator = new DataAnnotationValidator();
var invalidEntity = new TestEntityWithDataAnnotations
{
Id = Guid.NewGuid()
};
var validationResult = validator.Validate(invalidEntity);
Assert.IsFalse(validationResult.IsValid);
}
}
public class DataAnnotationValidator
{
public ValidationResult Validate(object obj)
{
Type objType = obj.GetType();
var typeDescriptor = GetTypeDescriptor(obj, objType);
var validationResult = new ValidationResult();
var classValidationResult = CheckClassIsValid(obj, typeDescriptor);
if (!classValidationResult.IsValid)
{
validationResult.AddErrors(classValidationResult.Errors);
}
foreach (PropertyDescriptor propertyDescriptor in typeDescriptor.GetProperties())
{
// Loop over all of the properties on our object that have Validation Attributes
var propValidationResult = CheckPropertyIsValid(obj, propertyDescriptor);
if(!propValidationResult.IsValid)
{
validationResult.AddErrors(propValidationResult.Errors);
}
}
return validationResult;
}
/// <summary>
/// Checks to see if there are any class level validation attributes and runs them
/// </summary>
/// <returns></returns>
private static ValidationResult CheckClassIsValid(object obj, ICustomTypeDescriptor typeDescriptor)
{
var errors = typeDescriptor.GetAttributes().OfType<ValidationAttribute>()
.Where(x => !x.IsValid(obj))
.Select(x => new ValidationError(typeDescriptor.GetClassName(), x.ErrorMessage));
return new ValidationResult(errors.ToList());
}
/// <summary>
/// Checks to see if a property has any DataAnnotations that it has violated
/// </summary>
private static ValidationResult CheckPropertyIsValid(object obj, PropertyDescriptor propertyDescriptor)
{
var errors = propertyDescriptor.Attributes.OfType<ValidationAttribute>()
.Where(x => !x.IsValid(obj))
.Select(x => new ValidationError(propertyDescriptor.Name, x.ErrorMessage));
return new ValidationResult(errors.ToList());
}
/// <summary>
/// Gets the model's type descriptor. In order to support the buddy class metadata model
/// for LINQ to SQL and Entity Framework, it uses
/// <see cref="AssociatedMetadataTypeTypeDescriptionProvider"/>.
/// </summary>
/// <param name="obj">The model object</param>
/// <param name="objType">The type of the model object</param>
/// <returns>The model's type descriptor</returns>
private static ICustomTypeDescriptor GetTypeDescriptor(object obj, Type objType)
{
var provider = new AssociatedMetadataTypeTypeDescriptionProvider(objType);
return provider.GetTypeDescriptor(objType, obj);
}
}
A bit of stupidity on my part. I needed to pass the value of the property into IsValid inside of CheckPropertyIsValid instead of the whole object.
private static ValidationResult CheckPropertyIsValid(object obj, PropertyDescriptor propertyDescriptor)
{
var errors = propertyDescriptor.Attributes.OfType<ValidationAttribute>()
.Where(x => !x.IsValid(propertyDescriptor.GetValue(obj)))
.Select(x => new ValidationError(propertyDescriptor.Name, x.ErrorMessage));
return new ValidationResult(errors.ToList());
}