English 中文(简体)
Simple Convention Automapper for two-way Mapping (Entities to/from ViewModels)
原标题:

UPDATE: this stuff has evolved into a nice project, see it at http://valueinjecter.codeplex.com


check this out, I just wrote a simple automapper, it takes the value from the property with the same name and type of one object and puts it into another, and you can add exceptions (ifs, switch) for each type you may need

so tell me what do you think about it ?

I did it so I could do something like this:

Product –> ProductDTO

ProductDTO –> Product

that s how it begun:

I use the "object" type in my Inputs/Dto/ViewModels for DropDowns because I send to the html a IEnumerable<SelectListItem> and I receive a string array of selected keys back

 public void Map(object a, object b)
    {
        var pp = a.GetType().GetProperties();
        foreach (var pa in pp)
        {
            var value = pa.GetValue(a, null);

            // property with the same name in b
            var pb = b.GetType().GetProperty(pa.Name);
            if (pb == null)
            {
                //no such property in b
                continue;
            }

            if (pa.PropertyType == pb.PropertyType)
            {
                pb.SetValue(b, value, null);
            }

        }
    }

UPDATE: the real usage:
the Build methods (Input = Dto):

        public static TI BuildInput<TI, T>(this T entity) where TI: class, new()
        {
            var input = new TI();
            input = Map(entity, input) as TI;
            return input;
        }

        public static T BuildEntity<T, TI, TR>(this TI input)
            where T : class, new()
            where TR : IBaseAdvanceService<T>
        {               
            var id = (long)input.GetType().GetProperty("Id").GetValue(input, null);
            var entity = LocatorConfigurator.Resolve<TR>().Get(id) ?? new T();
            entity = Map(input, entity) as T;
            return entity;
        }

        public static TI RebuildInput<T, TI, TR>(this TI input)
            where T: class, new()
            where TR : IBaseAdvanceService<T>
            where TI : class, new()
        {

                return input.BuildEntity<T, TI, TR>().BuildInput<TI, T>();
            }

in the controller:

    public ActionResult Create()
    { 
        return View(new Organisation().BuildInput<OrganisationInput, Organisation>());
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(OrganisationInput o)
    {
        if (!ModelState.IsValid)
        {
            return View(o.RebuildInput<Organisation,OrganisationInput, IOrganisationService>());                
        }
        organisationService.SaveOrUpdate(o.BuildEntity<Organisation, OrganisationInput, IOrganisationService>());
        return RedirectToAction("Index");
    }

The real Map method

public static object Map(object a, object b)
        {
            var lookups = GetLookups();

            var propertyInfos = a.GetType().GetProperties();
            foreach (var pa in propertyInfos)
            {
                var value = pa.GetValue(a, null);

                // property with the same name in b
                var pb = b.GetType().GetProperty(pa.Name);
                if (pb == null)
                {
                    continue;
                }

                if (pa.PropertyType == pb.PropertyType)
                {
                    pb.SetValue(b, value, null);
                }
                else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(LookupItem))
                {
                    pb.SetValue(b, (pa.GetValue(a, null) as LookupItem).GetSelectList(pa.Name), null);
                }
                else if (lookups.Contains(pa.Name) && pa.PropertyType == typeof(object))
                {
                    pb.SetValue(b, pa.GetValue(a, null).ReadSelectItemValue(), null);
                }
                else if (pa.PropertyType == typeof(long) && pb.PropertyType == typeof(Organisation))
                {
                    pb.SetValue(b, pa.GetValue<long>(a).ReadOrganisationId(), null);
                }
                else if (pa.PropertyType == typeof(Organisation) && pb.PropertyType == typeof(long))
                {
                    pb.SetValue(b, pa.GetValue<Organisation>(a).Id, null);
                }
            }

            return b;
        }
最佳回答

One thing you might want to add is to cache the reflection bits. If you map an object twice, you probably don t want to look up all the reflection stuff again. Also, things like GetValue and SetValue are quite slow, I switched to late-bound delegates + Reflection.Emit to speed things up.

问题回答

Just use AutoMapper. This is fine, but it ll grow into a mini project.

Just some things AM (the real one) does is:

  • reporting if you have properties that can t be mapped to
  • flattening objects
  • providing hooks for you to customise some aspects, not in a big switch statement
  • using Expression.Compile for perf reasons rather than reflection directly

But it s certainly an interesting space to mess about in, and the idea of auto mapping is certainly useful.

A bit like DI in 15 or 33 lines vs NInject or its friends - cool, but why?.

I take it you ve read the article and comments regarding 2 way mapping on Jimmy s blog ?

Just a thought:

One might wonder what the point of an abstraction is if the abstraction is so easily mapped to that being abstracted.

you can add exceptions (ifs, switch) for each type you may need

You re not going to do this. Seriously. Case statements acting on object types are bad OOP style. I mean, really bad. Like driving with a drink of Vodka inside your stomach. It may work for some time, but eventually you get into trouble.

OK Jimmy told you not to use AutoMapper... but I bet he meant something else. Now, have you invented something different - something that make Jimmy happy? ;-) No, you just made your own half-rolled AutoMapper. And Jimmy told you not to use it! ;-)

So here s my suggestion: ignore what Jimmy says, just think yourself.. and use AutoMapper ;-)





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签