c# - Entity Framework 6.1.3: Navigation Properties - Foreign Key Relationships -


i've been able ienumerable of valuetuples keys of given entity type (column name + propertyinfo), have found myself in need of method identifies 1:many , many:many foreign keys given entity type.

i use cross-reference mappings give me column name + property info set of valuetuples.

the main focus behind expand experience in ef , better understand how code first works.

below i'm using far inspect model key names, , clear context @ moment:

public static partial class entityextensions {      private static readonly object _tablenamessyncobject = new object();     private static readonly object _entitytypecachesyncobject = new object();     private static readonly object _entitykeypropertyassociationssyncobject = new object();      private static readonly dictionary<(type dbcontexttype, type entitytype), string> _tablenames = new dictionary<(type, type), string>();     private static readonly dictionary<type, list<(type entitytype, string schemaname, propertyinfo setexposure)>> _entitytypecache = new dictionary<type, list<(type entitytype, string schemaname, propertyinfo setexposure)>>();      private static readonly dictionary<(type, type), list<(string propertyname, string dbname)>> _entitykeypropertyassociations = new dictionary<(type, type), list<(string propertyname, string dbname)>>();      public static ienumerable<object> cleardbcontext<tdbcontext>(this tdbcontext dbcontext)         tdbcontext :             dbcontext,             iobjectcontextadapter     {         list<object> result = new list<object>();         var entitysets = dbcontext.getdbsettypeschemanameandexposuretriplets();         foreach (var entityset in entitysets)         {             var defctor = entityset.entitytype.getconstructor(type.emptytypes);              if (defctor == null)             {                 /* if there's no public constructor, how in model? */                 dofulldelete(dbcontext, entityset, result);                 continue;             }             var keysetnames = dbcontext.getdbsetkeynames(entityset.entitytype);             var keysetpropertiesandcolumns =                 keysetnames.select(x => (property: entityset.entitytype.getproperty(x.propertyname), columnname: x.dbname))                 .toarray();              var tupletype = (keysetpropertiesandcolumns.length <= 0 || keysetpropertiesandcolumns.length > 7)                   ? null                   : getmutabletupletype(keysetpropertiesandcolumns.length);              if (keysetpropertiesandcolumns.any(x => x.property == null) || tupletype == null)             {                 /* went wrong in our inspection of entity. */                 dofulldelete(dbcontext, entityset, result);                 continue;             }              var selectportion =                 string.join(                     ", ",                     keysetpropertiesandcolumns                     .select((x, i) =>                         $"{entityset.schemaname}.[{x.columnname}] [item{(i + 1)}]"));              var sqlquery = $"select {selectportion} {entityset.schemaname}";              var properties = keysetpropertiesandcolumns.select(y => y.property).toarray();             var datareader =                 dbcontext.database.sqlquery(                     tupletype.makegenerictype(                         keysetpropertiesandcolumns                         .select(x => x.property.propertytype)                         .toarray()),                     sqlquery);              var dbset = entityset.getproxyfor(dbcontext);// dbcontext.set(entityset.entitytype);              var entityelements =                 datareader.oftype<imutabletuplepassthrough>()                 .select(x =>                 {                     var localequivalent = dbset.local.oftype<object>().singleordefault(y => x.areidentitiesequivalent(properties, y));                     if (localequivalent != null)                         return localequivalent;                     var resultelement = activator.createinstance(entityset.entitytype);                     x.passpropertiesthroughto(properties, resultelement);                     dbset.attach(resultelement);                     return resultelement;                 })                 .toarray();             foreach (var entity in entityelements)                 //dbcontext.entry(entity).state = entitystate.deleted;                 result.add(entity);         }         return result;     }      private static idbsetproxy getproxyfor(this (type entitytype, string schemaname, propertyinfo setexposure) target, dbcontext dbcontext)     {         if (target.setexposure == null)             return dbsetproxy.createproxyfrom(dbcontext.set(target.entitytype));         var dbset = target.setexposure.getvalue(dbcontext);         if (dbset == null)             return dbsetproxy.createproxyfrom(dbcontext.set(target.entitytype));         return dbsetproxy.createproxyfrom(dbset);     }      private static void dofulldelete<tdbcontext>(tdbcontext dbcontext, (type entitytype, string schemaname, propertyinfo setexposure) entityset, list<object> resultset)         tdbcontext :             dbcontext,             iobjectcontextadapter     {         if (entityset.setexposure != null)         {             var proxy = entityset.getproxyfor(dbcontext);             if (proxy != null)             {                 using (proxy)                 {                     var setarray = (ienumerable)proxy;                     var fullset = setarray.oftype<object>();                     resultset.addrange(fullset);                     proxy.removerange(fullset);                 }                 return;             }         }         {              var set = dbcontext.set(entityset.entitytype);             var setarray = (ienumerable)set;             var fullset = setarray.oftype<object>();             resultset.addrange(fullset);             set.removerange(fullset);         }     }      /// <summary>returns generic <see cref="type"/> version of mutable tuple has <paramref name="itemcount"/> elements it.</summary>     /// <param name="itemcount">the number of elements mutable tuple.</param>     /// <returns>the generic <see cref="type"/> version of mutable tuple has <paramref name="itemcount"/> elements it.</returns>     public static type getmutabletupletype(int itemcount)     {         switch (itemcount)         {             case 1:                 return typeof(mutabletuple<>);             case 2:                 return typeof(mutabletuple<,>);             case 3:                 return typeof(mutabletuple<,,>);             case 4:                 return typeof(mutabletuple<,,,>);             case 5:                 return typeof(mutabletuple<,,,,>);             case 6:                 return typeof(mutabletuple<,,,,,>);             case 7:                 return typeof(mutabletuple<,,,,,,>);             default:                 throw new argumentoutofrangeexception(nameof(itemcount));         }     }      /* https://stackoverflow.com/questions/6106842/entity-framework-get-table-name-from-the-entity */     public static string gettablename<tdbcontext>(this tdbcontext dbcontext, type entitytype)         tdbcontext :             dbcontext,             iobjectcontextadapter     {         var dbcontexttype = typeof(tdbcontext);         var key = (dbcontexttype, entitytype);         lock (_tablenamessyncobject)         {             if (!_tablenames.trygetvalue(key, out var result))             {                 readonlycollection<entitycontainermapping> storagemetadata = dbcontext.objectcontext.metadataworkspace.getitems<entitycontainermapping>(dataspace.csspace);                 result = gettablenameinlockimpl(storagemetadata, entitytype, dbcontexttype);             }             return result;         }     }      private static string gettablenamewithstoragemetadata<tdbcontext>(readonlycollection<entitycontainermapping> storagemetadata, type entitytype)     {         var dbcontexttype = typeof(tdbcontext);         var key = (dbcontexttype, entitytype);         lock (_tablenamessyncobject)         {             if (!_tablenames.trygetvalue(key, out var result))                 result = gettablenameinlockimpl(storagemetadata, entitytype, dbcontexttype);             return result;         }     }      private static string gettablenameinlockimpl(readonlycollection<entitycontainermapping> storagemetadata, type entitytype, type dbcontexttype)     {         string result = null;         string entityname = entitytype.name;          foreach (entitycontainermapping ecm in storagemetadata)         {              if (ecm.storeentitycontainer.trygetentitysetbyname(entityname, false, out var entityset))             {                 result = $"[{entityset.schema}].[{entityset.table}]";                 break;             }         }          _tablenames.add((dbcontexttype, entitytype), result);         return result;     }      public static ienumerable<(string propertyname, string dbname)> getdbsetkeynames<tdbcontext>(this tdbcontext dbcontext, type t)         tdbcontext :             dbcontext,             iobjectcontextadapter     {         var tdbcontext = typeof(tdbcontext);         var key = (tdbcontext, t);         lock (_entitykeypropertyassociationssyncobject)         {             if (!_entitykeypropertyassociations.trygetvalue(key, out var result))             {                 _entitykeypropertyassociations.add(key, result = new list<(string propertyname, string dbname)>());                 var storagemetadata = dbcontext.objectcontext.metadataworkspace.getitems<entitycontainermapping>(dataspace.csspace);                 foreach (var ecm in storagemetadata)                 {                     var tablename = t.name;                     var entitysetbyname =                         ecm.entitysetmappings.where(x => x.entityset.elementtype.name == t.name).singleordefault();                     if (entitysetbyname != null)                     {                         foreach (var prop in entitysetbyname.entityset.elementtype.keyproperties)                         {                             var firstpropmapping =                                 (from typemapping in entitysetbyname.entitytypemappings                                  fragment in typemapping.fragments                                  propmap in fragment.propertymappings.oftype<scalarpropertymapping>()                                  propmap.property == prop                                  select propmap).singleordefault();                             if (firstpropmapping != null)                                 result.add((firstpropmapping.property.name, firstpropmapping.column.name));                             /*use firstpropmapping in result*/                         }                     }                 }             }             return result.toarray();         }     }      public static ienumerable<(type entitytype, string schemaname, propertyinfo setexposure)> getdbsettypeschemanameandexposuretriplets<tdbcontext>(this tdbcontext dbcontext)         tdbcontext :             dbcontext,             iobjectcontextadapter     {         var tdbcontext = typeof(tdbcontext);         lock (_entitytypecachesyncobject)         {             if (!_entitytypecache.trygetvalue(tdbcontext, out var resultset))             {                 _entitytypecache.add(tdbcontext, resultset = new list<(type entitytype, string schemaname, propertyinfo setexposure)>());                 var assembliestosearch = new hashset<assembly> { tdbcontext.assembly };                 /* setup few hashes avoid evaluating twice. */                 var types = new hashset<type>();                 var possibletypepropertieslookup = new dictionary<type, propertyinfo>();                 var dbsettype = typeof(dbset<>);                 var currenttype = tdbcontext;                 /* scan context hierarchy public properties exposing dbsets, give 'isexposedasdbset' portion of question. */                 while (currenttype != null)                 {                     var properties = currenttype.getproperties(bindingflags.public | bindingflags.instance);                     foreach (var prop in properties)                         if (prop.propertytype.isgenerictype && prop.propertytype.isconstructedgenerictype)                         {                             var proptypegenerictypedef = prop.propertytype.getgenerictypedefinition();                             if (proptypegenerictypedef == dbsettype)                             {                                 var enttype = prop.propertytype.getgenericarguments()[0];                                 if (!possibletypepropertieslookup.containskey(enttype))                                     possibletypepropertieslookup.add(enttype, prop);                             }                         }                     currenttype = currenttype.basetype;                 }                 foreach (var type in possibletypepropertieslookup.keys)                     assembliestosearch.add(type.assembly);                 readonlycollection<entitycontainermapping> storagemetadata = dbcontext.objectcontext.metadataworkspace.getitems<entitycontainermapping>(dataspace.csspace);                 foreach (entitycontainermapping ecm in storagemetadata)                 {                     foreach (var entitysetmapping in ecm.entitysetmappings)                     {                         var firstrelevanttype =                             assembliestosearch.select(x => x.gettype(entitysetmapping.entityset.elementtype.fullname, false)).where(x => x != null).toarray();                         if (firstrelevanttype.length > 1)                             throw new indexoutofrangeexception("cannot determine distinct entity set type origin.");                         else if (firstrelevanttype.length == 1)                         {                             var firsttype = firstrelevanttype[0];                             if (types.add(firsttype))                             {                                 if (possibletypepropertieslookup.containskey(firsttype))                                     resultset.add((firsttype, gettablenamewithstoragemetadata<tdbcontext>(storagemetadata, firsttype), possibletypepropertieslookup[firsttype]));                                 else                                     resultset.add((firsttype, gettablenamewithstoragemetadata<tdbcontext>(storagemetadata, firsttype), null));                             }                         }                     }                 }             }             return resultset.toarray(); /* toarray detach set cache */         }     } }  /// <summary>provides proxy operate against <see cref="dbset"/> or <see cref="dbset{tentity}"/></summary> public interface idbsetproxy :     idisposable,     ienumerable {     object attach(object entity);     ienumerable removerange(ienumerable set);     object remove(object entity);     object create();     object create(type derivedentitytype);     object find(params object[] keyvalues);     ilist local { get; } }  public class dbsetproxy :     idbsetproxy {     private dbset _original;     public dbsetproxy(dbset original) =>         this._original = original;      public object attach(object entity) =>         this._original?.attach(entity);      public object create() =>         this._original.create();      public object create(type derivedentitytype) =>         this._original.create(derivedentitytype);      public object find(params object[] keyvalues) =>         this._original.find(keyvalues);      public ienumerator getenumerator() =>         ((ienumerable)this._original).getenumerator();      public ilist local =>         this._original.local;      public object remove(object entity) =>         this._original.remove(entity);      public ienumerable removerange(ienumerable set) =>         this._original.removerange(set);      public static idbsetproxy createproxyfrom(object propertyset)     {          if ((propertyset ?? throw new argumentnullexception(nameof(propertyset))) dbset set)             return new dbsetproxy(set);         else         {             var type = propertyset.gettype();             if (type.isconstructedgenerictype && type.getgenerictypedefinition() == typeof(dbset<>))             {                 var entitytype = type.getgenericarguments()[0];                 var constructedproxy = typeof(dbsetproxy<>).makegenerictype(entitytype);                 var constructor = constructedproxy.getconstructor(bindingflags.public | bindingflags.instance, type.defaultbinder, new[] { typeof(dbset<>).makegenerictype(entitytype) }, null);                 if (constructor == null)                     throw new missingmemberexception($"{nameof(dbsetproxy)}<{entitytype.name}>", $".ctor({entitytype.name})");                 return (idbsetproxy)constructor.invoke(new[] { propertyset });             }             else                 throw new argumentoutofrangeexception(nameof(propertyset));         }     }      public void dispose()     {         this._original = null;     } } public class dbsetproxy<tentity> :     idbsetproxy     tentity :         class {     private dbset<tentity> _original;     private dbset __originalimplicitcast; /* used create only, due local cache. */      public dbsetproxy(dbset<tentity> original) =>         this._original = original;      public object attach(object entity) =>         (entity tentity e)         ? this._original.attach(e)         : null;      public object create() =>         _original?.create();      public object create(type derivedentitytype) =>         (this.__originalimplicitcast           ?? (__originalimplicitcast = _original))         .create(derivedentitytype);      public void dispose()     {         this._original = null;         this.__originalimplicitcast = null;     }      public object find(params object[] keyvalues) =>         this._original.find(keyvalues);      public ienumerator getenumerator() =>         ((ienumerable)this._original).getenumerator();      public ilist local =>         this._original.local;      public object remove(object entity) =>         (entity tentity e)         ? this._original.remove(e)         : null;      public ienumerable removerange(ienumerable set) =>         this._original.removerange(set.oftype<tentity>()); }  public interface imutabletuplepassthrough {     void passpropertiesthroughto(propertyinfo[] properties, object target);     bool areidentitiesequivalent(propertyinfo[] properties, object target); } 

the method in sample code yields list of objects test assumption or 2 how ef understand entities related 1 (see below).

the dbsetproxy used ensure local copies of types considered, since user of context using these navigation properties.

from writing code hand, know 1 have navigation properties need (in pseudo code, using 'entry' dbentityentry current entity):

foreach (var navigationproperty in navigationproperties) {     entry.originalvalues[navigationproperty.name] = navigationproperty.propinfo.getvalue(entity, null); } 

i need know many:many sets since need cleared. optional associations further need cleared. these 2 steps need performed before actual logic in current cleardbcontext method.

if has resource on how grok better/quicker, i'm game, i've done far has been derived code on getting table name ef: entity framework - table name entity

this goes few steps beyond , seems learning curve appears non-linear.


Comments

Popular posts from this blog

node.js - Node js - Trying to send POST request, but it is not loading javascript content -

javascript - Replicate keyboard event with html button -

javascript - Web audio api 5.1 surround example not working in firefox -