2010-08-19 29 views
16

Tôi đang cố gắng thực thi Mã khung thực thể đầu tiên CTP4. Giả sử tôi có:Có thể tự động ánh xạ khóa ngoài cho một đối tượng bằng cách sử dụng kho lưu trữ không?

public class Parent 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Child 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Parent Mother { get; set; } 
} 

public class TestContext : DbContext 
{ 
    public DbSet<Parent> Parents { get; set; } 
    public DbSet<Child> Children { get; set; } 
} 

public class ChildEdit 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int MotherId { get; set; } 
} 

Mapper.CreateMap<Child, ChildEdit>(); 

Ánh xạ tới mô hình Chỉnh sửa không phải là vấn đề. Trên màn hình của tôi, tôi chọn người mẹ qua một số kiểm soát (DropDownList, autocompleter, vv) và Id của người mẹ được đăng ở phía sau:

[HttpPost] 
public ActionResult Edit(ChildEdit posted) 
{ 
    var repo = new TestContext(); 

    var mapped = Mapper.Map<ChildEdit, Child>(posted); // <------- ??????? 
} 

Làm thế nào tôi nên giải quyết việc lập bản đồ cuối cùng? Tôi không muốn đặt Mother_Id vào đối tượng Child. Bây giờ tôi sử dụng giải pháp này, nhưng tôi hy vọng nó có thể được giải quyết trong Automapper.

 Mapper.CreateMap<ChildEdit, Child>() 
      .ForMember(i => i.Mother, opt => opt.Ignore()); 

     var mapped = Mapper.Map<ChildEdit, Child>(posted); 
     mapped.Mother = repo.Parents.Find(posted.MotherId); 

EDIT này hoạt động, nhưng bây giờ tôi phải làm điều đó cho mỗi phím nước ngoài (BTW: bối cảnh sẽ được tiêm trong dung dịch cuối cùng):

 Mapper.CreateMap<ChildEdit, Child>(); 
      .ForMember(i => i.Mother, 
         opt => opt.MapFrom(o => 
           new TestContext().Parents.Find(o.MotherId) 
             ) 
        ); 

gì tôi thực sự muốn sẽ là:

 Mapper.CreateMap<int, Parent>() 
      .ForMember(i => i, 
         opt => opt.MapFrom(o => new TestContext().Parents.Find(o)) 
        ); 

     Mapper.CreateMap<ChildEdit, Child>(); 

Có thể với Automapper không?

Trả lời

17

Trước tiên, tôi sẽ giả định rằng bạn có giao diện kho lưu trữ như IRepository<T>

Sau đó tạo lớp sau:

public class EntityConverter<T> : ITypeConverter<int, T> 
{ 
    private readonly IRepository<T> _repository; 
    public EntityConverter(IRepository<T> repository) 
    { 
     _repository = repository; 
    } 
    public T Convert(ResolutionContext context) 
    { 
     return _repository.Find(System.Convert.ToInt32(context.SourceValue));  
    } 
} 

Về cơ bản, lớp này sẽ được sử dụng để thực hiện tất cả chuyển đổi giữa thực thể int và tên miền. Nó sử dụng "Id" của thực thể để tải nó từ Kho lưu trữ. IRepository sẽ được tiêm vào bộ chuyển đổi bằng cách sử dụng một thùng chứa IoC, nhưng nhiều hơn và sau đó.

Hãy cấu hình ánh xạ AutoMapper sử dụng:

Mapper.CreateMap<int, Mother>().ConvertUsing<EntityConverter<Mother>>(); 

tôi khuyên bạn nên tạo bản đồ này "chung chung" thay vì để nếu bạn có tài liệu tham khảo khác để "Mother" trên các lớp khác họ đang lập bản đồ tự động mà không cần thêm -cố gắng.

Về dependency injection cho IRepository, nếu bạn đang sử dụng lâu đài Windsor, cấu hình AutoMapper nên cũng có:

IWindsorContainer container = CreateContainer(); 
Mapper.Initialize(map => map.ConstructServicesUsing(container.Resolve)); 

Tôi đã sử dụng phương pháp này và nó hoạt động khá tốt.

+0

Làm việc như một sự quyến rũ. Cảm ơn. –

+0

Bạn có thể cho biết cách sử dụng giải pháp của mình khi tôi muốn ánh xạ id tới thực thể không? Tôi có nghĩa là làm thế nào để lập bản đồ khi tôi thực hiện tất cả mọi thứ? – user2412672

6

Đây là cách tôi đã làm nó: (sử dụng ValueInjecter)
tôi đã thực hiện các yêu cầu lớn hơn một chút chỉ để hiển thị như thế nào nó hoạt động


[TestFixture] 
public class JohnLandheer 
{ 
    [Test] 
    public void Test() 
    { 
     var child = new Child 
     { 
      Id = 1, 
      Name = "John", 
      Mother = new Parent { Id = 3 }, 
      Father = new Parent { Id = 9 }, 
      Brother = new Child { Id = 5 }, 
      Sister = new Child { Id = 7 } 
     }; 
     var childEdit = new ChildEdit(); 

     childEdit.InjectFrom(child) 
       .InjectFrom<EntityToInt>(child); 

     Assert.AreEqual(1, childEdit.Id); 
     Assert.AreEqual("John", childEdit.Name); 
     Assert.AreEqual(3, childEdit.MotherId); 
     Assert.AreEqual(9, childEdit.FatherId); 
     Assert.AreEqual(5, childEdit.BrotherId); 
     Assert.AreEqual(7, childEdit.SisterId); 
     Assert.AreEqual(0, childEdit.Sister2Id); 

     var c = new Child(); 

     c.InjectFrom(childEdit) 
      .InjectFrom<IntToEntity>(childEdit); 

     Assert.AreEqual(1, c.Id); 
     Assert.AreEqual("John", c.Name); 
     Assert.AreEqual(3, c.Mother.Id); 
     Assert.AreEqual(9, c.Father.Id); 
     Assert.AreEqual(5, c.Brother.Id); 
     Assert.AreEqual(7, c.Sister.Id); 
     Assert.AreEqual(null, c.Sister2); 
    } 

    public class Entity 
    { 
     public int Id { get; set; } 
    } 

    public class Parent : Entity 
    { 
     public string Name { get; set; } 
    } 

    public class Child : Entity 
    { 
     public string Name { get; set; } 
     public Parent Mother { get; set; } 
     public Parent Father { get; set; } 
     public Child Brother { get; set; } 
     public Child Sister { get; set; } 
     public Child Sister2 { get; set; } 
    } 

    public class ChildEdit 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public int MotherId { get; set; } 
     public int FatherId { get; set; } 
     public int BrotherId { get; set; } 
     public int SisterId { get; set; } 
     public int Sister2Id { get; set; } 
    } 

    public class EntityToInt : LoopValueInjection 
    { 
     protected override bool TypesMatch(Type sourceType, Type targetType) 
     { 
      return sourceType.IsSubclassOf(typeof(Entity)) && targetType == typeof(int); 
     } 

     protected override string TargetPropName(string sourcePropName) 
     { 
      return sourcePropName + "Id"; 
     } 

     protected override bool AllowSetValue(object value) 
     { 
      return value != null; 
     } 

     protected override object SetValue(object sourcePropertyValue) 
     { 
      return (sourcePropertyValue as Entity).Id; 
     } 
    } 

    public class IntToEntity : LoopValueInjection 
    { 
     protected override bool TypesMatch(Type sourceType, Type targetType) 
     { 
      return sourceType == typeof(int) && targetType.IsSubclassOf(typeof(Entity)); 
     } 

     protected override string TargetPropName(string sourcePropName) 
     { 
      return sourcePropName.RemoveSuffix("Id"); 
     } 

     protected override bool AllowSetValue(object value) 
     { 
      return (int)value > 0; 
     } 

     protected override object SetValue(object sourcePropertyValue) 
     { 
      // you could as well do repoType = IoC.Resolve(typeof(IRepo<>).MakeGenericType(TargetPropType)) 
      var repoType = typeof (Repo<>).MakeGenericType(TargetPropType); 
      var repo = Activator.CreateInstance(repoType); 
      return repoType.GetMethod("Get").Invoke(repo, new[] {sourcePropertyValue}); 
     } 
    } 

    class Repo<T> : IRepo<T> where T : Entity, new() 
    { 
     public T Get(int id) 
     { 
      return new T{Id = id}; 
     } 
    } 

    private interface IRepo<T> 
    { 
     T Get(int id); 
    } 
} 
+0

Cảm ơn. Tôi muốn tránh phải tạo ra một repo trong logic lập bản đồ bởi vì nó đắt tiền, nhưng từ giải pháp của bạn tôi thu thập đó là không đúng sự thật. Đúng?. Tôi sẽ cố gắng nhận được một cái gì đó tương tự trong Automapper và đăng giải pháp của tôi. –

+0

bạn phải lấy dữ liệu từ đâu đó :), thường là tôi làm IoC.Resolve (loại), và điều đó sẽ không tốn kém vì container IoC sẽ cung cấp cho một cá thể đã được tạo – Omu

+0

Ở đây tôi đã làm nó uber chung vì tôi muốn cho thấy cách bạn xử lý nhiều thuộc tính của các loại khác nhau chỉ với một lần tiêm, nhưng tất nhiên có thể làm điều đó mà không có MakeGenericType, method.invoke và tất cả những điều đó nhưng có lẽ bạn sẽ phải tạo nhiều lần tiêm, bạn có thể xem xét " bắt đầu "của ValueInjecter – Omu

2

Có thể xác định chính nước ngoài tại EF cách này cũng như:

[ForeignKey("MotherId")] 
public virtual Parent Mother { get; set; } 
public int MotherId { get; set; } 

Trong trường hợp này, nó không cần thiết phải làm một truy vấn thêm để tìm mẹ.Chỉ cần gán MotherId của ViewModel cho MotherId của Model.

Các vấn đề liên quan