6

我有一個DbContext與以下幾個類型成員的:如何推廣對DbSet <TEntity>成員的訪問權限?

public DbSet<JobLevel> JobLevels { get; set; } 
public DbSet<Country> Countries { get; set; } 
public DbSet<Race> Races { get; set; } 
public DbSet<Language> Languages { get; set; } 
public DbSet<Title> Titles { get; set; } 

所有這些都是where T: IdNamePairBase,它只有IdName成員。我拼命想尋找一個共同的接口與訪問這些成員,概括出以下MVC3控制器代碼到一個控制器:

public ActionResult Edit(DropDownListModel model, Guid) 
{ 
    var dbSet = _dbContext.Countries; 
    var newItems = model.Items.Where(i => i.IsNew && !i.IsDeleted).Select(i => new { i.Name }); 
    foreach (var item in newItems) 
    { 
     if (!string.IsNullOrWhiteSpace(item.Name)) 
     { 
      var undead = ((IEnumerable<IdNamePairBase>)dbSet).FirstOrDefault(p => p.Name.ToLower() == item.Name.ToLower()); 
      if (undead != null) 
      { 
       // Assign new value to update to the new char. case if present. 
       undead.Name = item.Name; 
       undead.IsDeleted = false; 
       _dbContext.SaveChanges(); 
       continue; 
      } 
      var newPair = new Country { Name = item.Name }; 
      dbSet.Add(newPair); 
      _dbContext.SaveChanges(); 
     } 
    } 
    return RedirectToAction("Edit", new {listName = model.ListName}); 
} 

我怎麼能去解決我的問題,現在我需要一個控制器爲每個DbContext構件,如一個以上專用於DbSet<Country> Countries

部分解決:除了類似下面GertArnold的回答線,之前我知道關於_dbContext.Set<T>所有他強調了,我實現了我的上下文類這種方法來獲得集合特定類型的:

public IEnumerable<DbSet<T>> GetDbSetsByType<T>() where T : class 
{ 
    //var flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance; 
    var props = GetType().GetProperties() 
     .Where(p => p.PropertyType.IsGenericType && p.PropertyType.Name.StartsWith("DbSet")) 
     .Where(p => p.PropertyType.GetGenericArguments().All(t => t == typeof(T))); 
    return props.Select(p => (DbSet<T>)p.GetValue(this, null)); 
} 
+0

在不同的上下文中完全不同的方法,但也許對你有趣:http://stackoverflow.com/questions/9762808/change-fluent-api-mapping-dynamically – 2012-03-27 18:59:50

回答

9

一些推廣可以通過使用

var dbSet = _dbContext.Set<T> 

並將您的大部分方法放入具有泛型類型參數的方法中。

但是,應該有一個開關的地方,以決定哪種類型應指定並創建哪一類,因爲我覺得類型爲模型的屬性提供(是嗎?)。所以它可能不會看起來很優雅,但可能會更短,使用DRY-er代碼。

3

要在格特·阿諾德的回答補充,我想指出,有對的DbContext另一個方法重載,從一類對象返回一個普通DbSet:

var dbSet = dbContext.Set(typeof(T)) 

如果你想添加盲目的對象,然後建立set.Create()方法的對象,或者如果你已經擁有了「new」 keyowrd創建了一個對象,你可以通過使用(類似於this answer)將其轉換

var entity = dbSet.Create(); 
dbSet.Add(entity); 
DbEntityEntry entry = context.Entry(entity); 
entry.CurrentValues.SetValues(yourObject); 
3

我一直在尋找一個答案到th是問題,我發現使用託管擴展框架很容易。在這篇文章的底部有一個更快捷的方法,但是MEF允許一個更加可擴展的方法。

MEF允許你建立從不同的組件動態訪問插件;但是它可以用來在一個單獨的程序集應用程序中快速填充集合。本質上,我們將使用它作爲將我們的程序集反映回類中的安全方式。爲了使這個功能完整,我還將實施戰略模式實體框架模型。

添加引用到您的項目,指向System.ComponentModel.Composition。這將訪問MEF庫。

現在,我們需要實現的策略模式。如果您沒有Interfaces文件夾,請創建一個,然後添加IEntity.cs,如下所示。

IEntity。CS現在

namespace Your.Project.Interfaces 
{ 
    /// <summary> 
    ///  Represents an entity used with Entity Framework Code First. 
    /// </summary> 
    public interface IEntity 
    { 
     /// <summary> 
     ///  Gets or sets the identifier. 
     /// </summary> 
     /// <value> 
     ///  The identifier. 
     /// </value> 
     int Id { get; set; } 
    } 
} 

,每次具體的實體的需要實現這個接口:

public class MyEntity : IEntity 
{ 
    #region Implementation of IEntity 

    /// <summary> 
    ///  Gets or sets the identifier. 
    /// </summary> 
    /// <value> 
    ///  The identifier. 
    /// </value> 
    public int Id { get; set; } 

    #endregion 

    // Other POCO properties... 
} 

我覺得這是最好的做法,而不是創建單獨的接口爲每個實體,除非你在高測試環境中工作。實際上,接口只能用於需要抽象級別的地方;主要是當一個以上的具體類將繼承,或者當使用過度熱情的Inversion of Control引擎時。如果你的生產模型中的所有東西都有接口,那麼你的架構可能會有很大的缺陷。無論如何,足夠的散漫。

現在我們已經將所有實體「策略化」了,我們可以使用MEF對它們進行整理並在您的上下文中填充集合。

在你的情況下,添加一個新的屬性:

/// <summary> 
///  Gets a dynamically populated list of DbSets within the context. 
/// </summary> 
/// <value> 
///  A dynamically populated list of DbSets within the context. 
/// </value> 
[ImportMany(typeof(DbSet<IEntity>))] 
public IEnumerable<DbSet<IEntity>> Sets { get; private set; } 

這裏的[ImportMany(typeof(DbSet<IEntity>))],允許MEF來填充集合。

接着,對應Export屬性添加到每個DbSet範圍內:

[Export(typeof(DbSet<IEntity>))] 
public DbSet<MyEntity> MyEntities { get; set; } 

每個Import ED和ED Export屬性已知爲「份」。拼圖的最後一部分是組成這些部分。以下內容添加到您的上下文的構造函數:現在

// Instantiate the Sets list. 
Sets = new List<DbSet<IEntity>>(); 

// Create a new Types catalogue, to hold the exported parts. 
var catalogue = new TypeCatalog(typeof (DbSet<IEntity>)); 

// Create a new Composition Container, to match all the importable and imported parts. 
var container = new CompositionContainer(catalogue); 

// Compose the exported and imported parts for this class. 
container.ComposeParts(this); 

,如果運氣好的話,你應該有DbSets的動態填充的列表,你的範圍內。

我已經使用這種方法,允許通過擴展方法輕鬆截斷所有表。

/// <summary> 
///  Provides extension methods for DbSet objects. 
/// </summary> 
public static class DbSetEx 
{ 
    /// <summary> 
    ///  Truncates the specified set. 
    /// </summary> 
    /// <typeparam name="TEntity">The type of the entity.</typeparam> 
    /// <param name="set">The set.</param> 
    /// <returns>The truncated set.</returns> 
    public static DbSet<TEntity> Truncate<TEntity>(this DbSet<TEntity> set) 
     where TEntity : class, IEntity 
    { 
     set.ToList().ForEach(p => set.Remove(p)); 
     return set; 
    } 
} 

我已經在上下文中添加了一個方法來截斷整個數據庫。

/// <summary> 
///  Truncates the database. 
/// </summary> 
public void TruncateDatabase() 
{ 
    Sets.ToList().ForEach(s => s.Truncate()); 
    SaveChanges(); 
} 

EDIT(大修):

在溶液上方現已折舊。有些微調是必須要做的,以便現在就開始工作。要做到這一點,需要將DbSets導入到類型爲「object」的DbSet的臨時集合中,然後將此集合轉換爲所需接口類型的DbSet。基本目的,IEntity接口就足夠了。

#region Dynamic Table List 

    /// <summary> 
    ///  Gets a dynamically populated list of DbSets within the context. 
    /// </summary> 
    /// <value> 
    ///  A dynamically populated list of DbSets within the context. 
    /// </value> 
    public List<DbSet<IEntity>> Tables { get; private set; } 

    /// <summary> 
    ///  Gets a dynamically populated list of DbSets within the context. 
    /// </summary> 
    /// <value> 
    ///  A dynamically populated list of DbSets within the context. 
    /// </value> 
    [ImportMany("Sets", typeof (DbSet<object>), AllowRecomposition = true)] 
    private List<object> TableObjects { get; set; } 

    /// <summary> 
    ///  Composes the sets list. 
    /// </summary> 
    /// <remarks> 
    ///  To make this work, you need to import the DbSets into a temporary collection of 
    ///  DbSet of type "object", then cast this collection to DbSet of your required 
    ///  interface type. For basic purposes, the IEntity interface will suffice. 
    /// </remarks> 
    private void ComposeSetsList() 
    { 
     // Instantiate the list of tables. 
     Tables = new List<DbSet<IEntity>>(); 

     // Instantiate the MEF Import collection. 
     TableObjects = new List<object>(); 

     // Create a new Types catalogue, to hold the exported parts. 
     var catalogue = new TypeCatalog(typeof (DbSet<object>)); 

     // Create a new Composition Container, to match all the importable and imported parts. 
     var container = new CompositionContainer(catalogue); 

     // Compose the exported and imported parts for this class. 
     container.ComposeParts(this); 

     // Safe cast each DbSet<object> to the public list as DbSet<IEntity>. 
     TableObjects.ForEach(p => Tables.Add(p as DbSet<IEntity>)); 
    } 

    #endregion 

接下來,運行從構造的CompileSetsList()門面(如圖中的Web最佳實踐):

public MvcApplicationContext() 
    { 
     // Enable verification of transactions for ExecuteSQL functions. 
     Configuration.EnsureTransactionsForFunctionsAndCommands = true; 

     // Disable lazy loading. 
     Configuration.LazyLoadingEnabled = false; 

     // Enable tracing of SQL queries. 
     Database.Log = msg => Trace.WriteLine(msg); 

     // Use MEF to compile a list of all sets within the context. 
     ComposeSetsList(); 
    } 

然後,只是裝飾你的DbSet <>就像這樣:

/// <summary> 
    /// Gets or sets the job levels. 
    /// </summary> 
    /// <value> 
    /// The job levels. 
    /// </value> 
    [Export("Sets", typeof(DbSet<object>))] 
    public DbSet<JobLevel> JobLevels { get; set; } 

現在它會正常工作。

+0

非常好的答案。我今年初才瞭解到MEF的全部內容。 – ProfK 2014-09-29 03:36:06