2009-06-18 58 views
2

好了,短版本是,我有從LinqDataSourceUpdateEventArgs LINQ的實體,我需要處理手動更新,因爲MS是愚蠢的。是否有通過其主鍵獲取Linq2SQL實體的通用方法?

我可以從數據方面的表對象,這讓我,好了,這樣的:

var newObj = e.NewObject; 

var table = FormContext.GetTable(e.NewObject.GetType()); 

table.Attach(newObj, e.OriginalObject); 

if (BuildingObject != null) 
    BuildingObject(sender, new HeirarchicalBuildObjectEventArgs(newObj)); 


FormContext.SubmitChanges(); 

不幸的是,我得到的例外「不能用一個鍵已在使用中添加實體「。

當然,有趣的是我得到的FormContext.SubmitChanges(),而不是table.Attach()...這是沒有意義的我,但不管。

我在想我需要從上下文中真正獲取對象,並附加使用該對象而不是e.OriginalObject ...或者,作爲最後的手段,我需要獲取原始對象並編寫一個循環,將每個屬性的值複製到我從數據上下文中獲得的值中。

無論哪種方式,我需要仰視的對象由它不知道對象的類型主鍵。有沒有辦法做到這一點?

編輯:好吧,看看通過.NET反射器,我注意到,除其他事項外,LinqDataSourceView附加舊數據對象,然後將所有的值複製到它...但顯然跳過關聯。去嘗試過,我想連接舊的對象和複製值...

的真正有趣的部分?我寫了一個函數很久以前從一個實體實例的屬性複製到另一個,它包含此評論:

//我們不能照搬協會,或許不應該

有時候,我希望我的意見是更徹底......

編輯編輯:好了,再一次正確的答案是:我問錯了問題!

正確的代碼是:

 var newObj = e.NewObject; 

     var table = FormContext.GetTable(e.NewObject.GetType()); 

     if (BuildingObject != null) 
      BuildingObject(sender, new HeirarchicalBuildObjectEventArgs(newObj)); 

     table.Attach(newObj, e.OriginalObject); 

     FormContext.SubmitChanges(); 


     e.Cancel = true; 

我本來想BuildingObject後附着,但得到了一些其他錯誤,並移動在努力糾正其附加聲明。 (我想是因爲我打電話附加的錯誤版本或者,也許我的觀點相反...。)

+0

如果你不知道它的類型,你如何查找一個實體? – 2009-06-18 19:27:10

+0

我可以在不知道類型的情況下得到表格。 Linq2SQL的某個地方知道某些東西被標記爲主鍵......我不需要知道,特別是因爲我有一個我可以用來找到它的對象的淺表副本。事實上,像LinqDataSource這樣的東西設法做到了這一點......否則他們將無法執行我想重寫的行爲。 – CodeRedick 2009-06-18 19:50:23

回答

3

我經常使用Sutekishop實現通用資源庫,這是一個使用asp.net mvc和L2S構建的開源電子商務網絡商店。
它有一個泛型類型T,這依賴於模型類的L2S屬性不錯GetByID。這是做這項工作的部分:

public virtual T GetById(int id) 
{ 
    var itemParameter = Expression.Parameter(typeof(T), "item"); 

    var whereExpression = Expression.Lambda<Func<T, bool>> 
     (
     Expression.Equal(
      Expression.Property(
       itemParameter, 
       typeof(T).GetPrimaryKey().Name 
       ), 
      Expression.Constant(id) 
      ), 
     new[] { itemParameter } 
     ); 
    return GetAll().Where(whereExpression).Single(); 
} 

和查找主鍵屬性的擴展方法;正如你所看到的,它期望類屬性上的「IsPrimaryKey」的「Column」屬性。擴展方法:

public static PropertyInfo GetPrimaryKey(this Type entityType) { 
    foreach (PropertyInfo property in entityType.GetProperties()) { 
     if (property.IsPrimaryKey()) { 
      if (property.PropertyType != typeof (int)) { 
       throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int", property.Name, entityType)); 
      } 
      return property; 
     } 
    } 
    throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name)); 
} 

public static TAttribute GetAttributeOf<TAttribute>(this PropertyInfo propertyInfo) { 
    object[] attributes = propertyInfo.GetCustomAttributes(typeof(TAttribute), true); 
    if (attributes.Length == 0) 
     return default(TAttribute); 
    return (TAttribute)attributes[0]; 
} 

public static bool IsPrimaryKey(this PropertyInfo propertyInfo) { 
    var columnAttribute = propertyInfo.GetAttributeOf<ColumnAttribute>(); 
    if (columnAttribute == null) return false; 
    return columnAttribute.IsPrimaryKey; 
} 

此代碼的所有功勞均爲Mike Hadlow!整個實現可以在sutekishop source

2

嘗試類似以下的ID來獲得實體:

TLinqEntity是。由LinqToSql產生......,是一個泛型參數的類本身類的類型

protected TLinqEntity GetByID(object id, DataContext dataContextInstance) 
    { 
     return dataContextInstance.GetTable<TLinqEntity>() 
      .SingleOrDefault(GetIDWhereExpression(id)); 
    } 

    static Expression<Func<TLinqEntity, bool>> GetIDWhereExpression(object id) 
    { 
     var itemParameter = Expression.Parameter(typeof(TLinqEntity), "item"); 
     return Expression.Lambda<Func<TLinqEntity, bool>> 
      (
      Expression.Equal(
       Expression.Property(
        itemParameter, 
        TypeExtensions.GetPrimaryKey(typeof(TLinqEntity)).Name 
        ), 
       Expression.Constant(id) 
       ), 
      new[] { itemParameter } 
      ); 
    } 

    static PropertyInfo GetPrimaryKey(Type entityType) 
    { 
     foreach (PropertyInfo property in entityType.GetProperties()) 
     { 
      var attributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), true); 
      if (attributes.Length == 1) 
      { 
       ColumnAttribute columnAttribute = attributes[0]; 
       if (columnAttribute.IsPrimaryKey) 
       { 
        if (property.PropertyType != typeof(int)) 
        { 
         throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int", 
          property.Name, entityType)); 
        } 
        return property; 
       } 
      } 
     } 
     throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name)); 
    } 

這是更新方法(感謝Marc Gravell):

public virtual void Update(DataContext dataContext, TLinqEntity obj) 
    { 
     // get the row from the database using the meta-model 
     MetaType meta = dataContext.Mapping.GetTable(typeof(TLinqEntity)).RowType; 
     if (meta.IdentityMembers.Count != 1) 
      throw new InvalidOperationException("Composite identity not supported"); 
     string idName = meta.IdentityMembers[0].Member.Name; 
     var id = obj.GetType().GetProperty(idName).GetValue(obj, null); 

     var param = Expression.Parameter(typeof(TLinqEntity), "row"); 
     var lambda = Expression.Lambda<Func<TLinqEntity, bool>>(
      Expression.Equal(
       Expression.PropertyOrField(param, idName), 
       Expression.Constant(id, typeof(int))), param); 

     object dbRow = dataContext.GetTable<TLinqEntity>().Single(lambda); 

     foreach (MetaDataMember member in meta.DataMembers) 
     { 
      // don't copy ID or timstamp/rowversion 
      if (member.IsPrimaryKey || member.IsVersion) continue; 
      // (perhaps exclude associations too) 

      member.MemberAccessor.SetBoxedValue(
       ref dbRow, member.MemberAccessor.GetBoxedValue(obj)); 
     } 
     dataContext.SubmitChanges(); 
    } 
+0

可能會工作,但是我真的寧願不必回過頭來爲每個我們決定使用這種技術的課程添加東西......而且如果我打算這樣做,我只需創建一個需要一個用於獲取主鍵的特定數據成員。 – CodeRedick 2009-06-18 22:35:26

相關問題