7

Sono nel bel mezzo dell'aggiornamento dalla v1-3 alla v4, ma ho incontrato alcuni problemi.Gestione delle date con ODATA v4, EF6 e Web API v2.2

La mia comprensione è che DateTime non è supportato e devo sempre utilizzare DateTimeOffset. Belle.

Ma prima che io la memorizzazione di tipo di dati SQL date nel DateTime, ora sembra ottengo questo errore:

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]' 

Qual è il lavoro in giro per questo? Devo essere in grado di memorizzare in modo specifico solo le date nel database (il tempo e la località non sono importanti). Sarebbe bello se potessi ottenere l'Edm.Date come un tipo di dati restituito, ma non l'avevo prima.

Grazie.

Edit: Esempio classi

Prima:

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

Dopo:

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

Questo non è valido per EF.

risposta

2

per chi viene a questo in futuro, il team v4 OData hanno risolto questo problema.

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

Questo ora verrà risolto automaticamente in Edm.Date.

Se si è come me e si sta eseguendo il tipo date per convenzione, è necessario dichiarare manualmente le proprietà come date affinché non vengano risolte automaticamente come DateTimeOffset. OData al momento non ti permette di aggiungere le tue convenzioni.

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

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

0

È possibile fare riferimento al collegamento sottostante per definire il proprio DateTimeAndDateTimeOffsetWrapper per eseguire la traduzione tra due tipi.

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

definire due proprietà del modello, uno è DateTime che esiste solo nel modello Edm, l'altro è DateTimeOffset che esiste solo nel DB.


Se la soluzione di cui sopra non soddisfare la vostra richiesta, è necessario modificare i dati per DateTimeprima di salvarlo nel database e cambiare di nuovo a DateTimeOffset dopo il recupero dalla base di dati nelle azioni di controllo.

È possibile definire due classi quasi identiche per ottenere ciò. L'unica differenza è che uno ha la proprietà DateTime e l'altra ha la proprietà DateTimeOffset.

Il primo è utilizzato per EF e il mapping in DB.

Quest'ultimo viene utilizzato per definire il modello OData Edm e presentare agli utenti.

Come detto sopra, è necessario eseguire la traduzione tra queste due classi prima di salvare i dati e dopo aver recuperato i dati.

+0

suona come la seconda opzione può essere quello che mi serve, ma sarebbe non perdo tutto $ espandere le funzionalità in quanto non era più un'entità database? – Tim

+0

Inoltre ho aggiunto le classi per dimostrare il problema. – Tim

+0

È possibile recuperare i dati dal DB come OldTicket e quindi tradurre i dati in NewTicket. Al termine, è possibile applicare $ espandi e altre opzioni di query. Tuttavia questa parte verrà eseguita in memoria con linq-to-objects, piuttosto che con linq-to-entity nel vecchio modo. Comunque è un lavoro in giro. –

11

Un'opzione consiste nel definire una nuova proprietà nell'entità.Dire titolo viene mappato EF:

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

quindi aggiungere una nuova proprietà di DateTimeOffset:

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

e il codice per generare OData Modello assomiglia:

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

Il controller si presenta come :

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

Questa soluzione è quella corretta anche perché non cambia i metadati generati, contrassegnare questa soluzione come quella corretta – Raffaeu

+3

In realtà si dovrebbe ottenere un errore di runtime con questo codice. Il runtime darà errore per qualsiasi proprietà di entità di Nullable o DateTime ?. Non importa se ignori la proprietà al livello di configurazione ODataConventionModelBuilder. Almeno questo è quello che ottengo. –

+0

Il codice elencato funziona fino a quando non provi a selezionare $. – goroth

0

È possibile aggiungere il metodo AppendDatetimeOffset per aggiungere automaticamente i metodi utilizzando il motore T4 microsoft (ad es. aggiornamento del file modello * .tt). In modo che quando si rigenera il codice, non è necessario aggiungere nuovamente le classi. Spero che questo aiuti :)

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; 

    }