New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow setting the schema for an entire migration class #290
Comments
Thinking of doing this as a convention instead. See this discussion: https://groups.google.com/forum/?fromgroups=#!searchin/fluentmigrator-google-group/schema/fluentmigrator-google-group/RtoPUMWSoVY/9k9Mt2EnicIJ Although, I'll have to create an extension point so that this can be used by the console, MsBuild and Nant runners as well. |
That would be exactly what I need. We created our own .exe that invokes the FluentMigrator test runner for different schemas (using dynamic loading of DLLs with marker interfaces). It’s quite annoying to have to specify the schema for each and every command. From: Daniel Lee [mailto:notifications@github.com] Thinking of doing this as a convention instead. See this discussion: https://groups.google.com/forum/?fromgroups=#!searchin/fluentmigrator-google-group/schema/fluentmigrator-google-group/RtoPUMWSoVY/9k9Mt2EnicIJ Although, I'll have to create an extension point so that this can be used by the console, MsBuild and Nant runners as well. — |
+1 |
I'm working on this and will submit a PR soon (see my branch). Just trying to figure out the best way to override/set properties on the MigrationConventions class. I was thinking I would do it in the same way as for the VersionTableMetaData. But that won't be possible unfortunately as every convention is a func. So maybe setting them in the constructor is the easiest way. Then I just have to figure out how to test this properly. |
Think I'm finished. Just going to test it a bit more and think about any potential bad side effects. For example, setting the default schema before you execute the first migration is an interesting edge case. But that will have to be tomorrow. |
@daniellee from a usability perspective, that would be exactly the best case. Setting the schema once per migration is (for me, at least) a workable interm solution. Looking forward to trying this out. I actually switched from migratordotnet just because my production server used a different schema name, and my migrations broke. |
+1 |
The problem with setting the default schema before you execute the first migration is that you end up in a chicken and egg situation. The first migration to be run will create the VersionInfo table (the table that keeps track of which migrations have been applied) and it will try and create it under the default schema. Which doesn't exist yet. And will therefore crash. I'm thinking that the VersionInfo table is a special case and should not have the default schema convention applied to it. Instead if the user wants to change the schema for the VersionInfo table then they should create custom metadata for it. You can also potentially mess things up by changing the Foreign Key naming conventions or Index naming conventions but there's not much I can do about that. |
That's how we do it now. And I would assume that all migrations within the same assembly would use that same schema (unless overridden in the specific migration). |
@daniellee I had exactly that problem on my production hosting. The hosting company already had a Maybe I'm oversimplifying, but to look at T-SQL as an example, isn't applying the default schema as simple as a Also, it seems that specifying the schema in the first migration also specifies it for the |
Got a bit stuck this with change. There are already some pull requests that partially solve the problem of exposing the MigrationConventions. #287 does it by creating a few factories that are extensible. #386 has its own MigrationConventionsBase that wraps the MigrationConventions class. If I take this approach then I would inherit from the class and override the appropriate method much like you need to do for the VersionInfo table. The other possible approach is what I have right now on my branch. It keeps the existing structure of the MigrationConventions class so if I want to change some behavior in it then I have to inherit the class and set a property in the constructor. I'm not too keen on the factory approach so I'll show examples of the other two approaches. These assume that you want to keep most of the default conventions and just change the DefaultSchema so both examples inherit from MigrationConventions. First, via the constructor and keeping delegates:
Second, removing delegates and changing properties to be methods and making them overridable:
This is also not as black and white is it might seem. The second approach seems simpler for the end-user but does complicate some parts of the code as you can't switch out conventions at runtime. I've been looking at Nancy and at how they do this and they use a lot of delegates and actions but also sometimes just simple overrides of virtual methods. Their Bootstrapper class approach is maybe something we should be copying if we want to break FluentMigrator up into smaller parts. So what do you all think? I have two branches and both approaches work but once this is released then it will be hard to change without breaking stuff. P.S. I am thinking about adding ApplicationContext as a readonly property to the MigrationConventions class as that adds some more flexibility that you get with the factory approach in #287. Anything else that would be handy to have as a readonly property? |
I know you only want one of the two approaches, but I have a third to offer up. First, I solved this problem by creating a constant called What about just exposing a simple Would that be simpler? It seems like there isn't a schema attribute currently in the migration class. |
@daniellee any update on the pull you where working on ? |
@daniellee Recently i was thinking about the ability to change conventions
What do you think ? |
As I see there's still no solution, so for those who's looking for a workaround on this issue: First create a base class for your migrations and replace Create, Insert and Delete properties so that they return a proxy with our interceptor: public abstract class DatabaseMigrationBase: Migration
{
public static readonly string DefaultSchema;
private IServiceContainer _container;
static DatabaseMigrationBase()
{
DefaultSchema = "your_schema"; // load from config
}
protected DatabaseMigrationBase()
{
_container = new ServiceContainer(new ContainerOptions { EnableVariance = false });
_container.Register<ICreateExpressionRoot>(_ => BaseCreate(), new PerContainerLifetime());
_container.Register<IInsertExpressionRoot>(_ => BaseInsert(), new PerContainerLifetime());
_container.Register<IDeleteExpressionRoot>(_ => BaseDelete(), new PerContainerLifetime());
_container.Intercept(
sr => sr.ServiceType == typeof(ICreateExpressionRoot),
sf => new SetDefaultSchemaInterceptor(DefaultSchema));
_container.Intercept(
sr => sr.ServiceType == typeof(IInsertExpressionRoot),
sf => new SetDefaultSchemaInterceptor(DefaultSchema));
_container.Intercept(
sr => sr.ServiceType == typeof(IDeleteExpressionRoot),
sf => new SetDefaultSchemaInterceptor(DefaultSchema));
}
/// <summary>
/// Replace base methods so that proxy with our interceptor is returned.
/// </summary>
public new ICreateExpressionRoot Create
{
get { return _container.GetInstance<ICreateExpressionRoot>(); }
}
public new IInsertExpressionRoot Insert
{
get { return _container.GetInstance<IInsertExpressionRoot>(); }
}
public new IDeleteExpressionRoot Delete
{
get { return _container.GetInstance<IDeleteExpressionRoot>(); }
}
private ICreateExpressionRoot BaseCreate()
{
return base.Create;
}
private IInsertExpressionRoot BaseInsert()
{
return base.Insert;
}
private IDeleteExpressionRoot BaseDelete()
{
return base.Delete;
}
} Now implement interceptor which checks every call of public class SetDefaultSchemaInterceptor: IInterceptor
{
private string _defaultSchema;
public SetDefaultSchemaInterceptor(string defaultSchema)
{
_defaultSchema = defaultSchema;
}
public object Invoke(IInvocationInfo invocationInfo)
{
// execute any method of ICreateExpressionRoot and get it's result
var result = invocationInfo.Proceed();
// check if return type is not void
if (null == result || string.IsNullOrEmpty(_defaultSchema))
return result;
// get all methods named InSchema
var inSchemaMethods = result.GetType()
.GetMethods()
// add interface members implemented by type explicitly
.Union(GetExplicitMethods(result.GetType()))
.Where(m => m.Name == "WithSchema" || m.Name == "InSchema" || m.Name.EndsWith(".InSchema"));
foreach (var method in inSchemaMethods)
{
// check signature - one string parameter
var parameters = method.GetParameters();
if (parameters.Count() == 1 && parameters.First().ParameterType == typeof (string))
{
// if ok - set schema
method.Invoke(result, new object[] { _defaultSchema });
}
}
return result;
}
private MethodInfo[] GetExplicitMethods(Type type)
{
return type
.GetInterfaces()
.SelectMany(t => type.GetInterfaceMap(t).InterfaceMethods)
.ToArray();
}
} Now you may derive your migrations from DatabaseMigrationBase and use methods Create/Insert/Delete without specifying schema. |
Why does migrator is ignoring the |
Fixed by #772 |
Currently, you have to repeatingly call InSchema() in every statement. It would be nice to be able to associate the migration class with a particular schema. E.g. by overriding the Schema property from the MigrationBase.
The text was updated successfully, but these errors were encountered: