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

Fluent Mapping confusion #58

Closed
CaspianCanuck opened this Issue Dec 2, 2013 · 3 comments

Comments

Projects
None yet
2 participants
@CaspianCanuck
Contributor

CaspianCanuck commented Dec 2, 2013

I am trying to figure out how to do fluent mapping and have a couple of questions:

  1. Your example on using a factory pattern to create a fluently mapped DB goes like this:
DbFactory = DatabaseFactory.Config(x =>
{
    x.UsingDatabase(() => new Database("connString");
    x.WithFluentConfig(fluentConfig);
    x.WithMapper(new Mapper());
});

Except that there's no Mapper class. So what exactly should I do here?

  1. I have a few large POCO's with properties that mostly correspond 1-to-1 to DB columns except for a few that don't (i.e. different names or DB types). So I need to map most of them using convention-based mapping but override some properties' mappings. If I follow your examples and do this:
public class MyPocoMap : Map<MyPoco>
{
    public MyPocoMap()
    {
        Columns(x =>
        {
            x.Column(col => col.SomeProperty).WithName("SomeFieldName");
        });
    }
}

then would the rest of the POCO's properties be auto-mapped, or would I then have to explicitly map every single one of them?

Thanks!

@schotime

This comment has been minimized.

Show comment
Hide comment
@schotime

schotime Dec 2, 2013

Owner
  1. Here is my fluent mappings
    var conventions = FluentMappingConfiguration.Scan(scanner =>
    {
        scanner.Assembly(typeof(User).Assembly);
        scanner.IncludeTypes(x=>x.Namespace.StartsWith(typeof(User).Namespace));
        scanner.Columns.IgnoreWhere(y =>
        {
            // Don't ignore HStore
            if (y.GetMemberInfoType() == typeof(HStore))
                return false;

            if (!y.GetMemberInfoType().IsValueType && !(y.GetMemberInfoType() == typeof (string)))
                return !(y.GetMemberInfoType() == typeof (byte[]));

            if (!y.IsField() && ((PropertyInfo) y).GetSetMethodOnDeclaringType() == null)
                return false;

            return false;
        });
        scanner.TablesNamed(x => Inflector.MakePlural(x.Name).BreakUpCamelCaseUsing("_").ToLower());
        scanner.Columns.Named(x => x.Name.BreakUpCamelCaseUsing("_").ToLower());
        scanner.PrimaryKeysNamed(x => x.Name.BreakUpCamelCaseUsing("_").ToLower() + "_id");
        scanner.OverrideMappingsWith(new OverrideMappings());
    });

    DatabaseFactory = DatabaseFactory.Config(x => {
        x.WithMapper(new Mapper());
        x.WithFluentConfig(conventions);
        x.UsingDatabase(() => new Database("conn"));
    });

Here are my overrides

    public class OverrideMappings : Mappings
    {
        public OverrideMappings()
        {
            For<UserClient>().CompositePrimaryKey(x => x.UserId, x => x.ClientId);
        }
    }

So, for all classes, it will make them auto incrementing, however for the UserClient, it will override it to be a composite primary key.

The mapper is just a way to convert sql values to c# values. So I might want to convert int columns with a property type of bool from the int into the bool. You can omit this line or if you need a mapper this is a sample.

public class Mapper : DefaultMapper
{
    public override Func<object, object> GetFromDbConverter(Type DestType, Type SourceType)
    {
        if (DestType == typeof(HStore) && SourceType == typeof(string))
        {
            return x => HStore.Create((string)x);
        }

        return base.GetFromDbConverter(DestType, SourceType);
    }

    public override Func<object, object> GetToDbConverter(Type DestType, Type SourceType)
    {
        if (SourceType == typeof(HStore))
        {
            return x => ((HStore)x).ToSqlString();
        }
        return base.GetToDbConverter(DestType, SourceType);
    }
}

This converts the property of type string from the database into a HStore class. Both into and from the database.

If you need more info let me know.

Owner

schotime commented Dec 2, 2013

  1. Here is my fluent mappings
    var conventions = FluentMappingConfiguration.Scan(scanner =>
    {
        scanner.Assembly(typeof(User).Assembly);
        scanner.IncludeTypes(x=>x.Namespace.StartsWith(typeof(User).Namespace));
        scanner.Columns.IgnoreWhere(y =>
        {
            // Don't ignore HStore
            if (y.GetMemberInfoType() == typeof(HStore))
                return false;

            if (!y.GetMemberInfoType().IsValueType && !(y.GetMemberInfoType() == typeof (string)))
                return !(y.GetMemberInfoType() == typeof (byte[]));

            if (!y.IsField() && ((PropertyInfo) y).GetSetMethodOnDeclaringType() == null)
                return false;

            return false;
        });
        scanner.TablesNamed(x => Inflector.MakePlural(x.Name).BreakUpCamelCaseUsing("_").ToLower());
        scanner.Columns.Named(x => x.Name.BreakUpCamelCaseUsing("_").ToLower());
        scanner.PrimaryKeysNamed(x => x.Name.BreakUpCamelCaseUsing("_").ToLower() + "_id");
        scanner.OverrideMappingsWith(new OverrideMappings());
    });

    DatabaseFactory = DatabaseFactory.Config(x => {
        x.WithMapper(new Mapper());
        x.WithFluentConfig(conventions);
        x.UsingDatabase(() => new Database("conn"));
    });

Here are my overrides

    public class OverrideMappings : Mappings
    {
        public OverrideMappings()
        {
            For<UserClient>().CompositePrimaryKey(x => x.UserId, x => x.ClientId);
        }
    }

So, for all classes, it will make them auto incrementing, however for the UserClient, it will override it to be a composite primary key.

The mapper is just a way to convert sql values to c# values. So I might want to convert int columns with a property type of bool from the int into the bool. You can omit this line or if you need a mapper this is a sample.

public class Mapper : DefaultMapper
{
    public override Func<object, object> GetFromDbConverter(Type DestType, Type SourceType)
    {
        if (DestType == typeof(HStore) && SourceType == typeof(string))
        {
            return x => HStore.Create((string)x);
        }

        return base.GetFromDbConverter(DestType, SourceType);
    }

    public override Func<object, object> GetToDbConverter(Type DestType, Type SourceType)
    {
        if (SourceType == typeof(HStore))
        {
            return x => ((HStore)x).ToSqlString();
        }
        return base.GetToDbConverter(DestType, SourceType);
    }
}

This converts the property of type string from the database into a HStore class. Both into and from the database.

If you need more info let me know.

@CaspianCanuck

This comment has been minimized.

Show comment
Hide comment
@CaspianCanuck

CaspianCanuck Dec 3, 2013

Contributor

OK I think the fog in my head is slowly beginning to lift. :)

Let me get thing straight: In your Mapper example if DestType is not HStore or SourceType is not String then the conversion methods will return null from their DefaultMapper implementations, and no value conversion will take place, i.e. what goes in will come out. Correct?

In other words, I have complete control over how my POCO property values are persisted in the DB if I use my own Mapper. That's pretty powerful and sweet! But then if I don't want to customize any value conversions why couldn't I just use DefaultMapper, i.e. what was the point of making it abstract if it's usable in most scenarios?

As for my first question, I was under the impression that NPoco applies a certain set of default mapping conventions out of the box. But it looks like it's up to the developer to define his own conventions and then override them on the case by case basis.

Lastly, if I already have a bunch of map classes (e.g. MyPocoMap:Map<MyPoco>, etc.), then in order to be able to use them in overriding my conventions can I do this:

public class MappingOverrides : Mappings
{
    public MappingOverrides()
    {
        BuildMappingsFromMaps(
            new MyPocoMap(),
            new AnotherPocoMap(),
            new SomeOtherPocoMap(),
            // ... etc ...
        );
    }
}

and then do scanner.OverrideMappingsWith(new MappingOverrides()); per your example?

Contributor

CaspianCanuck commented Dec 3, 2013

OK I think the fog in my head is slowly beginning to lift. :)

Let me get thing straight: In your Mapper example if DestType is not HStore or SourceType is not String then the conversion methods will return null from their DefaultMapper implementations, and no value conversion will take place, i.e. what goes in will come out. Correct?

In other words, I have complete control over how my POCO property values are persisted in the DB if I use my own Mapper. That's pretty powerful and sweet! But then if I don't want to customize any value conversions why couldn't I just use DefaultMapper, i.e. what was the point of making it abstract if it's usable in most scenarios?

As for my first question, I was under the impression that NPoco applies a certain set of default mapping conventions out of the box. But it looks like it's up to the developer to define his own conventions and then override them on the case by case basis.

Lastly, if I already have a bunch of map classes (e.g. MyPocoMap:Map<MyPoco>, etc.), then in order to be able to use them in overriding my conventions can I do this:

public class MappingOverrides : Mappings
{
    public MappingOverrides()
    {
        BuildMappingsFromMaps(
            new MyPocoMap(),
            new AnotherPocoMap(),
            new SomeOtherPocoMap(),
            // ... etc ...
        );
    }
}

and then do scanner.OverrideMappingsWith(new MappingOverrides()); per your example?

@schotime

This comment has been minimized.

Show comment
Hide comment
@schotime

schotime Dec 3, 2013

Owner

Thats correct.
The Default Mapper is just so that if you are creating a mapper that you have the correct defaults.
There is a WithSmartConventions extension method which can get you started quicker with the scan.

scanner.WithSmartConventions();

The following method takes your maps.

scanner.OverrideMappingsWith(new[] {
            new MyPocoMap(),
            new AnotherPocoMap(),
            new SomeOtherPocoMap(),
            // ... etc ...
});

Thanks for the feedback though.
Any help with the getting started story is greatly appreciated.

Owner

schotime commented Dec 3, 2013

Thats correct.
The Default Mapper is just so that if you are creating a mapper that you have the correct defaults.
There is a WithSmartConventions extension method which can get you started quicker with the scan.

scanner.WithSmartConventions();

The following method takes your maps.

scanner.OverrideMappingsWith(new[] {
            new MyPocoMap(),
            new AnotherPocoMap(),
            new SomeOtherPocoMap(),
            // ... etc ...
});

Thanks for the feedback though.
Any help with the getting started story is greatly appreciated.

@schotime schotime closed this Dec 7, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment