2

我正在尝试使用 ValueInjector(来自 NuGet 的最新版本)将来自我的 EntityFramework Code First 对象的数据注入我的视图模型。我也想做相反的事情,但这是我开始的尝试。

我研究了各种映射并尝试了其中的许多。我还没有找到一个可以满足我需要的问题,而且我认为我的需要是一个相当基本的问题(SO 上有几个这样的问题,每个问题似乎都得到了不同的答案,这很好。)。

我真的试图对此进行尽职调查。我用谷歌搜索并搜索了 CodePlex 并下载了 ProDinner 应用程序(启动并运行它并在调试中逐步执行代码)。

我现在有自己的 ValueInjection 类来处理注入。我将以一种非常友好的方式粘贴我的代码和对象,以便您可以抓取并运行它们。请注意,我有一个使用基本 LoopValueInjection 的解决方案,但我不喜欢它,因为它需要手动管道来进行从实体到映射然后映射到实体的注入。ProDinner 示例有一个更加模板化的方法,我喜欢这种方法,但我无法适应我的需要。

我认为我的代码在逻辑上搞砸的地方是,如果源属性类型不是简单的对象,我不明白如何强制进行递归注入。在此示例中,Person.Address.* 属性将按名称匹配并在 PersonViewModel 类中键入这些属性;但是,注入循环遍历 Person 上的属性,并尝试匹配 Person.Address 属性的名称和类型。

我以为行

        Object result = Activator.CreateInstance(c.SourceProp.Type)
            .InjectFrom<CloneInjection>(c.SourceProp.Value);

会执行此递归,但我认为不会。

所以......谁能给我任何关于如何解决这个问题的指导?

//////  ENTITY MODELS

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public virtual Address Address { get; set; }
}

public class Address
{
    public int Id { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

//////  VIEW MODEL 

public class PersonViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int PersonId { get; set; }
    public int AddressId { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; } 
}   

////// CUSTOM INJECTOR
public class EntityToViewModel : ConventionInjection
{

    protected override bool Match(ConventionInfo c)
    {
        //ignore int = 0 and DateTime = to 1/01/0001
        if (c.SourceProp.Type == typeof(DateTime) && (DateTime)c.SourceProp.Value == default(DateTime) ||
            (c.SourceProp.Type == typeof(int) && (int)c.SourceProp.Value == default(int)))
            return false;

        if (c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null)
            return true;

        if (c.SourceProp.Type == typeof(int) &&
            c.TargetProp.Type == typeof(int) )
        {
            if( "id".Equals(c.SourceProp.Name.ToLower()) &&                 
                c.TargetProp.Name.ToLower().EndsWith("id") &&
                c.TargetProp.Name.StartsWith(c.Source.Type.Name))
            return true;
        }

        //Transition logic to SetValue for value types. This should 
        //allow Address values on Person to be set.
        if (!c.SourceProp.Type.IsPrimitive || c.SourceProp.Type.Equals(typeof(string)))
            return true;

        return false;

        //put id logic matching here
        //return c.SourceProp.Name == c.TargetProp.Name && c.SourceProp.Value != null;
    }

    protected override object SetValue(ConventionInfo c)
    {
        //If type is primative or string return the value as is   
        if (c.SourceProp.Type.IsPrimitive || c.SourceProp.Type.Equals(typeof(string)))
            return c.SourceProp.Value;

        Object result = Activator.CreateInstance(c.SourceProp.Type)
            .InjectFrom<CloneInjection>(c.SourceProp.Value);

        //for simple object types create a new instace and apply the clone injection on it
        return result;
    }
}   

////// Program.cs
public class Program
{

    public static void Main(string[] args)
    {
        Person defaultPerson = getDefaultPerson();
        //Throws an error. I'm not sure where in the pipeline this occurs, but 
        //it seems to happen somewhere other than the Match & SetValue method of 
        //my EntityToViewModel injection
        PersonViewModel pvm = CreateFromEntity(defaultPerson);          

        //Works, but want it more generic & without having to 
        //include hardcoded prefix for every non-simple object on my EF Model
        pvm = CreateFromEntityWorking(defaultPerson);
        Console.ReadLine();
    }

    private static PersonViewModel CreateFromEntity(Person person)
    {
        PersonViewModel pvm = new PersonViewModel();
        pvm.InjectFrom<EntityToViewModel>(person);
        return pvm;
    }

    ///WORKING MAPPING BUT SEEMS TOO HARDCODED
    private static PersonViewModel CreateFromEntityWorking(Person person)
    {
        PersonViewModel personvm = new PersonViewModel();

        //Fill out view model properties with the same name as those on Person
        personvm.InjectFrom(new LoopValueInjection().TargetPrefix("Person"), person);

        if (person != null && person.Address != null)
        {
            //Fill out view model properties for the Address
            personvm.InjectFrom(new LoopValueInjection().TargetPrefix("Address"), person.Address);
        }

        return personvm;
    }       

    public static Person getDefaultPerson()
    {
        Person p = new Person();
        Address a = new Address();
        p.Id = 1;
        p.FirstName = "John";
        p.LastName = "McClain";
        a.City = "New York";
        a.State = "New York";
        a.Zip = "55555";
        a.Id = 2;
        p.Address = a;
        return p;
    }   

}
4

1 回答 1

2

终于找到了我需要的例子。可悲的是,尽管我进行了所有的搜索,但我现在才刚刚登陆这些。这不是我自己的错;然而,我的脚因为我一直在踢它而感到酸痛。

相关网址:

文档 (RTFM) http://valueinjecter.codeplex.com/documentation

展平示例和约定 http://valueinjecter.codeplex.com/wikipage?title=flattening&referringTitle=Documentation

展开示例和约定 http://valueinjecter.codeplex.com/wikipage?title=unflattening&referringTitle=Documentation

我的问题的解决方案:

必须在视图模型上使用的约定是顶级属性不以它们的属性名称为前缀,因为它存在于实体上。因此,在我下面的类中,FirstName、LastName 和 Id 都作为我的 Person 实体的顶级属性存在,它们在我的视图模型类中没有前缀。Person.Address 属性的 City、State、Zip 和 Id 都带有前缀。

奇怪的是,我在尝试实现自己的注入时想到了这个解决方案。我得到了一个从 Entity 到 ViewModel 的工作......只是不是相反。

public class PersonViewModelPrefixed
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Id { get; set; }
    public int AddressId { get; set; }
    public string AddressCity { get; set; }
    public string AddressState { get; set; }
    public string AddressZip { get; set; } 
}

public static void Main(string[] args)
{   
        Person defaultPerson = getDefaultPerson();
        PersonViewModelPrefixed defaultPrefixedVm = getDefaultPrefixedViewModel();

        //flatten - Entity to View Model
        PersonViewModelPrefixed pvm = Flatten(defaultPerson);

        //unflatten - View Model to Entity
        Person person2 = Unflatten(defaultPrefixedVm);  
    Console.ReadLine();
}       

//unflatten - View Model to Entity
private static Person Unflatten(PersonViewModelPrefixed personViewModel)
{
    Person p = new Person();
    p.InjectFrom<UnflatLoopValueInjection>(personViewModel);
    return p;
}

//flatten - Entity to View Model
private static PersonViewModelPrefixed Flatten(Person person)
{
    PersonViewModelPrefixed pvm = new PersonViewModelPrefixed();
    pvm.InjectFrom<FlatLoopValueInjection>(person);
    return pvm;
}

那么,鉴于这些细节,是否有人对如何使 Flatten 和 Unflatten 方法更通用有一些建议?这就是我接下来要做的工作。

于 2012-10-11T14:29:15.920 回答