2014-07-18 26 views
7

Ich bin gerade dabei, von v1-3 auf v4 zu aktualisieren, aber ich habe ein paar Probleme bekommen.Datenverarbeitung mit OData v4, EF6 und Web API v2.2

Mein Verständnis ist, dass DateTime nicht unterstützt wird, und ich muss immer DateTimeOffset verwenden. Fein.

Aber bevor ich Sql date Datentyp in der Datetime-Speicherung, jetzt scheint es, ich diesen Fehler:

Member Mapping specified is not valid. The type 'Edm.DateTimeOffset[Nullable=False,DefaultValue=,Precision=]' of member 'CreatedDate' in type 'MyEntity' is not compatible with 'SqlServer.date[Nullable=False,DefaultValue=,Precision=0]' 

Was ist die Arbeit um für das? Ich muss nur Daten in der Datenbank speichern können (Zeit und Ort ist nicht wichtig). Wäre super, wenn ich das Edm.Date sowie einen zurückgegebenen Datentyp bekommen könnte, aber das hatte ich vorher nicht.

Danke.

Edit: Beispielklassen

Vorher:

public class Ticket 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    [Required, MaxLength(50)] 
    public string Reference { get; set; } 

    [Column(TypeName = "date")] 
    public DateTime LoggedDate { get; set; } 
} 

Nach:

public class Ticket 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    [Required, MaxLength(50)] 
    public string Reference { get; set; } 

    [Column(TypeName = "date")] 
    public DateTimeOffset LoggedDate { get; set; } 
} 

Dies ist in EF nicht gültig.

Antwort

2

Für alle, die in Zukunft zu diesem Thema kommen, hat das OData v4-Team dieses Problem behoben.

[Column(TypeName = "date")] 
public DateTime Birthday { get; set; } 

Dies wird nun automatisch zu Edm.Date auflösen.

Wenn Sie wie ich sind und date nach Konvention schreiben, müssen Sie die Eigenschaften manuell als Datum deklarieren, damit sie nicht automatisch als DateTimeOffset aufgelöst werden. In OData können Sie derzeit keine eigenen Konventionen hinzufügen.

customer.Property(c => c.Birthday).AsDate(); 

http://odata.github.io/WebApi/#12-01-DateAndTimeOfDayWithEF

0

Sie können den folgenden Link verwenden, um Ihre DateTimeAndDateTimeOffsetWrapper für die Übersetzung zwischen zwei Typen zu definieren.

http://www.odata.org/blog/how-to-use-sql-spatial-data-with-wcf-odata-spatial/

definieren zwei Eigenschaften auf Ihrem Modell, ist DateTime die im Edm Modell existiert nur, das andere ist DateTimeOffset die nur in der DB vorhanden ist.


Wenn die Lösung über Ihre Anfrage nicht erfüllt, müssen Sie die Daten zu DateTimebevor es und es zu Datenbank Speicher ändern zu DateTimeOffset ändern zurück, nachdem es aus der Datenbank in den Controller-Aktionen abgerufen werden.

Sie können zwei fast gleiche Klassen definieren, um dies zu erreichen. Der einzige Unterschied ist, dass man DateTime Eigentum hat und das andere Eigentum hat.

Die erste wird für EF und Mapping in DB verwendet.

Letzteres wird zum Definieren des OData Edm-Modells und zum Präsentieren für die Benutzer verwendet.

Wie ich oben sagte, müssen Sie die Übersetzung zwischen diesen beiden Klassen vor dem Speichern der Daten und nach dem Abrufen der Daten tun.

+0

Es klingt wie die zweite Option kann das sein, was ich brauche, aber ich würde nicht alle $ Funktionalität erweitern verlieren, da es nicht mehr eine Datenbankeinheit war? – Tim

+0

Auch ich habe Klassen hinzugefügt, um das Problem zu demonstrieren. – Tim

+0

Sie können die Daten aus der Datenbank als OldTicket abrufen und die Daten dann in NewTicket übertragen. Nachdem dies erledigt ist, können Sie die $ expand und andere Abfrageoptionen anwenden. Allerdings wird dieser Teil im Speicher mit linq-to-objects statt linq-to-entities auf altem Weg gemacht. Wie auch immer, es ist eine Arbeit. –

11

Eine Option besteht darin, eine neue Eigenschaft in der Entität zu definieren.Say ist Bezeichnung EF abgebildet:

public partial class Title 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Nullable<System.DateTime> CreatedOn { get; set; } 
} 

dann eine neue Eigenschaft von Datetime hinzufügen:

public partial class Title 
{ 
    [NotMapped] 
    public DateTimeOffset? EdmCreatedOn 
    { 
     // Assume the CreateOn property stores UTC time. 
     get 
     { 
      return CreatedOn.HasValue ? new DateTimeOffset(CreatedOn.Value, TimeSpan.FromHours(0)) : (DateTimeOffset?)null; 
     } 
     set 
     { 
      CreatedOn = value.HasValue ? value.Value.UtcDateTime : (DateTime?)null; 
     } 
    } 
} 

und den Code für generieren OData Modell wie folgt aussieht:

public static IEdmModel GetModel() 
    { 
     ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); 
     EntityTypeConfiguration<Title> titleType= builder.EntityType<Title>(); 
     titleType.Ignore(t => t.CreatedOn); 
     titleType.Property(t => t.EdmCreatedOn).Name = "CreatedOn"; 

     builder.EntitySet<Title>("Titles"); 

     builder.Namespace = typeof(Title).Namespace; 

     return builder.GetEdmModel(); 
    } 
} 

Der Controller wie folgt aussieht :

public class TitlesController : ODataController 
{ 
    CustomerManagementSystemEntities entities = new CustomerManagementSystemEntities(); 

    [EnableQuery(PageSize = 10, MaxExpansionDepth = 5)] 
    public IHttpActionResult Get() 
    { 
     IQueryable<Title> titles = entities.Titles; 
     return Ok(titles); 
    } 

    public IHttpActionResult Post(Title title) 
    { 
     entities.Titles.Add(title); 
     return Created(title); 
    } 
} 
+1

Diese Lösung ist auch die richtige, weil sie die generierten Metadaten nicht ändert, bitte markieren Sie diese Lösung als die richtige – Raffaeu

+3

Eigentlich sollten Sie einen Laufzeitfehler mit diesem Code bekommen. Die Laufzeit gibt Fehler für jede Entitätseigenschaft von Nullable oder DateTime ?. Es spielt keine Rolle, ob Sie die Eigenschaft auf der Konfigurationsebene ODataConventionModelBuilder ignorieren. Zumindest bekomme ich das. –

+0

Der aufgelistete Code funktioniert so lange, bis Sie versuchen, $ auszuwählen. – goroth

0

Sie können die AppendDatetimeOffset-Methode hinzufügen, um die Methoden mithilfe der Microsoft T4-Engine (d. H. Aktualisieren der Vorlagendatei * .tt). Damit müssen Sie beim Generieren des Codes keine Klassen mehr anhängen. Hope this Hilft :)

public string Property(EdmProperty edmProperty) 
    { 
     return string.Format(
      CultureInfo.InvariantCulture, 
      (_ef.IsKey(edmProperty) ? "[Key]" : "") + 
      "{0} {1} {2} {{ {3}get; {4}set; }} {5}",   
      Accessibility.ForProperty(edmProperty), 
      _typeMapper.GetTypeName(edmProperty.TypeUsage), 
      _code.Escape(edmProperty), 
      _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), 
      _code.SpaceAfter(Accessibility.ForSetter(edmProperty)), 
      AppendDateTimeOffset(edmProperty)); 
    } 

    public string AppendDateTimeOffset(EdmProperty edmProperty){ 

    if(!_typeMapper.GetTypeName(edmProperty.TypeUsage).Contains("DateTime")) return " "; 
    //proceed only if date time 
    String paramNull = @"public Nullable<System.DateTimeOffset> edm{0} 
         {{ 
          get 
          {{ 
           return {0}.HasValue ? new DateTimeOffset({0}.Value, TimeSpan.FromHours(0)) : (DateTimeOffset?)null; 
          }} 
         }}"; 

    String paramNotNull = @"public System.DateTimeOffset edm{0} 
         {{ 
          get 
          {{ 
           return new DateTimeOffset({0}, TimeSpan.FromHours(0)); 
          }} 
         }}"; 

     String s= String.Empty; 
     if(edmProperty.Nullable){ 
     s = string.Format(paramNull, edmProperty.Name);  
     }else 
     { 
     s = string.Format(paramNotNull, edmProperty.Name);  
     } 
     return s; 

    }