Sunday, November 11, 2012

Getting Domain Name

How can you parse a sub-domain (e.g. www.google.com) and determine the domain (e.g. google.com) from it?

I've tried suggestions starting from regex to parsing it manually, but there are no clear rules.

So now what?

Well, there are lists of 1st level domains and 2nd level domains all over the net, browsers use them to determine which domain you can put cookies on, lets say you have your domain by the name of mydomain.com, you have many websites such as site1.mydomain.com and site2.mydomain.com but you want to share cookies between them, you can set cookies on mydomain.com and both sites will be able to access these cookies, but allowing the websites to read/write cookies on .com domain will be somewhat a security risk and browsers prevent that.

How do they determine what is the domain from the subdomain? 
We can have .com or .co.uk or .info.pl, so there no actual rules we can build an algorithm on.

From the tld and ccsld lists we can determine what the domain part is, by going through the levels until we can no longer find them in the lists, the next one up is the domain name.

This way of determining the domain from subdomains is pretty quick, for 100k domains it takes roughly 275ms on my machine once the tld/sld list is populated.

Here's the code:


/// <summary>
/// Retrieves a domain from a subdomain
/// </summary>
/// <param name="subdomain">the subdomain to be parsed</param>
/// <returns>domain</returns>
public static string GetDomain(string subdomain)
{
    if (string.IsNullOrWhiteSpace(subdomain))
        return null;

    //make sure we have a fresh version of the domain list
    CheckCache();

    //clean up the subdomain
    var cleandomain = subdomain.Trim().ToLower();
    
    //split it into parts by dot
    var domainparts = cleandomain.Split('.');

    //assign the top of the domain parts
    string result = domainparts[domainparts.Length - 1];

    //go over the rest of the parts and add them to the domain until we failed to find a 
    //match in the _domains HashSet, this means we've reached the domain.
    for (int i = domainparts.Length-2; i >= 0; i--)
    {
        if (!_domains.Contains("." + result))
            break;

        result = domainparts[i] + "." + result;
    }

    return result;
}


What happens is that we spit the domain to its parts, then we go over each part and see where we fail to locate that domain in our hashset.

The way to populate the hashset is just insert com, co.uk, info.pl etc'.

Here's the project:
https://github.com/drorgl/ForBlog/tree/master/DomainParsing


Friday, October 26, 2012

Translating Objects of One Type to Another with AutoMapper


If you need to convert object types from one to another, writing it by hand could be a pain, check out AutoMapper.

There's a good getting started on the project's wiki.

The basic idea is to define a map between the source and the destination:

Mapper.CreateMap<source, destination>();

To make sure your mappings are correct:

Mapper.AssertConfigurationIsValid();

You can flatten hierarchies if you name the destination properties as such:

Source:
Element.Name
Destination:
ElementName

You can define conditions in which a certain property will be mapped or not:

Mapper.CreateMap<SourceEntity, DestinationModel>()
    .ForMember(dest => dest.Name, opt=>opt.Condition(i=>((SourceEntity)i.Parent.SourceValue).Id == 2));

You can define custom mappings - for example, we'll switch between the name and the description:

Mapper.CreateMap<SourceEntity, DestinationModel>()
    .ForMember(dest => dest.Name, opt => opt.MapFrom(source => source.Description))
    .ForMember(dest => dest.Description, opt => opt.MapFrom(source => source.Name));

You can have custom mapping transformations:

For Example with lambda expressions:

Mapper.CreateMap<SourceEntity, FlatDestinationModel>()
    .ForMember(i => i.AttributesSomeValue, opt => opt.ResolveUsing(i=>string.Join(", ", i.Attributes.Select(a=>a.Value).ToArray())));

Or with a ValueResolver class:
class AttributeValueResolver : ValueResolver<SourceEntity,string>
{
    protected override string ResolveCore(SourceEntity source)
    {
        return string.Join(", ", source.Attributes.Select(i=>i.Value).ToArray());
    }
}

Mapper.CreateMap<SourceEntity, FlatDestinationModel>().ForMember(i => i.AttributesSomeValue, opt => opt.ResolveUsing<AttributeValueResolver>());

You can ignore certain properties:

Mapper.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Description, opt => opt.Ignore());

You can set specific order in which the properties are mapped:

Mapper.CreateMap<SourceEntity, ChangeAwareDestinationModel>()
   .ForMember(dest => dest.Description, opt => opt.SetMappingOrder(3))
   .ForMember(dest => dest.Name, opt => opt.SetMappingOrder(2));

You can set a specific value for all mapping operations:

Mapper.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Description, opt => opt.UseValue("No Description"));

You can execute code before/after mapping (like putting the object into a tracking list):

Mapper.CreateMap<SourceEntity, DestinationModel>()
    .BeforeMap((source, destination) =>
        {
            Console.WriteLine("Before Map");
        })
    .AfterMap((source, destination) =>
        {
            Console.WriteLine("After Map");
        });

You can create custom type converters:

With lambda:

Mapper.CreateMap<SourceEntity, DestinationModel>().ConvertUsing(s =>
    {
        return new DestinationModel
        {
            Description = s.Description,
            Name = s.Name
        };
    });

or with ITypeConverter:

class CustomTypeConverter : ITypeConverter<SourceEntity,DestinationModel>
{

    #region ITypeConverter<SourceEntity,DestinationModel> Members

    public DestinationModel Convert(ResolutionContext context)
    {
        var src = (SourceEntity)context.SourceValue;

        return new DestinationModel
        {
            Description = src.Description,
            Name = src.Name
        };
    }

    #endregion
}

Mapper.CreateMap<SourceEntity, DestinationModel>().ConvertUsing<CustomTypeConverter>();

You can create custom object initialization (if you want to use a different constructor):

Mapper.CreateMap<SourceEntity, ChangeAwareDestinationModel>().ConstructUsing(c => new ChangeAwareDestinationModel(((SourceEntity)c.SourceValue).Name));

Note that the difference between ConvertUsing and ConstructUsing is that ConvertUsing is invoking the method you provided and exits the conversion procedure while ConstructUsing only instantiates the object and continues execution on the rest of the mapping rules.

You can map lists to array and vice versa (including the normal mapping capabilities):

Mapper.CreateMap<SourceEntity, DestinationModel>();

var dst = Mapper.Map<SourceEntity[], List<DestinationModel>>(src);

Dynamic Map - Creates a proxy from a known interface and populates it based on the interface map (Note that the interface must be public):

Mapper.CreateMap<SourceEntity, IDestinationModel>();

var dst = Mapper.DynamicMap<IDestinationModel>(src);

You can also use LINQ (in AutoMapper.QueryableExtensions namespace):

var dst = src.Project().To<DestinationModel>();

src must be an IQueryable to use Linq

You can replace null with a value:

Mapper.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Description, opt => opt.NullSubstitute("Value is null"));

You can leave a property alone:

Mapper.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Description, opt => opt.UseDestinationValue());

DestinationModel dst = new DestinationModel
{
    Description = "prepopulated description"
};

dst = Mapper.Map<SourceEntity, DestinationModel>(src,dst);

Use Custom Formatter (implementing IValueFormatter):

Mapper.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Description, opt => { opt.ResolveUsing<AttributeCountResolver>(); opt.AddFormatter<CustomFormatter>(); });

class CustomFormatter : IValueFormatter
{
    #region IValueFormatter Members

    public string FormatValue(ResolutionContext context)
    {
        int value = (int)context.SourceValue;

        if (value == 0)
            return "No Items";
        else if (value == 1)
            return "1 Item";
           
        return string.Format("{0} Items", value);
    }

    #endregion
}

You can convert Dictionaries:

Mapper.CreateMap<SourceEntity, DestinationModel>();

var dst = Mapper.Map<Dictionary<int,SourceEntity>, Dictionary<int,DestinationModel>>(src);

Multiple Mapping Engines for multiple mapping profiles:

ConfigurationStore enginestore1 = new ConfigurationStore(new TypeMapFactory(),MapperRegistry.AllMappers());
ConfigurationStore enginestore2 = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.AllMappers());
MappingEngine engine1 = new MappingEngine(enginestore1);
MappingEngine engine2 = new MappingEngine(enginestore2);

enginestore1.CreateMap<SourceEntity,DestinationModel>().ForMember(dest => dest.Description, opt=>opt.Ignore());
enginestore2.CreateMap<SourceEntity, DestinationModel>().ForMember(dest => dest.Name, opt => opt.Ignore());

var dst1 = engine1.Map<SourceEntity,DestinationModel>(srcdata[0]);
var dst2 = engine2.Map<SourceEntity, DestinationModel>(srcdata[1]);

The way it works is by having two ConfigurationStores, each for each engine, you create a map on the store and execute the mapping on the engine.


The only negative thing I have to say about this project is that I haven't found any inline documentation.