2009-06-18 5 views
2

Ok, die kurze Version ist, dass ich eine Linq-Entität von einem LinqDataSourceUpdateEventArgs habe und ich muss das Update manuell behandeln, weil MS dumm war.Gibt es eine generische Möglichkeit, eine Linq2SQL-Entität durch ihren Primärschlüssel zu erhalten?

kann ich die Tabelle Objekt aus dem Datenkontext erhalten, die mir erlaubt, na ja, dies:

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(); 

Leider bekomme ich die Ausnahme „Kann ein Unternehmen mit einem Schlüssel nicht hinzufügen, die bereits in Gebrauch ist . "

Natürlich ist der lustige Teil, ich bekomme das auf FormContext.SubmitChanges(), nicht auf table.Attach() ... was für mich keinen Sinn macht, aber was auch immer.

Ich denke, ich muss tatsächlich das Objekt aus dem Kontext zu bekommen, und anhängen, dass anstelle von e.OriginalObject ... ODER, als letztes Mittel, muss ich das ursprüngliche Objekt und schreiben eine Schleife, die kopiert den Wert jeder Eigenschaft in den, den ich aus dem Datenkontext bekomme.

So oder so, ich muss ein Objekt nach seinem Primärschlüssel suchen, ohne den Typ des Objekts zu kennen. Gibt es eine Möglichkeit, das zu tun?

EDIT: Ok, habe einen Blick durch .NET Reflector und ich merke, dass unter anderem LinqDataSourceView das OLD-Datenobjekt anhängt und kopiert dann alle Werte hinein ... aber das überspringt offenbar die Assoziationen. Ich werde versuchen, das alte Objekt anzuhängen und Werte zu kopieren, denke ich ...

Der wirklich lustige Teil? Ich schrieb eine Funktion Eigenschaften von einer Einheit Instanz zu einem anderen vor langer Zeit zu kopieren, und es enthält diesen Kommentar:

// Wir können Verbände nicht kopieren, und wahrscheinlich sollte nicht

Manchmal wünsche ich meine Kommentare waren gründlicher ...

EDIT BEARBEITEN: Ok, also noch einmal die richtige Antwort ist: Ich habe die falsche Frage gestellt!

Der richtige Code ist:

 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; 

ich ursprünglich nach BuildingObject zu befestigen versuchte, bekam aber einen anderen Fehler und zog die Aussage in dem Bemühen, bringen Sie ihn zu korrigieren. (Ich glaube, weil ich die falsche Version anhängen Aufruf wurde Vielleicht hatte ich die Argumente umgekehrt ....)

+0

Wie können Sie eine Entität nachschlagen, wenn Sie ihren Typ nicht kennen? –

+0

Ich kann den Tisch bekommen, ohne den Typ zu kennen. Irgendwo dort Linq2SQL weiß, dass etwas als Primärschlüssel markiert ist ... Ich sollte nicht wissen müssen, vor allem, weil ich eine flache Kopie des Objekts habe, das ich verwenden könnte, um es zu finden. In der Tat schaffen Dinge wie LinqDataSource das irgendwie ... sonst wären sie nicht in der Lage, das Verhalten, das ich neu schreiben will, auszuführen. – CodeRedick

Antwort

3

Ich verwende oft die Implementierung von generischen Repository von Sutekishop, Open-Source-E-Commerce-Webshop mit ASP.net mvc und L2S gebaut.
Es hat schöne GetByID für generische Typ T, die auf L2S-Attribute auf Modellklassen beruht.Dies ist der Teil, der die Aufgabe erfüllt:

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(); 
} 

und Erweiterungsmethode, die nach Primärschlüsseleigenschaft sucht; Wie Sie sehen können, erwartet es das Attribut "Column" mit "IsPrimaryKey" für die Klasseneigenschaft. Erweiterungsmethoden:

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; 
} 

Alle Gutschriften für diesen Code geht zu Mike Hadlow! Die gesamte Implementierung finden Sie in sutekishop source

+0

die Antwort, weil es am ähnlichsten aussieht, was funktionieren würde, für die ursprüngliche Frage, hatte ich es umgesetzt, bevor ich mein wirkliches Problem erkannte. Technisch hätte ich nicht die ID, aber ich bin mir ziemlich sicher, dass ich diese Funktionen nutzen könnte ... – CodeRedick

+0

nun ja, ich habe deine Frage nicht ganz verstanden, also habe ich mich auf den Titel konzentriert. Wenn Hoffnung, zumindest, würde dieser Code eines Tages helfen! –

+0

Das ist eigentlich sehr falsch; Sie sollten sich hier nicht auf Attribute verlassen - POCO wird ebenfalls unterstützt. Vielleicht möchten Sie sich http://stackoverflow.com/questions/880090/c-linq-sql-an-updatebyid-method-for-the-repository-pattern ansehen –

2

Probieren Sie etwas wie die nach dem Entity von ID zu erhalten:

(Wo TLinqEntity ist die . Art der Klasse, die von LinqToSql erzeugt wird ... und ist ein allgemeiner Parameter in der Klasse selbst)

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)); 
    } 

Dies ist die Update-Methode (dank Marc Gravell):

+0

Könnte funktionieren, aber ich würde wirklich lieber nicht zurückgehen und anfangen, Dinge zu jeder Klasse hinzuzufügen, entscheiden wir uns, diese Technik zu verwenden ... und wirklich, wenn ich das machen würde, würde ich einfach ein Interface erstellen, das benötigt ein spezifisches Datenelement zum Erhalten des Primärschlüssels. – CodeRedick

+0

Es funktioniert, weil ich es für jedes Projekt verwende, das ich bei der Implementierung von Linq2Sql –

+0

nach diesem Kommentar verwende "// (vielleicht auch Verbindungen ausschließen)" - ja, Assoziationen sollten auch ausgeschlossen werden, weil es Ihre aktuellen Werte ändern wird. –

Verwandte Themen