Overriding code-generated DbContext constructor

15,292

Solution 1

The best I can suggest is a factory method:

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

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

and use HOLDbEntities.Create() rather than new HOLDbEntities().

Solution 2

I up-voted the previous accepted answer because it is a fairly elegant way of doing it. However another approach would be to modify the T4 template that generates the dbContext Class.

When using EF DB first you have a .edmx file and under that you have an [Entity].Context.tt file. Go into that file and remove (or modify) the following code:

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)#>
<#
    }
}
#>

now your context class will generate without a constructor, so you should be able to go and create one in an extended class.

Solution 3

i changed the context.tt as follows:

<#=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);
    }

so i can declare a OnModelConstructed method in a partial class of the context.

Share:
15,292
Chris Dixon
Author by

Chris Dixon

I'm a Senior Programmer specialising in .NET Core (C#), JavaScript (Vanilla/MVVM) and surrounding technologies. LinkedIn: https://www.linkedin.com/in/chris-dixon-69938829

Updated on June 05, 2022

Comments

  • Chris Dixon
    Chris Dixon about 2 years

    I'm sure I've done this before at some stage, but I can't figure out how to now! My scenario:

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

    Now, I want this connection string to be easily changeable (I want to Implement from the HOLDbEntities), so I need to override this constructor.

    I've tried:

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

    But this throw an error:

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

    I can understand why this errors, but how would I stop the constructor being auto-generated in the first place in order to do what I'm trying to achieve?

  • Chris Dixon
    Chris Dixon over 11 years
    This seems very elegant to what I'm trying to achieve, I'll give it a shot now and see if it works!
  • Chris Dixon
    Chris Dixon over 11 years
    Works fine, but Unity is now complaining that the context cannot be constructed with a String value. Any way around this? Your answer is correct for my scenario anyway.
  • alamin
    alamin over 8 years
    in MSDN it says DbContext constructor overriding with a string Constructs a new context instance using the given string as the name or connection string for the database to which a connection will be made. - can you please you explain it to me clearly or give me an example or reference. i did not find any good result while searching for it :(
  • as9876
    as9876 over 8 years
    Won't the T4 code also get regenerated if you, say, drag another table onto the designer?
  • Dylan Hayes
    Dylan Hayes about 8 years
    @AvrahamSeff nope, the t4s stay the same, and generate .cs files. any changes to the .cs files would get overrwitten in the way that you describe, but this is a change to the file that is doing the generating.
  • Admin
    Admin over 7 years
    @as9876 the T4 code includes generic code that iterates over the entity types, so it doesn't need to be regenerated when new entity types are added
  • Jonathan Wood
    Jonathan Wood over 6 years
    If you're going to edit the T4 template, then why not just modify the constructor there instead of removing it and adding another partial class?