2015-11-26 20 views
5

Tôi biết câu hỏi này đã được hỏi nhưng tôi không thể tìm thấy câu trả lời thỏa mãn tôi. Những gì tôi đang cố gắng làm là lấy một số DbSet<T> cụ thể dựa trên tên loại của nó.Tìm DbSet chung trong DbContext động

tôi có như sau:

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")] 
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")] 

class MyDbContext : DbContext { 

    public DbSet<ModelA> A { get; set; } 
    public DbSet<ModelB> B { get; set; } 

    public dynamic GetByName_SwitchTest(string name) { 
     switch (name) { 
      case "A": return A; 
      case "B": return B; 
     } 
    } 

    public dynamic GetByName_ReflectionTest(string fullname) 
    { 
     Type targetType = Type.GetType(fullname); 
     var model = GetType() 
      .GetRuntimeProperties() 
      .Where(o => 
       o.PropertyType.IsGenericType && 
       o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) && 
       o.PropertyType.GenericTypeArguments.Contains(targetType)) 
      .FirstOrDefault(); 
     if (null != model) 
      return model.GetValue(this); 
     return null; 
    } 
} 

Tôi không có sự cố khi nhận kiểu riêng của mình cho dù đó là thông qua một công tắc đơn giản hoặc phản ánh. Tuy nhiên, tôi cần phải trả về kiểu như là một động vì tôi không biết loại DbSet sẽ là gì. Sau đó, ở một nơi khác trong cùng một assembly, tôi sử dụng nó theo cách này:

// MyDbContext MyDbContextInstance.. 
var model = MyDbContextInstance.GetByName_SwitchTest("A"); 
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException 

Tại thời điểm này model chứa một thể hiện của một loại InternalDbSet<ModelA>. Từ đó, bất kỳ sử dụng tôi làm với các đối tượng model tôi nhận được một RunTimeBinderException: 'Microsoft.Data.Entity.Internal.InternalDbSet' không chứa một định nghĩa cho 'FirstOrDefault'

tra trên trang web, tôi tìm thấy một blog post giải thích rằng (dixit blog của mình):

lý do cuộc gọi đến FirstOrDefault() không thành công là loại thông tin không có sẵn khi chạy. Lý do không phải là là do các loại ẩn danh không được công khai. Khi phương thức trả về một thể hiện của loại ẩn danh đó, nó trả về một System.Object tham chiếu một cá thể của một loại ẩn danh - một loại mà thông tin của chúng không có sẵn cho chương trình chính.

Và sau đó ông chỉ là một giải pháp:

Các giải pháp thực sự là khá đơn giản. Tất cả chúng ta phải làm là mở ra AssemplyInfo.cs của dự án ClassLibrary1 và thêm dòng sau vào nó: [assembly:InternalsVisibleTo("assembly-name")]

tôi đã thử giải pháp này trên mã của tôi, nhưng nó không hoạt động. Đối với thông tin tôi có một giải pháp asp.net 5 với hai hội đồng chạy trên dnx dotnet46. Một ứng dụng và một dll chứa tất cả các mô hình của tôi và DbContext. Tất cả các cuộc gọi liên quan tôi làm được đặt trên dll mặc dù.

Giải pháp này có cơ hội hoạt động không? Tôi có thiếu gì đó không? Bất kỳ con trỏ nào sẽ được đánh giá cao?

Cảm ơn trước

[EDIT]

Tôi đã cố gắng để trở IQueryable<dynamic> hơn dynamic và tôi có thể làm các truy vấn cơ bản model.FirstOrDefault();nhưng trên hết tôi muốn để có thể lọc trên một trường quá:

var record = model.FirstOrDefault(item => item.MyProperty == true); 
+0

thế nào về sự thay đổi năng động để IEnumerable ? – Kelmen

+0

Tôi đã thử một cái gì đó tương tự (thay đổi để IQueryable ) và mặc dù tôi có thể làm một số truy vấn như 'model.FirstOrDefault()' nó sẽ không cho phép tôi làm những việc như 'model.FirstOrDefault (item => item.MyProperty = = true) '. làm giảm tính hữu dụng của sự vật. – DarkUrse

+0

Tôi đã gặp phải sự cố tương tự này. Bất kỳ cơ hội nào bạn đã đạt đến độ phân giải? – JosephGarrone

Trả lời

0

* Tuyên bố từ chối trách nhiệm: Phản hồi này không đưa ra câu trả lời nghiêm ngặt cho câu hỏi của tôi. Đó là một cách tiếp cận khác để giải quyết vấn đề của riêng tôi.Tôi biết đây là một ví dụ cụ thể cho một tình huống nhất định sẽ không hoạt động cho tất cả mọi người. Tôi đăng cách tiếp cận này với hy vọng nó sẽ giúp một ai đó nhưng sẽ không đánh dấu nó như là câu trả lời như tôi vẫn hy vọng cho một giải pháp thực sự.

Để bắt đầu, hãy chấp nhận thực tế rằng thông tin hữu ích duy nhất chúng tôi có thể lấy ra khỏi mã hiện tại là liệu bản ghi có tồn tại hay không .. Bất kỳ nỗ lực nào của truy vấn động sau đó sẽ cung cấp cho RuntimeBinderException.

Sau đó, hãy tiếp tục với một thực tế khác; DbContext.Add (đối tượng) và DbContext.Update (đối tượng) không dựa trên mẫu để chúng tôi có thể sử dụng chúng để lưu mô hình của chúng tôi (Thay vì db.A.Add() hoặc db.A.Update())

Trong tình hình của riêng tôi, không còn là cần thiết để tìm ra một thủ tục

  1. Xác định mô hình một chút khác nhau

để bắt đầu, tôi cần một lĩnh vực đó là retrievable trên tất cả các mô hình của tôi mà rõ ràng phải là một cách để xác định một bản ghi duy nhất.

// IModel give me a reliable common field to all my models (Fits my DB design maybe not yours though) 
interface IModel { Guid Id { get; set; } } 

// ModelA inherit IModel so that I always have access to an 'Id' 
class ModelA : IModel { 
    public Guid Id { get; set; } 
    public int OtherField { get; set; } 
} 

// ModelB inherit IModel so that I always have access to an 'Id' 
class ModelB : IModel { 
    public Guid Id { get; set; } 
    public string WhateverOtherField { get; set; } 
} 
  1. Re-mục đích động truy vấn một chút để làm một cái gì đó chúng ta biết công trình

tôi đã không tìm thấy một cách để làm truy vấn thông minh tự động, vì vậy thay vào đó tôi biết tôi có thể xác định một hồ sơ đáng tin cậy và biết liệu nó có tồn tại hay không.

class MyDbContext : DbContext { 

    public DbSet<ModelA> A { get; set; } 
    public DbSet<ModelB> B { get; set; } 

    // In my case, this method help me to know the next action I need to do 
    // The switch/case option is not pretty but might have better performance 
    // than Reflection. Anyhow, this is one's choice. 
    public bool HasRecord_SwitchTest(string name) { 
     switch (name) { 
      case "A": return A.AsNoTracking().Any(o => o.Id == id); 
      case "B": return B.AsNoTracking().Any(o => o.Id == id); 
     } 
     return false; 
    } 

    // In my case, this method help me to know the next action I need to do 
    public bool HasRecord_ReflectionTest(string fullname) 
    { 
     Type targetType = Type.GetType(fullname); 
     var model = GetType() 
      .GetRuntimeProperties() 
      .Where(o => 
       o.PropertyType.IsGenericType && 
       o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) && 
       o.PropertyType.GenericTypeArguments.Contains(targetType)) 
      .FirstOrDefault(); 
     if (null != model) 
      return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id); 
     return false; 
    } 

    // Update and save immediately - simplified for example 
    public async Task<bool> UpdateDynamic(object content) 
    { 
     EntityEntry entry = Update(content, GraphBehavior.SingleObject); 
     return 1 == await SaveChangesAsync(true); 
    } 

    // Insert and save immediately - simplified for example 
    public async Task<bool> InsertDynamic(object content) 
    { 
     EntityEntry entry = Add(content, GraphBehavior.SingleObject); 
     return 1 == await SaveChangesAsync(true); 
    } 
} 
  1. Một chút đường ống dẫn nước để cung cấp cho một cảm giác với tình hình của tôi

Tiếp theo, những gì tôi cần phải làm gì với các truy vấn năng động là một cách để tái tạo dữ liệu từ máy chủ đến khách hàng của tôi. (Tôi đã bỏ qua một đoạn lớn của kiến ​​trúc để đơn giản hóa ví dụ này)

class ReplicationItem 
{ 
    public ReplicationAction Action { get; set; } // = Create, Update, Delete 
    public string ModelName { get; set; } // Model name 
    public Guid Id { get; set; } // Unique identified across whole platform 
} 
  1. Kết nối các bit.

Bây giờ, đây là thói quen kết nối các bit

public async void ProcessReplicationItem(ReplicationItem replicationItem) 
{ 
    using (var db = new MyDbContext()) 
    { 
     // Custom method that attempts to get remote value by Model Name and Id 
     // This is where I get the strongly typed object 
     var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id); 
     bool hasRemoteRecord = remoteRecord.Content != null; 

     // Get to know if a local copy of this record exists. 
     bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id); 

     // Ensure response is valid whether it is a successful get or error is meaningful (ie. NotFound) 
     if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound) 
     { 
      switch (replicationItem.Action) 
      { 
       case ReplicationAction.Create: 
       { 
        if (hasRemoteRecord) 
        { 
         if (hasLocalRecord) 
          await db.UpdateDynamic(remoteRecord.Content); 
         else 
          await db.InsertDynamic(remoteRecord.Content); 
        } 
        // else - Do nothing 
        break; 
       } 
       case ReplicationAction.Update: 
        [etc...] 
      } 
     } 
    } 
} 

// Get record from server and with 'response.Content.ReadAsAsync' type it 
// already to the appropriately 
public static async Task<Response> TryGetAsync(ReplicationItem item) 
{ 
    if (string.IsNullOrWhiteSpace(item.ModelName)) 
    { 
     throw new ArgumentException("Missing a model name", nameof(item)); 
    } 

    if (item.Id == Guid.Empty) 
    { 
     throw new ArgumentException("Missing a primary key", nameof(item)); 
    } 

    // This black box, just extrapolate a uri based on model name and id 
    // typically "api/ModelA/{the-guid}" 
    string uri = GetPathFromMessage(item); 

    using (var client = new HttpClient()) 
    { 
     client.BaseAddress = new Uri("http://localhost:12345"); 

     HttpResponseMessage response = await client.GetAsync(uri); 
     if (response.IsSuccessStatusCode) 
     { 
      return new Response() 
      { 
       Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)), 
       Success = true, 
       ResponseCode = response.StatusCode 
      }; 
     } 
     else 
     { 
      return new Response() 
      { 
       Success = false, 
       ResponseCode = response.StatusCode 
      }; 
     } 
    } 
} 

public class Response 
{ 
    public object Content { get; set; } 
    public bool Success { get; set; } 
    public HttpStatusCode ResponseCode { get; set; } 
} 

ps: tôi vẫn còn quan tâm đến một câu trả lời thực tế, vì vậy hãy tiếp tục gửi bài cho câu trả lời khác nếu bạn có một thực tế để chia sẻ .

0

Vậy làm cách nào tôi làm điều đó khi tôi không biết về <T> trong thời gian biên dịch.

Đầu tiên cần có loại như phương thức DbContext.Set trả về một cá thể DbSet không chung để truy cập vào các thực thể thuộc loại đã cho trong ngữ cảnh và cửa hàng cơ bản.

public virtual DbSet Set(Type entityType) 

Lưu ý đối số ở đây là loại thực thể cần đặt lại. Và đặt cho loại thực thể nhất định là giá trị trả lại.

var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>); 

tại một khi tôi có loại này

if(type != null) 
{ 
DbSet context = context.Set(type); 
} 

Hoặc một liner sẽ

DbSet mySet = context.Set(Type.GetType("<Your Entity Name>")); 
+1

Cảm ơn bạn đã trả lời. Tuy nhiên - 'public virtual DbSet Set (Type entityType)' - dường như không còn tồn tại với Entity Framework 7. Ngày đầu đó, 'DbSet' sẽ không cho phép tôi thực hiện các truy vấn API thông thạo trên các mô hình. – DarkUrse

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