Last active
February 12, 2019 21:02
-
-
Save DrSammyD/11387939 to your computer and use it in GitHub Desktop.
An expression generator to deep copy entities from one type to another, preserving reference loops.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
namespace EntityExpressionCopy | |
{ | |
public static class CopyToExt | |
{ | |
private static readonly ConcurrentDictionary<Type, LambdaExpression> CrateMergeStackFunc; | |
private static readonly ConcurrentDictionary<Type, Object> AssignDict; | |
public static MethodInfo AsQueryableMethod; | |
public static MethodInfo SelectMethod; | |
public static MethodInfo ToListMethod; | |
public static MethodInfo CopyToMethod; | |
public static MethodInfo CreateMergeMethod; | |
public static MethodInfo TryCopyMethod; | |
static CopyToExt() | |
{ | |
AssignDict = new ConcurrentDictionary<Type, Object>(new Dictionary<Type, Object>()); | |
CrateMergeStackFunc = new ConcurrentDictionary<Type, LambdaExpression>(new Dictionary<Type, LambdaExpression>()); | |
foreach (MethodInfo m in typeof(Queryable).GetMethods().Where(m => m.Name == "Select")) | |
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("selector"))) | |
if (p.ParameterType.GetGenericArguments().Any(x => x.GetGenericArguments().Count() == 2)) | |
SelectMethod = (MethodInfo)p.Member; | |
foreach (MethodInfo m in typeof(Enumerable).GetMethods().Where(m => m.Name == "ToList")) | |
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("source"))) | |
if (p.ParameterType.GetGenericArguments().Count() == 1) | |
ToListMethod = (MethodInfo)p.Member; | |
foreach (MethodInfo m in typeof(Queryable).GetMethods().Where(m => m.Name == "AsQueryable")) | |
foreach (ParameterInfo p in m.GetParameters().Where(p => p.Name.Equals("source"))) | |
if (p.ParameterType.GetGenericArguments().Count() == 1) | |
AsQueryableMethod = (MethodInfo)p.Member; | |
TryCopyMethod = typeof(MergeStack).GetMethods().First(m => m.Name == "TryCopy"); | |
} | |
public static IQueryable<TDest> CopyTo<TSrc, TDest>(this IQueryable<TSrc> queryable) | |
where TSrc : class, IEntity | |
where TDest : class, IEntity | |
{ | |
return queryable.AsEnumerable().BaseCopyTo<TSrc, TDest>().AsQueryable(); | |
} | |
public static IEnumerable<TDest> CopyTo<TSrc, TDest>(this IEnumerable<TSrc> queryable) | |
where TSrc : class, IEntity | |
where TDest : class, IEntity | |
{ | |
return queryable.BaseCopyTo<TSrc, TDest>(); | |
} | |
private static IEnumerable<TDest> BaseCopyTo<TSrc, TDest>(this IEnumerable<TSrc> enumerable) | |
{ | |
var copyExpr = CreateTryCopy<TSrc, TDest>(); | |
var merge = new MergeStack(); | |
var ret= enumerable.Select(x=> new Stackholder<TSrc>{Ms = merge,Src = x}).AsQueryable().Select(copyExpr); | |
return ret; | |
} | |
public static Expression<Func<Stackholder<TSrc>, TDest>> CreateTryCopy<TSrc, TDest>() | |
{ | |
return CrateMergeStackFunc.GetOrAdd(typeof(Func<Stackholder<TSrc>, TDest>), (type) => | |
{ | |
var shtype = typeof(Stackholder<TSrc>); | |
var parentParam = Expression.Parameter(shtype); | |
var srcExpr = Expression.PropertyOrField(parentParam, "Src"); | |
var msExpr = Expression.PropertyOrField(parentParam, "Ms"); | |
var tcMethod = Expression.Call(msExpr, | |
TryCopyMethod.MakeGenericMethod(new Type[] { typeof(TSrc), typeof(TDest) }), | |
srcExpr); | |
return Expression.Lambda<Func<Stackholder<TSrc>, TDest>>(tcMethod, parentParam); | |
}) as Expression<Func<Stackholder<TSrc>, TDest>>; | |
} | |
internal static bool Impliments(this Type type, Type inheritedType) | |
{ | |
return (type.IsSubclassOf(inheritedType) || type.GetInterface(inheritedType.FullName) != null); | |
} | |
internal static bool Impliments<T>(this Type type, Type inheritedType = null) | |
{ | |
return type.Impliments(typeof(T)); | |
} | |
internal static Func<TSrc, TDest, MergeStack, TDest> Assign<TSrc, TDest>() | |
{ | |
return (Func<TSrc, TDest, MergeStack, TDest>)AssignDict.GetOrAdd(typeof(Func<TSrc, TDest>), (indexType) => | |
{ | |
var tSrc = typeof(TSrc); | |
var tDest = typeof(TDest); | |
var srcEntityInterfaces = tSrc.GetInterfaces().Where(x => x.Impliments<IEntity>()).ToArray(); | |
var destEntityInterfaces = tDest.GetInterfaces().Where(x => x.Impliments<IEntity>()).ToArray(); | |
var srcParam = Expression.Parameter(tSrc); | |
var destParam = Expression.Parameter(tDest); | |
var msExpr = Expression.Parameter(typeof(MergeStack)); | |
var common = destEntityInterfaces.Intersect(srcEntityInterfaces).ToArray(); | |
var memberbindings = common.Where(x => x.Impliments<IEntityProperty>()) | |
.Select(type => type.GetProperties().First()) | |
.Select( | |
prop => | |
Expression.Assign(Expression.Property(destParam, prop.Name), | |
Expression.Property(srcParam, prop.Name))) | |
.Cast<Expression>().ToList(); | |
foreach (var type in common.Where(x => x.Impliments<IEntityObject>())) | |
{ | |
var destSubType = destEntityInterfaces.First(x => x.Impliments(type)) | |
.GetGenericArguments() | |
.First(); | |
var srcSubType = srcEntityInterfaces.First(x => x.Impliments(type)) | |
.GetGenericArguments() | |
.First(); | |
var dProp = destEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First(); | |
var sProp = srcEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First(); | |
var tcParam = Expression.Parameter(srcSubType); | |
var tcMethod = Expression.Call(msExpr, | |
TryCopyMethod.MakeGenericMethod(new Type[] { srcSubType, destSubType }), | |
tcParam); | |
LambdaExpression mergeLambda = Expression.Lambda(tcMethod, tcParam); | |
MemberExpression memberExpression = Expression.Property(srcParam, sProp.Name); | |
InvocationExpression invocationExpression = Expression.Invoke(mergeLambda, | |
Expression.Property(srcParam, sProp.Name)); | |
var check = Expression.Condition( | |
Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, | |
Expression.Constant(null, sProp.PropertyType)), invocationExpression, | |
Expression.Constant(null, invocationExpression.Type)); | |
BinaryExpression binaryExpression = Expression.Assign(Expression.Property(destParam, dProp.Name), | |
check); | |
memberbindings.Add(binaryExpression); | |
} | |
foreach (var type in common.Where(x => x.Impliments<IEntityCollection>())) | |
{ | |
var destSubType = destEntityInterfaces.First(x => x.Impliments(type)) | |
.GetGenericArguments() | |
.First(); | |
var srcSubType = srcEntityInterfaces.First(x => x.Impliments(type)) | |
.GetGenericArguments() | |
.First(); | |
var dProp = destEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First(); | |
var sProp = srcEntityInterfaces.First(x => x.Impliments(type)).GetProperties().First(); | |
var tcParam = Expression.Parameter(srcSubType); | |
var tcMethod = Expression.Call(msExpr, | |
TryCopyMethod.MakeGenericMethod(new Type[] { srcSubType, destSubType }), | |
tcParam); | |
LambdaExpression mergeLambda = Expression.Lambda(tcMethod, tcParam); | |
var memberExpression = Expression.Property(srcParam, sProp.Name); | |
var selectExpr = Expression.Call(null, | |
AsQueryableMethod.MakeGenericMethod(new Type[] { srcSubType }), | |
new Expression[] { memberExpression }); | |
selectExpr = Expression.Call(null, | |
CopyToExt.SelectMethod.MakeGenericMethod(new Type[] { srcSubType, destSubType }), | |
new Expression[] { selectExpr, mergeLambda }); | |
selectExpr = Expression.Call(null, | |
CopyToExt.ToListMethod.MakeGenericMethod(new Type[] { destSubType }), | |
new Expression[] { selectExpr }); | |
var check = Expression.Condition( | |
Expression.MakeBinary(ExpressionType.NotEqual, memberExpression, | |
Expression.Constant(null, sProp.PropertyType)), selectExpr, | |
Expression.Constant(null, selectExpr.Type)); | |
memberbindings.Add(Expression.Assign(Expression.Property(destParam, dProp.Name), check)); | |
} | |
memberbindings.Add(destParam); | |
return | |
Expression.Lambda<Func<TSrc, TDest, MergeStack, TDest>>(Expression.Block(memberbindings), | |
new ParameterExpression[] { srcParam, destParam, msExpr }).Compile(); | |
}); | |
} | |
} | |
public class Stackholder<TSrc> | |
{ | |
public MergeStack Ms; | |
public TSrc Src; | |
} | |
public class Enumerableholder<TSrc> | |
{ | |
public IEnumerable Key; | |
public TSrc Src; | |
} | |
public class MergeStack | |
{ | |
private readonly Dictionary<Type, Dictionary<Object, Object>> _mergeObjDict = new Dictionary<Type, Dictionary<object, object>>(); | |
public TDest TryCopy<TSrc, TDest>(TSrc Src) | |
where TSrc : class, IEntity | |
where TDest : class, IEntity, new() | |
{ | |
if (Src == null) return null; | |
var objToIndex = new TDest(); | |
Dictionary<object, object> objToObj; | |
if (!_mergeObjDict.ContainsKey(objToIndex.GetType())) | |
{ | |
objToObj = new Dictionary<object, object>(); | |
_mergeObjDict.Add(objToIndex.GetType(), objToObj); | |
} | |
else | |
{ | |
objToObj = _mergeObjDict[objToIndex.GetType()]; | |
} | |
if (!objToObj.ContainsKey(Src)) | |
{ | |
objToObj.Add(Src, objToIndex); | |
return CopyToExt.Assign<TSrc, TDest>()(Src, objToIndex, this); | |
} | |
return objToObj[Src] as TDest; | |
} | |
} | |
public interface IEntity | |
{ | |
} | |
public interface IEntityObject : IEntity | |
{ | |
} | |
public interface IEntityCollection : IEntity | |
{ | |
} | |
public interface IEntityProperty : IEntity | |
{ | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#@ template language="C#" debug="false" hostspecific="true"#> | |
<#@ include file="EF.Utility.CS.ttinclude"#><#@ | |
output extension=".cs"#><# | |
const string inputFile = @"Model.edmx"; | |
var textTransform = DynamicTextTransformation.Create(this); | |
var code = new CodeGenerationTools(this); | |
var ef = new MetadataTools(this); | |
var typeMapper = new TypeMapper(code, ef, textTransform.Errors); | |
var fileManager = EntityFrameworkTemplateFileManager.Create(this); | |
var itemCollection = | |
new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); | |
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); | |
if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) | |
{ | |
return string.Empty; | |
} | |
WriteHeader(codeStringGenerator, fileManager); | |
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) | |
{ | |
fileManager.StartNewFile(entity.Name + ".cs"); | |
BeginNamespace(code); | |
#> | |
<#=codeStringGenerator.UsingDirectives(inHeader: false) #> | |
<# | |
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); | |
var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); | |
var complexProperties = typeMapper.GetComplexProperties(entity); | |
var simpleProperties = typeMapper.GetSimpleProperties(entity); | |
var navigationProperties = typeMapper.GetNavigationProperties(entity); | |
#> | |
<#=codeStringGenerator.EntityInterfaceOpening(entity) #> | |
{ | |
} | |
<# | |
if (simpleProperties.Any()) | |
{ | |
foreach (var edmProperty in simpleProperties) | |
{ | |
if (CodeStringGenerator.HasContract(edmProperty)) | |
{ | |
#> | |
<#=codeStringGenerator.EntityInterfaceOpening(entity, edmProperty) | |
#>, IEntityProperty | |
{ | |
<#=codeStringGenerator.Contract(edmProperty) #> | |
} | |
<# | |
} | |
} | |
} | |
if (complexProperties.Any()) | |
{ | |
#> | |
<# | |
foreach (var complexProperty in complexProperties) | |
{ | |
if (CodeStringGenerator.HasContract(complexProperty)) | |
{ | |
#> | |
<#=codeStringGenerator.EntityInterfaceOpening(entity, complexProperty) | |
#> | |
{ | |
<#=codeStringGenerator.Contract(complexProperty) #> | |
} | |
<# | |
} | |
} | |
} | |
if (navigationProperties.Any()) | |
{ | |
#> | |
<# | |
foreach (var navigationProperty in navigationProperties) | |
{ | |
if (CodeStringGenerator.HasContract(navigationProperty)) | |
{ | |
#> | |
<#=codeStringGenerator.EmptyInterfaceOpening(entity,navigationProperty)#>{} | |
<#=codeStringGenerator.EntityInterfaceOpening(entity, navigationProperty) | |
#> <#=codeStringGenerator.EntityInterfaceBaseOpening(entity,navigationProperty)#> | |
{ | |
<#=codeStringGenerator.NavigationContract(navigationProperty) #> | |
} | |
<# | |
} | |
} | |
} | |
#> | |
<#= | |
codeStringGenerator.EntityInterfaceOpening(entity, simpleProperties, complexProperties, navigationProperties) | |
#> | |
{ | |
} | |
<#=codeStringGenerator.EntityClassOpening(entity, simpleProperties, complexProperties, navigationProperties) #> | |
{ | |
<# | |
if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) | |
{ | |
#> | |
public <#=code.Escape(entity) #>() | |
{ | |
<# | |
foreach (var edmProperty in propertiesWithDefaultValues) | |
{ | |
#> | |
this.<#=code.Escape(edmProperty) #> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue) #>; | |
<# | |
} | |
foreach (var navigationProperty in collectionNavigationProperties) | |
{ | |
#> | |
this.<#=code.Escape(navigationProperty) #> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()) | |
#>>(); | |
<# | |
} | |
foreach (var complexProperty in complexProperties) | |
{ | |
#> | |
this.<#=code.Escape(complexProperty) #> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage) #>(); | |
<# | |
} | |
#> | |
} | |
<# | |
} | |
if (simpleProperties.Any()) | |
{ | |
foreach (var edmProperty in simpleProperties) | |
{ | |
#> | |
<#=codeStringGenerator.Property(edmProperty) #> | |
<# | |
} | |
} | |
if (complexProperties.Any()) | |
{ | |
#> | |
<# | |
foreach (var complexProperty in complexProperties) | |
{ | |
#> | |
<#=codeStringGenerator.Property(complexProperty) #> | |
<# | |
} | |
} | |
if (navigationProperties.Any()) | |
{ | |
#> | |
<# | |
foreach (var navigationProperty in navigationProperties) | |
{ | |
#> | |
<#=codeStringGenerator.NavigationProperty(navigationProperty) #> | |
<# | |
} | |
} | |
#> | |
} | |
<# | |
EndNamespace(code); | |
} | |
foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection)) | |
{ | |
fileManager.StartNewFile(complex.Name + ".cs"); | |
BeginNamespace(code); | |
#> | |
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false) | |
#> | |
<#=Accessibility.ForType(complex) #> partial class <#=code.Escape(complex) #> | |
{ | |
<# | |
var complexProperties = typeMapper.GetComplexProperties(complex); | |
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); | |
if (propertiesWithDefaultValues.Any() || complexProperties.Any()) | |
{ | |
#> | |
public <#=code.Escape(complex) #>() | |
{ | |
<# | |
foreach (var edmProperty in propertiesWithDefaultValues) | |
{ | |
#> | |
this.<#=code.Escape(edmProperty) #> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue) #>; | |
<# | |
} | |
foreach (var complexProperty in complexProperties) | |
{ | |
#> | |
this.<#=code.Escape(complexProperty) #> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage) #>(); | |
<# | |
} | |
#> | |
} | |
<# | |
} | |
var simpleProperties = typeMapper.GetSimpleProperties(complex); | |
if (simpleProperties.Any()) | |
{ | |
foreach (var edmProperty in simpleProperties) | |
{ | |
#> | |
<#=codeStringGenerator.Property(edmProperty) #> | |
<# | |
} | |
} | |
if (complexProperties.Any()) | |
{ | |
#> | |
<# | |
foreach (var edmProperty in complexProperties) | |
{ | |
#> | |
<#=codeStringGenerator.Property(edmProperty) #> | |
<# | |
} | |
} | |
#> | |
} | |
<# | |
EndNamespace(code); | |
} | |
foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) | |
{ | |
fileManager.StartNewFile(enumType.Name + ".cs"); | |
BeginNamespace(code); | |
#> | |
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false) | |
#> | |
<# | |
if (typeMapper.EnumIsFlags(enumType)) | |
{ | |
#> | |
[Flags] | |
<# | |
} | |
#> | |
<#=codeStringGenerator.EnumOpening(enumType) #> | |
{ | |
<# | |
var foundOne = false; | |
foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) | |
{ | |
foundOne = true; | |
#> | |
<#=code.Escape(typeMapper.GetEnumMemberName(member)) #> = <#=typeMapper.GetEnumMemberValue(member) #>, | |
<# | |
} | |
if (foundOne) | |
{ | |
this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); | |
} | |
#> | |
} | |
<# | |
EndNamespace(code); | |
} | |
fileManager.Process(); | |
#> | |
<#+ | |
public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) | |
{ | |
fileManager.StartHeader(); | |
#> | |
//------------------------------------------------------------------------------ | |
// <auto-generated> | |
// <#=GetResourceString("Template_GeneratedCodeCommentLine1") #> | |
// | |
// <#=GetResourceString("Template_GeneratedCodeCommentLine2") #> | |
// <#=GetResourceString("Template_GeneratedCodeCommentLine3") #> | |
// </auto-generated> | |
//------------------------------------------------------------------------------ | |
<#=codeStringGenerator.UsingDirectives(inHeader: true) #> | |
<#+ | |
fileManager.EndBlock(); | |
} | |
public void BeginNamespace(CodeGenerationTools code) | |
{ | |
var codeNamespace = code.VsNamespaceSuggestion(); | |
if (!String.IsNullOrEmpty(codeNamespace)) | |
{ | |
#> | |
namespace <#=code.EscapeNamespace(codeNamespace) #> | |
{ | |
<#+ | |
PushIndent(" "); | |
} | |
} | |
public void EndNamespace(CodeGenerationTools code) | |
{ | |
if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion())) | |
{ | |
PopIndent(); | |
#> | |
} | |
<#+ | |
} | |
} | |
public const string TemplateId = "CSharp_DbContext_Types_EF5"; | |
public class CodeStringGenerator | |
{ | |
private readonly CodeGenerationTools _code; | |
private readonly TypeMapper _typeMapper; | |
private readonly MetadataTools _ef; | |
public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) | |
{ | |
ArgumentNotNull(code, "code"); | |
ArgumentNotNull(typeMapper, "typeMapper"); | |
ArgumentNotNull(ef, "ef"); | |
_code = code; | |
_typeMapper = typeMapper; | |
_ef = ef; | |
} | |
public string EntityVar(EntityType entity) | |
{ | |
return (_code.Escape(entity)).ToLowerInvariant(); | |
} | |
public string SrcProperty(EntityType entity, EdmMember edmMember) | |
{ | |
return src(ContractInterfaceName(entity, edmMember)); | |
} | |
public string DestProperty(EntityType entity, EdmMember edmMember) | |
{ | |
return dest(ContractInterfaceName(entity, edmMember)); | |
} | |
private string dest(string name) | |
{ | |
return BaseDestName() + name; | |
} | |
private string src(string name) | |
{ | |
return BaseSrcName() + name; | |
} | |
public string Property(EdmProperty edmProperty) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1} {2} {{ {3}get; {4}set; }}", | |
Accessibility.ForProperty(edmProperty), | |
_typeMapper.GetTypeName(edmProperty.TypeUsage), | |
_code.Escape(edmProperty), | |
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)), | |
_code.SpaceAfter(Accessibility.ForSetter(edmProperty))); | |
} | |
public string Contract(EdmProperty edmProperty) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1} {{ get; set; }}", | |
_typeMapper.GetInterfaceName(edmProperty.TypeUsage), | |
_code.Escape(edmProperty)); | |
} | |
public string GetContractOpening(EntityType entity, EdmMember edmProperty, bool fromEntity=false) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"public static {1} {0}(this {2} {3})", | |
GetContractName(edmProperty), | |
_typeMapper.GetInterfaceName(edmProperty.TypeUsage.EdmType, true, null), | |
fromEntity ? _code.Escape(entity) : BaseInterfaceName(entity), | |
BaseSrcName()); | |
} | |
public string SetContractOpening(EntityType entity,EdmMember edmMember, bool withMergeStack =false, bool toEntity=false) | |
{ | |
string accessibility = withMergeStack ? "internal" : "public"; | |
string mergeStackProp = withMergeStack ? ", MergeStack ms" : ""; | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{6} static void {0}(this {1} {2}, {3} {4}{5})", | |
SetContractName(edmMember), | |
toEntity ? _code.Escape(entity) : BaseInterfaceName(entity), | |
BaseDestName(), | |
_typeMapper.GetInterfaceName(edmMember.TypeUsage.EdmType, true, null), | |
BaseSrcName(), | |
mergeStackProp, | |
accessibility); | |
} | |
public string GetContractName(EdmMember edmMember) | |
{ | |
return "Get_" + _code.Escape(edmMember); | |
} | |
public string SetContractName(EdmMember edmMember) | |
{ | |
return "Set_" + _code.Escape(edmMember); | |
} | |
public String BaseSrcName() | |
{ | |
return "src"; | |
} | |
public String BaseDestName() | |
{ | |
return "dest"; | |
} | |
public string ContractNew(EdmProperty edmProperty) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}()", | |
_typeMapper.GetInterfaceName(edmProperty.TypeUsage), | |
"New" + _code.Escape(edmProperty)); | |
} | |
public static bool HasContract(EdmMember edmMember) | |
{ | |
return Accessibility.ForProperty(edmMember) == "public" || | |
(Accessibility.ForGetter(edmMember) == "public" && | |
Accessibility.ForSetter(edmMember) == "public"); | |
} | |
public static bool RelationshipMany(NavigationProperty navigationProperty) | |
{ | |
return navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many; | |
} | |
public string NavigationProperty(NavigationProperty navigationProperty) | |
{ | |
var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1} {2} {{ {3}get; {4}set; }}", | |
AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), | |
navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many | |
? ("ICollection<" + endType + ">") | |
: endType, | |
_code.Escape(navigationProperty), | |
_code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), | |
_code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); | |
} | |
public string NavigationContract(NavigationProperty navigationProperty) | |
{ | |
var endType = _typeMapper.GetInterfaceName(navigationProperty.ToEndMember.GetEntityType()); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{1} {2} {{ {3}get; {4}set; }}", | |
Accessibility.ForProperty(navigationProperty), | |
navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many | |
? ("ICollection<" + endType + ">") | |
: endType, | |
_code.Escape(navigationProperty), | |
_code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), | |
_code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); | |
} | |
public string NavigationContractNew(NavigationProperty navigationProperty) | |
{ | |
var endType = _typeMapper.GetInterfaceName(navigationProperty.ToEndMember.GetEntityType()); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}();", | |
endType, | |
NavigationContractNewName(navigationProperty)); | |
} | |
public string NavigationContractNewName(NavigationProperty navigationProperty) | |
{ | |
return "New" + _code.Escape((navigationProperty)); | |
} | |
public string AccessibilityAndVirtual(string accessibility) | |
{ | |
return accessibility + (accessibility != "private" ? " virtual" : ""); | |
} | |
public string EntityClassOpening(EntityType entity, | |
IEnumerable<EdmProperty> edmProperties, | |
IEnumerable<EdmProperty> complexProperties, | |
IEnumerable<NavigationProperty> navigationProperties) | |
{ | |
var edmInterfaces = edmProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)); | |
var complexInterfaces = complexProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)+"<"+">"); | |
var navigationInterfaces = | |
navigationProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)+"<"+_typeMapper.GetTypeName(x.ToEndMember.GetEntityType())+">"); | |
var interfaces = edmInterfaces.Concat(complexInterfaces).Concat(navigationInterfaces); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}partial class {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
_code.Escape(entity), | |
_code.StringBefore(" : ", String.Join(", \n ", interfaces))); | |
} | |
public string EntityInterfaceOpening(EntityType entity) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
BaseInterfaceName(entity), | |
_code.StringBefore(" : ", null)); | |
} | |
public string EntityInterfaceOpening(EntityType entity, EdmProperty edmProperty) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
ContractInterfaceName(entity, edmProperty), | |
_code.StringBefore(" : ", BaseInterfaceName(entity))); | |
} | |
public string EntityExtensionOpening(EntityType entity) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} static class {1}", | |
Accessibility.ForType(entity), | |
_typeMapper.GetInterfaceName(entity) + "Ext"); | |
} | |
public string ContractInterfaceName(EntityType entity, EdmMember edmMember) | |
{ | |
return BaseInterfaceName(entity) + "_" + _code.Escape(edmMember); | |
} | |
public string ContractInterfaceName(EntityType entity) | |
{ | |
return BaseInterfaceName(entity) + "__All"; | |
} | |
public string BaseInterfaceName(EntityType entity) | |
{ | |
return "I" + _code.Escape(entity); | |
} | |
public string EntityInterfaceOpening(EntityType entity, | |
IEnumerable<EdmProperty> edmProperties, | |
IEnumerable<EdmProperty> complexProperties, | |
IEnumerable<NavigationProperty> navigationProperties) | |
{ | |
var edmInterfaces = edmProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)); | |
var complexInterfaces = complexProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)); | |
var navigationInterfaces = | |
navigationProperties.Where(HasContract).Select(x => ContractInterfaceName(entity, x)); | |
var interfaces = edmInterfaces; | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
ContractInterfaceName(entity), | |
_code.StringBefore(" : ", String.Join(", \n ", interfaces))); | |
} | |
public string EntityInterfaceOpening(EntityType entity, NavigationProperty navigationProperty) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
ContractInterfaceName(entity, navigationProperty)+"<T>", | |
_code.StringBefore(" : ", ContractInterfaceName(entity, navigationProperty).Split('<').First())); | |
} | |
public string EmptyInterfaceOpening(EntityType entity, NavigationProperty navigationProperty) | |
{ | |
String type = _typeMapper.GetTypeName(navigationProperty.TypeUsage); | |
String bt = type.Contains("Collection")?"Collection":"Object"; | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
ContractInterfaceName(entity, navigationProperty), | |
_code.StringBefore(" : ", BaseInterfaceName(entity)+", IEntity"+bt)); | |
} | |
public string EntityInterfaceBaseOpening(EntityType entity, NavigationProperty navigationProperty) | |
{ | |
String type = _typeMapper.GetTypeName(navigationProperty.TypeUsage); | |
type = _typeMapper.GetTypeName(navigationProperty.TypeUsage).Split('<').Last(); | |
type = type.Split('>').First(); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
" where T : class, I{0}, new()", | |
type | |
); | |
} | |
public string EntityBaseInterfaceOpening(EntityType entity) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1}interface {2}{3}", | |
Accessibility.ForType(entity), | |
_code.SpaceAfter(_code.AbstractOption(entity)), | |
BaseInterfaceName(entity), | |
_code.StringBefore(" : ", null)); | |
} | |
public string EnumOpening(SimpleType enumType) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} enum {1} : {2}", | |
Accessibility.ForType(enumType), | |
_code.Escape(enumType), | |
_code.Escape(_typeMapper.UnderlyingClrType(enumType))); | |
} | |
public void WriteFunctionParameters(EdmFunction edmFunction, | |
Action<string, string, string, string> writeParameter) | |
{ | |
var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); | |
foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) | |
{ | |
var isNotNull = parameter.IsNullableOfT | |
? parameter.FunctionParameterName + ".HasValue" | |
: parameter.FunctionParameterName + " != null"; | |
var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + | |
parameter.FunctionParameterName + ")"; | |
var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + | |
parameter.RawClrTypeName + "))"; | |
writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); | |
} | |
} | |
public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) | |
{ | |
var parameters = _typeMapper.GetParameters(edmFunction); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} IQueryable<{1}> {2}({3})", | |
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), | |
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), | |
_code.Escape(edmFunction), | |
string.Join(", ", | |
parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray())); | |
} | |
public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) | |
{ | |
var parameters = _typeMapper.GetParameters(edmFunction); | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"return ((IObjectContextAdapter)this).ObjectCongtext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", | |
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), | |
edmFunction.NamespaceName, | |
edmFunction.Name, | |
string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), | |
_code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); | |
} | |
public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) | |
{ | |
var parameters = _typeMapper.GetParameters(edmFunction); | |
var returnType = _typeMapper.GetReturnType(edmFunction); | |
var paramList = String.Join(", ", | |
parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()); | |
if (includeMergeOption) | |
{ | |
paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; | |
} | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} {1} {2}({3})", | |
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), | |
returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", | |
_code.Escape(edmFunction), | |
paramList); | |
} | |
public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) | |
{ | |
var parameters = _typeMapper.GetParameters(edmFunction); | |
var returnType = _typeMapper.GetReturnType(edmFunction); | |
var callParams = _code.StringBefore(", ", | |
String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); | |
if (includeMergeOption) | |
{ | |
callParams = ", mergeOption" + callParams; | |
} | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", | |
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", | |
edmFunction.Name, | |
callParams); | |
} | |
public string DbSet(EntitySet entitySet) | |
{ | |
return string.Format( | |
CultureInfo.InvariantCulture, | |
"{0} DbSet<{1}> {2} {{ get; set; }}", | |
Accessibility.ForReadOnlyProperty(entitySet), | |
_typeMapper.GetTypeName(entitySet.ElementType), | |
_code.Escape(entitySet)); | |
} | |
public string UsingDirectives(bool inHeader, bool includeCollections = true) | |
{ | |
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) | |
? string.Format( | |
CultureInfo.InvariantCulture, | |
"{0}using System;{1}" + | |
"{2}", | |
inHeader ? Environment.NewLine : "", | |
includeCollections | |
? (Environment.NewLine + "using System.Collections.Generic;") | |
: "", | |
inHeader ? "" : Environment.NewLine) | |
: ""; | |
} | |
} | |
public class TypeMapper | |
{ | |
private const string ExternalTypeNameAttributeName = | |
@"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; | |
private readonly System.Collections.IList _errors; | |
private readonly CodeGenerationTools _code; | |
private readonly MetadataTools _ef; | |
public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) | |
{ | |
ArgumentNotNull(code, "code"); | |
ArgumentNotNull(ef, "ef"); | |
ArgumentNotNull(errors, "errors"); | |
_code = code; | |
_ef = ef; | |
_errors = errors; | |
} | |
public string GetTypeName(TypeUsage typeUsage) | |
{ | |
return typeUsage == null | |
? null | |
: GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); | |
} | |
public string GetTypeName(EdmType edmType) | |
{ | |
return GetTypeName(edmType, isNullable: null, modelNamespace: null); | |
} | |
public string GetTypeName(TypeUsage typeUsage, string modelNamespace) | |
{ | |
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); | |
} | |
public string GetTypeName(EdmType edmType, string modelNamespace) | |
{ | |
return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); | |
} | |
public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) | |
{ | |
if (edmType == null) | |
{ | |
return null; | |
} | |
var collectionType = edmType as CollectionType; | |
if (collectionType != null) | |
{ | |
return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", | |
GetTypeName(collectionType.TypeUsage, modelNamespace)); | |
} | |
var typeName = _code.Escape(edmType.MetadataProperties | |
.Where(p => p.Name == ExternalTypeNameAttributeName) | |
.Select(p => (string) p.Value) | |
.FirstOrDefault()) | |
?? (modelNamespace != null && edmType.NamespaceName != modelNamespace | |
? _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) | |
: _code.Escape(edmType)); | |
if (edmType is StructuralType) | |
{ | |
return typeName; | |
} | |
if (edmType is SimpleType) | |
{ | |
var clrType = UnderlyingClrType(edmType); | |
if (!IsEnumType(edmType)) | |
{ | |
typeName = _code.Escape(clrType); | |
} | |
return clrType.IsValueType && isNullable == true | |
? String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) | |
: typeName; | |
} | |
throw new ArgumentException("edmType"); | |
} | |
public string GetInterfaceName(TypeUsage typeUsage) | |
{ | |
return typeUsage == null | |
? null | |
: GetInterfaceName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); | |
} | |
public string GetInterfaceName(EdmType edmType) | |
{ | |
return GetInterfaceName(edmType, isNullable: null, modelNamespace: null); | |
} | |
public string GetInterfaceName(TypeUsage typeUsage, string modelNamespace) | |
{ | |
return typeUsage == null | |
? null | |
: GetInterfaceName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); | |
} | |
public string GetInterfaceName(EdmType edmType, string modelNamespace) | |
{ | |
return GetInterfaceName(edmType, isNullable: null, modelNamespace: modelNamespace); | |
} | |
public string GetInterfaceName(EdmType edmType, bool? isNullable, string modelNamespace) | |
{ | |
if (edmType == null) | |
{ | |
return null; | |
} | |
var collectionType = edmType as CollectionType; | |
if (collectionType != null) | |
{ | |
return String.Format(CultureInfo.InvariantCulture, "ICollection<T>", | |
GetInterfaceName(collectionType.TypeUsage, modelNamespace)); | |
} | |
var typeName = _code.Escape(edmType.MetadataProperties | |
.Where(p => p.Name == ExternalTypeNameAttributeName) | |
.Select(p => (string) p.Value) | |
.FirstOrDefault()) | |
?? (modelNamespace != null && edmType.NamespaceName != modelNamespace | |
? _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) | |
: _code.Escape(edmType)); | |
if (edmType is StructuralType) | |
{ | |
return "T"; | |
} | |
if (edmType is SimpleType) | |
{ | |
var clrType = UnderlyingClrType(edmType); | |
if (!IsEnumType(edmType)) | |
{ | |
typeName = _code.Escape(clrType); | |
} | |
return clrType.IsValueType && isNullable == true | |
? String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) | |
: typeName; | |
} | |
throw new ArgumentException("edmType"); | |
} | |
public Type UnderlyingClrType(EdmType edmType) | |
{ | |
ArgumentNotNull(edmType, "edmType"); | |
var primitiveType = edmType as PrimitiveType; | |
if (primitiveType != null) | |
{ | |
return primitiveType.ClrEquivalentType; | |
} | |
if (IsEnumType(edmType)) | |
{ | |
return GetEnumUnderlyingType(edmType).ClrEquivalentType; | |
} | |
return typeof (object); | |
} | |
public object GetEnumMemberValue(MetadataItem enumMember) | |
{ | |
ArgumentNotNull(enumMember, "enumMember"); | |
var valueProperty = enumMember.GetType().GetProperty("Value"); | |
return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); | |
} | |
public string GetEnumMemberName(MetadataItem enumMember) | |
{ | |
ArgumentNotNull(enumMember, "enumMember"); | |
var nameProperty = enumMember.GetType().GetProperty("Name"); | |
return nameProperty == null ? null : (string) nameProperty.GetValue(enumMember, null); | |
} | |
public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) | |
{ | |
ArgumentNotNull(enumType, "enumType"); | |
var membersProperty = enumType.GetType().GetProperty("Members"); | |
return membersProperty != null | |
? (System.Collections.IEnumerable) membersProperty.GetValue(enumType, null) | |
: Enumerable.Empty<MetadataItem>(); | |
} | |
public bool EnumIsFlags(EdmType enumType) | |
{ | |
ArgumentNotNull(enumType, "enumType"); | |
var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); | |
return isFlagsProperty != null && (bool) isFlagsProperty.GetValue(enumType, null); | |
} | |
public bool IsEnumType(GlobalItem edmType) | |
{ | |
ArgumentNotNull(edmType, "edmType"); | |
return edmType.GetType().Name == "EnumType"; | |
} | |
public PrimitiveType GetEnumUnderlyingType(EdmType enumType) | |
{ | |
ArgumentNotNull(enumType, "enumType"); | |
return (PrimitiveType) enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); | |
} | |
public string CreateLiteral(object value) | |
{ | |
if (value == null || value.GetType() != typeof (TimeSpan)) | |
{ | |
return _code.CreateLiteral(value); | |
} | |
return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan) value).Ticks); | |
} | |
public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile) | |
{ | |
ArgumentNotNull(types, "types"); | |
ArgumentNotNull(sourceFile, "sourceFile"); | |
var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase); | |
if (types.Any(item => !hash.Add(item))) | |
{ | |
_errors.Add( | |
new CompilerError(sourceFile, -1, -1, "6023", | |
String.Format(CultureInfo.CurrentCulture, | |
GetResourceString("Template_CaseInsensitiveTypeConflict")))); | |
return false; | |
} | |
return true; | |
} | |
public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection) | |
{ | |
return GetItemsToGenerate<SimpleType>(itemCollection) | |
.Where(e => IsEnumType(e)); | |
} | |
public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T : EdmType | |
{ | |
return itemCollection | |
.OfType<T>() | |
.Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) | |
.OrderBy(i => i.Name); | |
} | |
public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection) | |
{ | |
return itemCollection | |
.Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) | |
.Select(g => GetGlobalItemName(g)); | |
} | |
public string GetGlobalItemName(GlobalItem item) | |
{ | |
if (item is EdmType) | |
{ | |
return ((EdmType) item).Name; | |
} | |
else | |
{ | |
return ((EntityContainer) item).Name; | |
} | |
} | |
public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type) | |
{ | |
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); | |
} | |
public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type) | |
{ | |
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); | |
} | |
public IEnumerable<EdmProperty> GetComplexProperties(EntityType type) | |
{ | |
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); | |
} | |
public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type) | |
{ | |
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); | |
} | |
public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type) | |
{ | |
return | |
type.Properties.Where( | |
p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); | |
} | |
public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type) | |
{ | |
return | |
type.Properties.Where( | |
p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); | |
} | |
public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type) | |
{ | |
return type.NavigationProperties.Where(np => np.DeclaringType == type); | |
} | |
public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type) | |
{ | |
return | |
type.NavigationProperties.Where( | |
np => | |
np.DeclaringType == type && | |
np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); | |
} | |
public FunctionParameter GetReturnParameter(EdmFunction edmFunction) | |
{ | |
ArgumentNotNull(edmFunction, "edmFunction"); | |
var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); | |
return returnParamsProperty == null | |
? edmFunction.ReturnParameter | |
: ((IEnumerable<FunctionParameter>) returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); | |
} | |
public bool IsComposable(EdmFunction edmFunction) | |
{ | |
ArgumentNotNull(edmFunction, "edmFunction"); | |
var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); | |
return isComposableProperty != null && (bool) isComposableProperty.GetValue(edmFunction, null); | |
} | |
public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction) | |
{ | |
return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); | |
} | |
public TypeUsage GetReturnType(EdmFunction edmFunction) | |
{ | |
var returnParam = GetReturnParameter(edmFunction); | |
return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); | |
} | |
public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) | |
{ | |
var returnType = GetReturnType(edmFunction); | |
return !includeMergeOption && returnType != null && | |
returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; | |
} | |
} | |
public class EdmMetadataLoader | |
{ | |
private readonly IDynamicHost _host; | |
private readonly System.Collections.IList _errors; | |
public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors) | |
{ | |
ArgumentNotNull(host, "host"); | |
ArgumentNotNull(errors, "errors"); | |
_host = host; | |
_errors = errors; | |
} | |
public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath) | |
{ | |
ArgumentNotNull(sourcePath, "sourcePath"); | |
if (!ValidateInputPath(sourcePath)) | |
{ | |
return new EdmItemCollection(); | |
} | |
var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath)); | |
if (schemaElement != null) | |
{ | |
using (var reader = schemaElement.CreateReader()) | |
{ | |
IList<EdmSchemaError> errors; | |
var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] {reader}, | |
out errors); | |
ProcessErrors(errors, sourcePath); | |
return itemCollection; | |
} | |
} | |
return new EdmItemCollection(); | |
} | |
public string GetModelNamespace(string sourcePath) | |
{ | |
ArgumentNotNull(sourcePath, "sourcePath"); | |
if (!ValidateInputPath(sourcePath)) | |
{ | |
return string.Empty; | |
} | |
var model = LoadRootElement(_host.ResolvePath(sourcePath)); | |
if (model == null) | |
{ | |
return string.Empty; | |
} | |
var attribute = model.Attribute("Namespace"); | |
return attribute != null ? attribute.Value : ""; | |
} | |
private bool ValidateInputPath(string sourcePath) | |
{ | |
if (sourcePath == "$" + "edmxInputFile" + "$") | |
{ | |
_errors.Add( | |
new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty, | |
GetResourceString("Template_ReplaceVsItemTemplateToken"))); | |
return false; | |
} | |
return true; | |
} | |
public XElement LoadRootElement(string sourcePath) | |
{ | |
ArgumentNotNull(sourcePath, "sourcePath"); | |
var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); | |
return root.Elements() | |
.Where(e => e.Name.LocalName == "Runtime") | |
.Elements() | |
.Where(e => e.Name.LocalName == "ConceptualModels") | |
.Elements() | |
.Where(e => e.Name.LocalName == "Schema") | |
.FirstOrDefault() | |
?? root; | |
} | |
private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath) | |
{ | |
foreach (var error in errors) | |
{ | |
_errors.Add( | |
new CompilerError( | |
error.SchemaLocation ?? sourceFilePath, | |
error.Line, | |
error.Column, | |
error.ErrorCode.ToString(CultureInfo.InvariantCulture), | |
error.Message) | |
{ | |
IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning | |
}); | |
} | |
} | |
public bool IsLazyLoadingEnabled(EntityContainer container) | |
{ | |
string lazyLoadingAttributeValue; | |
var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled"; | |
bool isLazyLoading; | |
return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, | |
out lazyLoadingAttributeValue) | |
|| !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading) | |
|| isLazyLoading; | |
} | |
} | |
public static void ArgumentNotNull<T>(T arg, string name) where T : class | |
{ | |
if (arg == null) | |
{ | |
throw new ArgumentNullException(name); | |
} | |
} | |
private static readonly Lazy<System.Resources.ResourceManager> ResourceManager = | |
new Lazy<System.Resources.ResourceManager>( | |
() => | |
new System.Resources.ResourceManager("System.Data.Entity.Design", | |
typeof (MetadataItemCollectionFactory).Assembly), isThreadSafe: true); | |
public static string GetResourceString(string resourceName) | |
{ | |
ArgumentNotNull(resourceName, "resourceName"); | |
return ResourceManager.Value.GetString(resourceName, null); | |
} | |
#> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Program | |
{ | |
private static void Main() | |
{ | |
var list = new List<IQueryable<Person>>(); | |
var personQueryable = new List<Person> | |
{ | |
new Person() | |
{ | |
Addresses = new List<Address>() | |
{ | |
new Address() {Line1 = "thing", Person = new Person()} | |
}, | |
Name = "otherthing", | |
Address = new Address() {Line1 = "newthing", Person = new Person(){}} | |
} | |
}.AsQueryable().ToList(); | |
list.Add(personQueryable.AsQueryable()); | |
for (int i = 1; i < 6000; i++) | |
{ | |
list.Add(new List<Person> | |
{ | |
new Person() | |
{ | |
Addresses = new List<Address>() | |
{ | |
new Address() {Line1 = "thing", Person = new Person()} | |
}, | |
Name = "otherthing", | |
Address = new Address() {Line1 = "newthing", Person = new Person() {}} | |
} | |
}.AsQueryable()); | |
list.Last().First().Address.Person = list.Last().First(); | |
} | |
List<List<PersonPoco>> qList = new List<List<PersonPoco>>(); | |
personQueryable.First().Addresses.Add(personQueryable.First().Address); | |
personQueryable.First().Address.Person = personQueryable.First(); | |
var tDict = new Dictionary<int, int>(); | |
var assign = tDict[1] = 1; | |
var thingy = | |
personQueryable.Select(z => (z == null) ? z.Address : null); | |
//new VisualizerDevelopmentHost(thingy.Expression, typeof(ExpressionTreeVisualizer),typeof(ExpressionTreeObjectSource)).ShowVisualizer(); | |
Stopwatch x = Stopwatch.StartNew(); | |
Parallel.ForEach(list, (i) => | |
qList.Add(i.CopyTo<Person, PersonPoco>().ToList()) | |
); | |
var count = qList.Select(z => z.First().Address.Person).Distinct().Count(); | |
x.Stop(); | |
var t1 = x.ElapsedMilliseconds; | |
x = Stopwatch.StartNew(); | |
var people = personQueryable.CopyTo<Person, PersonPoco>().ToList(); | |
x.Stop(); | |
var t2 = x.ElapsedMilliseconds; | |
var pq = personQueryable.ToList(); | |
people = pq.CopyTo<Person, PersonPoco>().ToList(); | |
x.Stop(); | |
var t3 = x.ElapsedMilliseconds; | |
x = Stopwatch.StartNew(); | |
people = pq.CopyTo<Person, PersonPoco>().ToList(); | |
x.Stop(); | |
var tAdd = people.First().Address; | |
var vAdd = people.First().Addresses; | |
var t4 = x.ElapsedMilliseconds; | |
Console.WriteLine(personQueryable.First()); | |
} | |
} | |
public interface IPerson : IEntity | |
{ | |
} | |
public interface IPerson_Name : IPerson, IEntityProperty | |
{ | |
String Name { get; set; } | |
} | |
public interface IPerson_Addresses : IPerson, IEntityCollection | |
{ | |
} | |
public interface IPerson_Addresses<T> : IPerson_Addresses | |
where T : IAddress, new() | |
{ | |
ICollection<T> Addresses { get; set; } | |
} | |
public interface IPerson_Address : IPerson, IEntityObject | |
{ | |
} | |
public interface IPerson_Address<T> : IPerson_Address | |
where T : IAddress, new() | |
{ | |
T Address { get; set; } | |
} | |
public class Person : IPerson_Name, IPerson_Addresses<Address>, IPerson_Address<Address>, IAddress | |
{ | |
public String Name { get; set; } | |
public ICollection<Address> Addresses { get; set; } | |
public Address Address { get; set; } | |
} | |
public class PersonPoco : IPerson_Name, IPerson_Addresses<AddressPoco>, IPerson_Address<AddressPoco> | |
{ | |
public string Name { get; set; } | |
public ICollection<AddressPoco> Addresses { get; set; } | |
public AddressPoco Address { get; set; } | |
} | |
public interface IAddress : IEntity | |
{ | |
} | |
public interface IAddress_Line1 : IAddress, IEntityProperty | |
{ | |
String Line1 { get; set; } | |
} | |
public interface IAddress_Person : IAddress, IEntityObject | |
{ | |
} | |
public interface IAddress_Person<T> : IAddress_Person | |
where T : IPerson, new() | |
{ | |
T Person { get; set; } | |
} | |
public class Address : IAddress_Line1, IAddress_Person<Person> | |
{ | |
public String Line1 { get; set; } | |
public Person Person { get; set; } | |
} | |
public class AddressPoco : IAddress_Line1, IAddress_Person<PersonPoco> | |
{ | |
public String Line1 { get; set; } | |
public PersonPoco Person { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment