Soy un principiante con EntityFramework y WCF, así que todavía no sé cómo funciona todo, así que intentaré explicar mi situación lo mejor que pueda.

Tengo el servicio WCF que usa una base de datos con EntityFramework y he logrado que funcione bien, por ejemplo, así:

using (var entities = new databaseEntities())
{
    // Check if there is 0 rows, then just add the new row.
    int count = entities.Table1.Where(i => i.Name == name).Count();

    if (count < 1)
    {
        var newEntry = new Table1
        {
            Name = name,
            InsertDate = DateTime.Now,
            CreatedBy = createdBy,
            Comment = comment,
            Active = true
        };

        entities.Table1.Add(newEntry);
        entities.SaveChanges();
     }
}

El problema llega cuando tengo más de una tabla y quiero decidir cuál usar. Las tablas son básicamente las mismas y, por lo tanto, usarían las mismas operaciones, por lo que me gustaría usar una función para todas ellas (de esa manera puedo evitar el código duplicado). Pero parece que no puedo entender cómo puedo cambiar la tabla en tiempo de ejecución, por ejemplo, a través de un interruptor / caso.

Por ejemplo:

// A function that gets the type of the table I want to access
void WriteToSomeTable(int type)
{
    switch (type)
    {
        case 0:
            //The table to update is Table1
            break;
        case 1:
            //The table to update is Table2
            break;
     }
}

Si quiero obtener el recuento de todas las entradas con un nombre dado con

int count = entities.Table1.Where(i => i.Name == "somename").Count();

¿Cómo puedo determinar las "entidades.Table1" en tiempo de ejecución? Puedo hacer variables:

System.Data.Entity.DbSet<Table1> firstTable = entities.Table1;
System.Data.Entity.DbSet<Table2> secondTable = entities.Table2;

Entonces pensé que con una lista podría establecer un índice int; a un valor diferente con el interruptor / caso y luego simplemente use

int count = list[index].Where(i => i.Name == "somename").Count();

Pero no puedo agregarlos a una Lista, ya que son diferentes tipos

// entity.Table1 is
System.Data.Entity.DbSet<Table1>
// and entity.Table2 is
System.Data.Entity.DbSet<Table2>

ArrayList tampoco lo cortará, ya que no hay una función ".Where" si trato de usar los objetos dentro de ArrayList.

También probé solo con System.Data.Entity.Dbset, pero para usar la función ".Where", necesitaría usar la función .Cast (), pero no puedo almacenar la "TEntity" necesaria en una variable (o puedo ¿YO?). Por ejemplo:

System.Data.Entity.DbSet firstTable = entity.Table1
Type t = firstTable.GetType();
int count = firstTable.Cast<t>().Where(i => i.Name == "somename").Count();//doesn't work
//This, however works:
int count = firstTable.Cast<Table1>().Where(i => i.Name == "somename").Count();

Espero tener algún sentido sobre cuál es mi problema aquí :) Espero que alguien tenga una idea, cómo resolver esto, ya que he luchado con esto durante años, y la única solución que he encontrado es tener una función separada llame a cada conmutador / caso con el mismo código exacto, a excepción de la parte "entity.Table". Y tener que escribir el mismo conjunto de código varias veces no es una muy buena solución :(

0
Juuseri 25 abr. 2017 a las 14:09

2 respuestas

La mejor respuesta

Hacer función genérica ...

    public void MyAddFunction<T>(T newItem, databaseEntities db, 
           Expression<Func<T, bool>> predicate) where T : class
    {
        var table = db.Set<T>();

        int count = table.Count(predicate);

        if(count < 1)
        {
            table.Add(newItem);
        }

        db.SaveChanges();
    }

Y de acuerdo a sus casos, llame a su función (desea agregar table1 por ejemplo)

using(var entities = new databaseEntities())
{
   MyAddFunction<Table1>(new Table1(), entities , x => x.Name == "name");
}
3
caner 25 abr. 2017 a las 12:38

No lo dice, pero dado que está trabajando con Entity Framework, supongo que su clase databaseEntities es la DbContext que contiene todas sus Entidades como propiedades DbSet<TEntity>.

Escribe que su problema es que conoce el tipo de entidades (en mi ejemplo, TEntity), pero no sabe cómo obtener el DbSet correspondiente. Su propuesta es utilizar una declaración de cambio para esto.

Por suerte esto no es necesario. DbContext.Set (Type) hace esto por usted. Usted proporciona el Tipo, Dbcontext devuelve el DbSet de este tipo.

public class SchoolContext : DbContext
{
    public DbSet<Student> Students {get; set;}
    public DbSet<Teacher> Teachers {get; set;}
    public DbSet<ClassRoom> ClassRooms {get; set;}
    ...
}

Si ya conoce el tipo en tiempo de compilación, use DbContext.Set<TEntity>, si TEntity solo se conoce en tiempo de ejecución, use DbContext.Set (Type entityType) `

Type entityType = ...;
DbSet mySet = DbContext.Set(entityType);

El problema es, por supuesto, que en tiempo de compilación no conoce el entityType y, por lo tanto, no sabe qué funciones puede llamar y qué propiedades tiene su entityType.

Si está seguro de que sus entidades tienen ciertas propiedades, como en su propiedad de ejemplo Name, considere derivar todas sus entidades de una interfaz común. Me gusta esto:

interface ICommonSchoolProperties
{
    public int Id {get;}           // primary key
    public string Name {get;}
}

public class Teacher : ICommonSchoolProperties {...}
public class ClassRoom : ICommonSchoolProperties {...}
etc.

Ahora está seguro de que cada vez que le pide a SchoolContext un artículo de cualquier tipo, está seguro de que los artículos que obtiene tienen al menos un Id y un Name. Y así puedes hacer lo siguiente:

Type entityType = ...
IEnumerable<ICommonSchoolProperties> schoolItems = mySchoolContext
    .Set(entityType)
    .Cast<ICommonSchoolProperties>)();

Y puede llamar a las funciones de las que está seguro que tiene su SchoolProperty.

var nrOfItems = schoolItems
    .Where(schoolItem => schoolItem.Name = ...)
    .Count();
var allKnownNames = schoolItems
    .Select(schoolItem => schoolItem.Name)
    .Distinct();

El problema persiste si desea llamar a funciones que tienen los profesores, pero ClassRooms no.

Este es un caso bastante raro, y si tiene un objeto del que no sabe qué funciones tiene, debe reconsiderar su diseño y pensar en crear funciones que manejen estos objetos, en lugar de dar el tipo, decodificar qué funciones esto objeto tiene y luego usarlos

En lugar de:

private void WriteToSomeTable(Type type)
{
    if type is a teacher
        call teacher functions
    else if type is classroom
        call classroomfunctions
    else

    DoSomeThingcommon(...)
}

Considerar:

public void WriteTeacherTable()
{
    call teacher functions();
    DoSomethingCommon();
}

private void WriteClassRoomtable()
{
     call classroom functions;
     DoSomethingCommon();
}

Tenga en cuenta que el número de líneas apenas aumenta.

En algún lugar dentro de su programa, hay un lugar donde sabe que está tratando con maestros en lugar de ClassRooms. La única razón en un diseño OO adecuado en el que mezclaría Teachers y ClassRooms como si fueran algo similar sería si supiera que solo desea llamar a las funciones que tienen en común. Si ese es el caso, regrese a la función de interfaz donde sabe a qué funciones comunes puede llamar.

1
Harald Coppoolse 25 abr. 2017 a las 12:28