/// <summary> /// Gets a compiler code based on a filler compile type. /// </summary> /// <param name="fillCompileType">The filler compile type.</param> /// <param name="filler">The filler.</param> /// <param name="fillType">The filler type.</param> /// <param name="formattedMembers">The formatted members.</param> /// <param name="namespaces">A collection of namespaces to format into the code.</param> /// <returns>A compilable string.</returns> internal static string GetCompilerCode <TFiller>(DataIgnoreType fillCompileType, TFiller filler, Type fillType, IEnumerable <string> formattedMembers, IEnumerable <string> namespaces) where TFiller : InternalDataFiller { var code = fillCompileType == DataIgnoreType.Read ? DataCompiler.ReaderCode.Replace("@ReaderType", filler.ReaderName) : DataCompiler.WriterCode; return(code.Replace("@Type", fillType.FullName.Replace("+", ".")) .Replace("@Members", string.Join("\r\n", formattedMembers)) .Replace("@Usings", string.Join("\r\n", namespaces))); }
/// <summary> /// Generates a fill method based on a model and a filler. /// </summary> /// <returns>A fill method.</returns> internal static MethodInfo GenerateFillMethod <TModel, TBaseModel, TFiller>(DataIgnoreType fillCompileType) where TBaseModel : DataModel <TModel, TBaseModel, TFiller> where TFiller : InternalDataFiller { var fillType = typeof(TModel); MethodInfo fillerMethod; if (fillCompileType == DataIgnoreType.Read) { if (_fillerReadMethods.TryGetValue(fillType.FullName, out fillerMethod)) { return(fillerMethod); } } else // (implicit) if (fillCompileType == DataIgnoreType.Write) { if (_fillerWriteMethods.TryGetValue(fillType.FullName, out fillerMethod)) { return(fillerMethod); } } TFiller filler; if (!TryGetFiller <TFiller>(out filler)) { throw new MemberAccessException("Cannot access the filler context. Make sure the filler has an instance."); } var members = filler.CreateReadFormatInternal(DataHelper.GetPropertiesByIgnore <TModel, TBaseModel, TFiller>(null, fillCompileType)); var namespaces = new List <string>(); var formattedMembers = members.Select(member => DataHelper.FormatMember(member, fillCompileType, namespaces)); namespaces = namespaces.Distinct().ToList(); var code = DataHelper.GetCompilerCode(fillCompileType, filler, fillType, formattedMembers, namespaces); return(CompileFillMethod(code, fillType, fillCompileType)); }
/// <summary> /// Compiles the fill method based on generated code and a fill type. /// </summary> /// <param name="code">The generated code to compile.</param> /// <param name="fillType">The associated fill type.</param> /// <returns>The compiled fill method.</returns> private static MethodInfo CompileFillMethod(string code, Type fillType, DataIgnoreType fillCompilerType) { var compilerResults = DataCompiler.Compile <CSharpCodeProvider>(code); if (compilerResults.Errors.HasErrors) { Console.WriteLine(code); var errors = new List <string>(); foreach (var error in compilerResults.Errors) { errors.Add(error.ToString()); } throw new DataCompilerException("Failed to compile ...", errors); } else { var method = DataCompiler.FindMethod(compilerResults.CompiledAssembly, DataCompiler.Namespace, fillCompilerType == DataIgnoreType.Read ? DataCompiler.ReaderClassName : DataCompiler.WriterClassName, fillCompilerType == DataIgnoreType.Read ? DataCompiler.ReadMethod : DataCompiler.WriteMethod); if (method != null) { if (fillCompilerType == DataIgnoreType.Read) { _fillerReadMethods.TryAdd(fillType.FullName, method); } else // (implicit) if (fillCompilerType == DataIgnoreType.Write) { _fillerWriteMethods.TryAdd(fillType.FullName, method); } } return(method); } }
/// <summary> /// Generates a fill method internally. /// </summary> /// <returns>The generated fill method.</returns> internal MethodInfo GenerateFillMethodInternal <TModel, TBaseModel, TFiller>(DataIgnoreType fillCompileType) where TBaseModel : DataModel <TModel, TBaseModel, TFiller> where TFiller : InternalDataFiller { return(DataFillGenerator.GenerateFillMethod <TModel, TBaseModel, TFiller>(fillCompileType)); }
/// <summary> /// Gets all properties by a specific ignore type. /// </summary> /// <param name="model">The model.</param> /// <param name="handleType">The type to handle. (Not ignore.)</param> /// <returns>An IEnumerable of PropertyInfo which is the properties.</returns> public static IEnumerable <PropertyInfo> GetPropertiesByIgnore <TModel, TBaseModel, TFiller>(TBaseModel model, DataIgnoreType handleType) where TBaseModel : DataModel <TModel, TBaseModel, TFiller> where TFiller : InternalDataFiller { if (model != null) { switch (handleType) { case DataIgnoreType.Read: if (model.ReadMembers != null) { return(model.ReadMembers); } break; case DataIgnoreType.Write: if (model.WriteMembers != null) { return(model.WriteMembers); } break; default: throw new ArgumentException("handleType"); } } var properties = typeof(TModel).GetProperties().Where(property => { var ignoreAttribute = property.GetCustomAttribute <DataIgnoreAttribute>(); return(ignoreAttribute == null || (ignoreAttribute.IgnoreType != handleType && ignoreAttribute.IgnoreType != DataIgnoreType.Both)); }).ToList(); if (model != null) { switch (handleType) { case DataIgnoreType.Read: model.ReadMembers = properties; break; case DataIgnoreType.Write: model.WriteMembers = properties; break; default: throw new ArgumentException("handleType"); } } return(properties); }
/// <summary> /// Formats a DataReadInfo member into a compilable string. /// </summary> /// <param name="member">The member.</param> /// <param name="fillCompileType">The filler compile type.</param> /// <param name="namespaces">A collection of namespaces where custom namespaces are added.</param> /// <returns>A string that's compilable.</returns> internal static string FormatMember(DataReadInfo member, DataIgnoreType fillCompileType, ICollection <string> namespaces) { // Reader .../// const string READ_FORMAT = "reader.Get<{0}>(\"{1}\")"; const string SET_CUSTOM_FORMAT = "model.{0} = {1};"; const string SET_FORMAT = "model.{0} = ({1}){2};"; // Writer ... const string WRITE_FORMAT = "if (predicate.Invoke(new KeyValuePair<string,object>(\"{0}\", model.{1}))) {{ members.Add(\"{0}\", model.{1}); }}"; const string WRITE_TIMESTAMP_FORMAT = "model.{0} = DateTime.UtcNow; if (predicate.Invoke(new KeyValuePair<string,object>(\"{0}\", model.{1}))) {{ members.Add(\"{0}\", model.{1}); }}"; const string WRITE_STRING_FORMAT = "if (predicate.Invoke(new KeyValuePair<string,object>(\"{0}\", model.{1}.ToString()))) {{ members.Add(\"{0}\", model.{1}.ToString()); }}"; if (fillCompileType == DataIgnoreType.Read) { var custom = (member as DataReadCustomInfo); if (custom != null) { if (custom.AssociatedNamespaces != null) { namespaces.AddRange(custom.AssociatedNamespaces.Select(ns => string.Format("using {0};", ns))); } var value = string.Format(READ_FORMAT, typeof(string).Name, member.ReadName); return(string.Format(SET_CUSTOM_FORMAT, member.Member.Name, custom.ReadFormat.Replace("@value", value))); } else { var value = string.Format(READ_FORMAT, member.TypeName, member.ReadName); return(string.Format(SET_FORMAT, member.Member.Name, member.TypeName, value)); } } else // (implicit) if (fillCompileType == DataIgnoreType.Write) { var custom = (member as DataReadCustomInfo); if (custom != null) { if (custom.AssociatedNamespaces != null) { namespaces.AddRange(custom.AssociatedNamespaces.Select(ns => string.Format("using {0};", ns))); } } var specialAttribute = member.Member.GetCustomAttribute <DataSpecialTypeAttribute>(); if (specialAttribute != null) { // implement more ... // use switch ... switch (specialAttribute.DataType) { case SpecialDataType.Timestamp: { return(string.Format(WRITE_TIMESTAMP_FORMAT, member.ReadName, member.Member.Name)); } case SpecialDataType.AsString: { return(string.Format(WRITE_STRING_FORMAT, member.ReadName, member.Member.Name)); } } } return(string.Format(WRITE_FORMAT, member.ReadName, member.Member.Name)); } }