2009-04-24 8 views
33

Utilisation des commandes LINQ et LINQ To datacontext SQL, Im essayant d'exemple une entité appelée « Produccion » de mon datacontext ainsi:La construction explicite du type d'entité '###' dans la requête n'est pas autorisée.

Demo.View.Data.PRODUCCION pocoProduccion = 
(
    from m in db.MEDICOXPROMOTORs 
    join a in db.ATENCIONs on m.cmp equals a.cmp 
    join e in db.EXAMENXATENCIONs on a.numeroatencion equals e.numeroatencion 
    join c in db.CITAs on e.numerocita equals c.numerocita 
    where e.codigo == codigoExamenxAtencion 
    select new Demo.View.Data.PRODUCCION 
    { 
     cmp = a.cmp, 
     bonificacion = comi, 
     valorventa = precioEstudio, 
     codigoestudio = lblCodigoEstudio.Content.ToString(), 
     codigopaciente = Convert.ToInt32(lblCodigoPaciente.Content.ToString()), 
     codigoproduccion = Convert.ToInt32(lblNroInforme.Content.ToString()), 
     codigopromotor = m.codigopromotor, 
     fecha = Convert.ToDateTime(DateTime.Today.ToShortDateString()), 
     numeroinforme = Convert.ToInt32(lblNroInforme.Content.ToString()), 
     revisado = false, 
     codigozona = (c.codigozona.Value == null ? Convert.ToInt32(c.codigozona) : 0), 
     codigoclinica = Convert.ToInt32(c.codigoclinica), 
     codigoclase = e.codigoclase, 
    } 
).FirstOrDefault(); 

Pendant l'exécution du code ci-dessus, j'obtiens l'erreur suivante que la la trace de la pile est incluse:

System.NotSupportedException was caught 
    Message="The explicit construction of the entity type 'Demo.View.Data.PRODUCCION' in a query is not allowed." 
    Source="System.Data.Linq" 
    StackTrace: 
     en System.Data.Linq.SqlClient.QueryConverter.VisitMemberInit(MemberInitExpression init) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) 
     en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitSelect(Expression sequence, LambdaExpression selector) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) 
     en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitFirst(Expression sequence, LambdaExpression lambda, Boolean isFirst) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc) 
     en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node) 
     en System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node) 
     en System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations) 
     en System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query) 
     en System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression) 
     en System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source) 
     en Demo.View.InformeMedico.realizarProduccionInforme(Int32 codigoExamenxAtencion, Double precioEstudio, Int32 comi) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 602 
     en Demo.View.InformeMedico.UpdateEstadoEstudio(Int32 codigo, Char state) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 591 
     en Demo.View.InformeMedico.btnGuardar_Click(Object sender, RoutedEventArgs e) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 683 
    InnerException: 

Est-ce que cela est désormais autorisé dans LINQ2SQL?

Répondre

20

Les entités peuvent être créées en dehors des requêtes et insérées dans le magasin de données à l'aide d'un DataContext. Vous pouvez ensuite les récupérer en utilisant des requêtes. Cependant, vous ne pouvez pas créer d'entités dans le cadre d'une requête.

+12

des thats signifie plus de lignes de code droite? –

+0

Comment puis-je transmettre les valeurs de sélection de cette requête retournée à une entité, je veux dire ne pas utiliser les membres, tout comme "varReturningQuery as ProductionEntity"? –

9

Je viens de rencontrer le même problème.

J'ai trouvé une solution très facile.

var a = att as Attachment; 

Func<Culture, AttachmentCulture> make = 
    c => new AttachmentCulture { Culture = c }; 

var culs = from c in dc.Cultures 
      let ac = c.AttachmentCultures.SingleOrDefault( 
              x => x.Attachment == a) 
      select ac == null ? make(c) : ac; 

return culs; 
+1

peut être mieux écrit que sélectionnez ac? faire (c); – recursive

+2

Excellente solution - merci! Des idées pour lesquelles cela n'est pas autorisé sans cela? – Whisk

+1

@Whisk - au cas où vous êtes toujours intéressé, j'ai trouvé http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/1ce25da3-44c6-407d-8395-4c146930004b - '... La construction manuelle d'instances d'entités sous forme de projection pollue le cache avec des objets potentiellement mal formés ... ' –

0

Dans le livre "70-515 Applications Web Développement avec Microsoft .NET Framework 4 - kit de formation auto rythme", à la page 638 a l'exemple suivant pour les résultats de sortie à un objet fortement typé:

IEnumerable<User> users = from emp in employees where emp.ID !=0 
    select new User 
    { 
    Name = emp.First + " " + emp.Last, 
    EmployeeId = emp.ID 
    } 

Le conseil de Mark Pecks semble contredire ce livre - cependant, pour moi cet exemple montre toujours l'erreur ci-dessus, me laissant un peu confus. Est-ce lié aux différences de version? Toutes les suggestions sont les bienvenues

+5

L'exemple ci-dessus fonctionne si "Utilisateur" ne fait pas partie du DataContext. – Stone

13

Je trouve cette limitation très ennuyante, et va à l'encontre de la tendance courante de ne pas utiliser SELECT * dans les requêtes. Toujours avec les types anonymes C#, il existe une solution de contournement, en récupérant les objets dans un type anonyme, puis copiez-les dans le bon type.

Par exemple:

var q = from emp in employees where emp.ID !=0 
select new {Name = emp.First + " " + emp.Last, EmployeeId = emp.ID } 
var r = q.ToList(); 
List<User> users = new List<User>(r.Select(new User 
    { 
     Name = r.Name, 
     EmployeeId = r.EmployeeId 
    })); 

Et dans le cas où nous traitons une seule valeur (comme dans la situation décrite dans la question), il est encore plus facile, et nous avons juste besoin de copier directement les valeurs suivantes:

var q = from emp in employees where emp.ID !=0 
select new { Name = emp.First + " " + emp.Last, EmployeeId = emp.ID } 
var r = q.FirstOrDefault(); 
User user = new User { Name = r.Name, EmployeeId = r.ID }; 

Si le nom des propriétés correspondent aux colonnes de base de données, nous pouvons le faire encore plus simple dans la requête, en faisant sélection

var q = from emp in employees where emp.ID !=0 
select new { emp.First, emp.Last, emp.ID } 

On peut aller de l'avant et écrire une expression lambda qui peut copier automatiquement en fonction du nom de la propriété, sans avoir à spécifier explicitement les valeurs.

+0

Je suis d'accord que la limitation est très ennuyante. L'appel à 'ToList' dans votre solution, cependant, mettra les résultats en mémoire, ce qui n'est probablement pas souhaitable pour les gros ensembles de données. –

8

J'ai trouvé que si vous faites un .ToList() sur la requête avant d'essayer de contruct nouveaux objets, il fonctionne

+4

Mais cela perd l'exécution différée. –

7

Voici une autre solution:

  1. Faire une classe qui dérive de votre LINQ à la classe SQL.Je suppose que la classe L2S que vous voulez retourner est Ordre:

    internal class OrderView : Order { } 
    
  2. maintenant écrire la requête de cette façon:

    var query = from o in db.Order 
          select new OrderView // instead of Order 
          { 
           OrderID = o.OrderID, 
           OrderDate = o.OrderDate, 
           // etc. 
          }; 
    
  3. Cast le résultat dans l'ordre, comme ceci:

    return query.Cast<Order>().ToList(); // or .FirstOrDefault() 
    
  4. (ou utiliser quelque chose de plus sensible, comme BLToolkit/DB LINQ)

Note: Je n'ai pas testé pour voir si le suivi fonctionne ou non; cela fonctionne pour récupérer des données, ce dont j'avais besoin.

+1

Brillant! Bien que je n'ai pas besoin de rejeter – irfandar

+0

Je suis d'accord c'est une excellente solution! S'il y a une raison technique pour laquelle je ne devrais pas projeter sur une classe d'entité alors ça ne me dérange pas, mais qui veut redéfinir une nouvelle classe (potentiellement longue) avec exactement la même forme? Personne, c'est qui. Cette solution le résout sans redéfinition explicite. Excellent travail pbz, cloué. –

5

Je construis un type anonyme, utilise IEnumerable (qui préserve l'exécution différée), puis recompile l'objet datacontext. Les deux employés et des gestionnaires sont des objets DataContext:

var q = dc.Employees.Where(p => p.IsManager == 1) 
      .Select(p => new { Id = p.Id, Name = p.Name }) 
      .AsEnumerable()  
      .Select(item => new Manager() { Id = item.Id, Name = item.Name }); 
+0

Est-ce que 'AsEnumerable' ne provoquerait pas le chargement? –

0

j'ai trouvé une autre solution pour le problème qui vous permet même de conserver le résultat que IQueryale, donc il ne s'exécute pas réellement la requête jusqu'à ce que vous voulez qu'il soit exécuté (comme ce serait avec la méthode ToList()).

Alors linq ne vous permet pas de créer une entité dans le cadre d'une requête? Vous pouvez déplacer cette tâche vers la base de données elle-même et créer une fonction qui va récupérer les données que vous voulez. Après avoir importé la fonction dans votre contexte de données, il vous suffit de définir le type de résultat sur celui que vous voulez.


j'ai découvert quand je devais écrire un morceau de code qui produirait un IQueryable<T> dans lequel les éléments n'existent réellement dans le tableau contenant T.

Questions connexes