2013-02-26 3 views
11

Sono sicuro di averlo già fatto in alcune fasi, ma non riesco a capire come farlo ora! Il mio scenario:Sovrascrittura del costruttore DbContext generato dal codice

// This is generated from EDMX 
public partial class HOLDbEntities : DbContext 
{ 
    public HOLDbEntities() 
      : base("name=HOLDbEntities") 
     { 
     } 
} 

Ora, voglio che questa stringa di connessione per essere facilmente modificabili (Voglio realizzare dai HOLDbEntities), quindi ho bisogno di ignorare questo costruttore.

ho provato:

public partial class HOLDbEntities 
{ 
    private const string _contextName = "HOLDbEntities"; 
    public static string ContextName { get { return _contextName; } } 

    public HOLDbEntities() 
     : base(ContextName) 
    { 
    } 
} 

Ma questo genera un errore:

HOLDbEntities already defines a member called "HOLDbEntities" with the same parameter types.

Posso capire perché questo gli errori, ma come mi sarei fermato il costruttore di essere auto-generata nel primo posto per fare ciò che sto cercando di ottenere?

+1

Le classi parziali sono parti della stessa classe, quindi non è possibile avere due o più metodi (incluso il costruttore) con la stessa firma. Nell'esempio fornito è possibile rimuovere il costruttore nella classe parziale generata (_note: _ se si rigenera l'entità, le modifiche verranno perse) o il costruttore di overload (ad esempio 'public HOLDbEntities (string contextName) : base (contextName)'). – Leri

+1

Vorrei semplicemente aggiungere un altro costruttore con il parametro di cui hai bisogno in modo che esistano entrambi e nel tuo codice chiamante decidi quale utilizzare –

risposta

8

Il meglio che posso suggerire è un metodo di fabbrica:

private HOLDbEntities(string contextName) : base(contextName) { } 

public static HOLDbEntities Create() { 
    return new HOLDbEntities(ContextName); 
} 

e utilizzare HOLDbEntities.Create() piuttosto che new HOLDbEntities().

+0

Questo sembra molto elegante per quello che sto cercando di ottenere, gli darò uno scatto ora e vedi se funziona! –

+0

Funziona bene, ma Unity ora si lamenta che il contesto non può essere costruito con un valore String. In qualche modo intorno a questo? La tua risposta è corretta per il mio scenario comunque. –

+0

in MSDN dice che il costruttore DbContext sovrascrive con una stringa Costruisce una nuova istanza di contesto utilizzando la stringa data come nome o stringa di connessione per il database a cui verrà effettuata una connessione.- puoi per favore spiegarmelo chiaramente o darmi un esempio o un riferimento. ( – alamin

18

Ho votato la risposta accettata in precedenza perché è un modo abbastanza elegante di farlo. Tuttavia un altro approccio potrebbe essere quello di modificare il modello T4 che genera la classe dbContext.

Quando si utilizza EF DB, si dispone di un file .edmx e sotto si ha un file [Entity] .Context.tt. Andate in quel file e rimuovere (o modificare) il seguente codice:

public <#=code.Escape(container)#>() 
     : base("name=<#=container.Name#>") 
    { 
<# 
if (!loader.IsLazyLoadingEnabled(container)) 
{ 
#> 
     this.Configuration.LazyLoadingEnabled = false; 
<# 
} 

foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) 
{ 
    // Note: the DbSet members are defined below such that the getter and 
    // setter always have the same accessibility as the DbSet definition 
    if (Accessibility.ForReadOnlyProperty(entitySet) != "public") 
    { 
#> 
     <#=codeStringGenerator.DbSetInitializer(entitySet)#> 
<# 
    } 
} 
#> 

adesso la tua classe del contesto genererà senza un costruttore, così si dovrebbe essere in grado di andare a crearne uno in una classe estesa.

+0

Il codice T4 non viene rigenerato se, per esempio, trascini un altro tavolo sul designer? – as9876

+0

@AvrahamSeff, i t4 rimangono lo stesso, e genera file .cs. qualsiasi modifica ai file .cs viene sovrascritta nel modo in cui descrivi, ma questa è una modifica al file che sta generando. –

+0

@ as9876 il codice T4 include codice generico che itera sui tipi di entità, quindi non ha bisogno di essere rigenerato quando vengono aggiunti nuovi tipi di entità –

0

Ecco la mia soluzione al problema. Modifica il file TT come suggerito da Dylan Hayes e ho sostituito il costruttore con il mio. Nel mio caso, avevo bisogno di cambiare solo i nomi degli schemi di alcuni schemi. Ho impostato una variabile nel file di configurazione per dirmi in che ambiente ero e ho usato lo schema giusto.

using System.Configuration; 
using System.Data.Entity; 
using System.Data.Entity.Core.Mapping; 
using System.Data.Entity.Core.Metadata.Edm; 
using System.Data.Entity.Core.Objects; 
using System.Data.Entity.Infrastructure; 
using System.Reflection; 
using System.Xml; 

namespace WS.Framework.WSJDEData 
{ 

    public partial class WSJDE : DbContext 
    { 
     public WSJDE() 
      : base("name=WSJDE") 
     { 
      ObjectContext context = (this as IObjectContextAdapter).ObjectContext; 

      string environment = ConfigurationManager.AppSettings.Get("Environment"); 

      const string devCTL = "TESTCTL"; 
      const string devDTA = "TESTDTA"; 
      const string qaCTL = "CRPCTL"; 
      const string qaDTA = "CRPDTA"; 
      const string prodCTL = "PRODCTL"; 
      const string prodDTA = "PRODDTA"; 

      var x = Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.ssdl"); 

      XmlReader[] sReaders = new XmlReader[] 
       { 
        XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.ssdl")) 
       }; 

      XmlReader[] mReaders = new XmlReader[] 
       {XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("WSJDEData.WSJDE.msl"))}; 

      StoreItemCollection sCollection = new StoreItemCollection(sReaders); 

      ObjectContext objContext = ((IObjectContextAdapter) context).ObjectContext; 
      MetadataWorkspace workspace = objContext.MetadataWorkspace; 

      EdmItemCollection cCollection = workspace.GetItemCollection(DataSpace.CSpace) as EdmItemCollection; 


      StorageMappingItemCollection csCollection = new StorageMappingItemCollection(cCollection, sCollection, 
                         mReaders); 

      workspace.RegisterItemCollection(sCollection); 
      workspace.RegisterItemCollection(csCollection); 

      EntityContainer container = workspace.GetItem<EntityContainer>("WSJDEModelStoreContainer", DataSpace.SSpace); 

      foreach (EntitySetBase entitySetBase in container.BaseEntitySets) 
      { 
       string schema = entitySetBase.Schema; 

       if (schema != null) 
       { 
        string name = schema.Substring(schema.Length - 3); 

        if (name == "CTL") 
        { 
         switch (environment) 
         { 
          case "Dev": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, devCTL); 
           break; 
          case "QA": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, qaCTL); 
           break; 
          case "Prod": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, prodCTL); 
           break; 

         } 
        } 

        if (name == "DTA") 
        { 
         switch (environment) 
         { 
          case "Dev": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, devDTA); 
           break; 
          case "QA": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, qaDTA); 
           break; 
          case "Prod": 
           typeof (EntitySetBase).GetField("_schema", 
                   BindingFlags.NonPublic | BindingFlags.Instance) 
                 .SetValue(entitySetBase, prodDTA); 
           break; 

         } 
        } 
       } 
      } 
     } 
    } 
} 
3

cambiai context.tt come segue:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext 
{ 
    public <#=code.Escape(container)#>() 
     : base("name=<#=container.Name#>") 
    { 

<# 
if (!loader.IsLazyLoadingEnabled(container)) 
{ 
#> 
     this.Configuration.LazyLoadingEnabled = false; 
<# 
} 
foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) 
{ 
    // Note: the DbSet members are defined below such that the getter and 
    // setter always have the same accessibility as the DbSet definition 
    if (Accessibility.ForReadOnlyProperty(entitySet) != "public") 
    { 
#> 
     <#=codeStringGenerator.DbSetInitializer(entitySet)#> 
<# 
    } 
} 
#> 
var Method = (typeof(Entities)).GetMethods(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FirstOrDefault(x => x.Name == "OnModelConstructed"); 
if (Method!=null) Method.Invoke(this,null); 
    } 

così posso dichiarare una OnModelConstructed metodo in una classe parziale del contesto.