web-dev-qa-db-fra.com

Mappage de fonction Entité Framework 6 Code First

Je veux intégrer Entity Framework 6 à notre système, mais j'ai un problème.

  1. Je veux utiliser Code First. Je ne souhaite pas utiliser le fichier Database First * .edmx pour d'autres raisons.
  2. J'utilise le mappage d'attributs [Table], [Column] et cela fonctionne bien
  3. La base de données a de nombreuses fonctions définies par l'utilisateur et je dois les utiliser dans la requête Linq To Entities.

Le problème est:

Je ne peux pas mapper la fonction via un attribut comme [Table], [Colonne]. Un seul attribut est disponible [DbFunction], ce qui nécessite le fichier * .edmx.

Je suis d'accord pour avoir le mappage de fonctions dans le fichier * .edmx, mais cela signifie que je ne peux pas utiliser le mappage d'attributs pour les entités: [Table], [Colonne]. Le mappage doit être complet en * .edmx ou en attributs.

J'ai essayé de créer DbModel et d'ajouter une fonction via ce code:

public static class Functions
{
    [DbFunction("CodeFirstNamespace", "TestEntity")]
    public static string TestEntity()
    {
        throw new NotSupportedException();
    }
}


public class MyContext : DbContext, IDataAccess
{
    protected MyContext (string connectionString)
        : base(connectionString, CreateModel())
    {
    }

    private static DbCompiledModel CreateModel()
    {
        var dbModelBuilder = new DbModelBuilder(DbModelBuilderVersion.Latest);
        dbModelBuilder.Entity<Warehouse>();
        var dbModel = dbModelBuilder.Build(new DbProviderInfo("System.Data.SqlClient", "2008"));

        var edmType = PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String);
        var payload =
            new EdmFunctionPayload
            {
                Schema = "dbo",
                ParameterTypeSemantics = ParameterTypeSemantics.AllowImplicitConversion,
                IsComposable = true,
                IsNiladic = false,
                IsBuiltIn = false,
                IsAggregate = false,
                IsFromProviderManifest = true,
                StoreFunctionName = "TestEntity",
                ReturnParameters =
                    new[]
                    {
                        FunctionParameter.Create("ReturnType", edmType, ParameterMode.ReturnValue)
                    }
            };

        var function = EdmFunction.Create("TestEntity", "CodeFirst", DataSpace.CSpace, payload, null);
        dbModel.DatabaseMapping.Model.AddItem(function);
        var compiledModel = dbModel.Compile();       // Error happens here
        return compiledModel;
    }
}

Mais faites exception:

Une ou plusieurs erreurs de validation ont été détectées lors de la génération du modèle:

Edm.String: : The namespace 'String' is a system namespace and cannot be used by other schemas. Choose another namespace name.

Le problème est dans la variable "edmType". Je ne peux pas créer correctement ReturnType pour la fonction. Quelqu'un peut-il suggérer comment ajouter une fonction au modèle? L'interface d'ajout de fonction est exposée, donc elle devrait pouvoir le faire, mais il n'y a aucune information sur le Web pour cette situation. Probablement, quelqu'un sait quand l'équipe Entity Framework va implémenter le mappage d'attributs pour des fonctions comme Line To Sql.

Version EF: 6.0.0-beta1-20521

Merci!


Oui, cela fonctionne pour moi. Mais pour les fonctions scalaires uniquement. J'ai également besoin de la fonction de carte, qui renvoie IQueryable:

 IQueryable<T> MyFunction()

Où T est EntityType ou RowType ou tout type. Je ne peux pas faire ça du tout (la version EF est 6.0.2-21211). Je pense que cela devrait fonctionner de cette façon:

private static void RegisterEdmFunctions(DbModel model)
{
    var storeModel = model.GetStoreModel();
    var functionReturnValueType = storeModel.EntityTypes.Single(arg => arg.Name == "MyEntity").GetCollectionType();
    var payload =
        new EdmFunctionPayload
        {
            IsComposable = true,
            Schema = "dbo",
            StoreFunctionName = "MyFunctionName",
            ReturnParameters =
                new[]
                { 
                    FunctionParameter.Create("ReturnValue", functionReturnValueType, ParameterMode.ReturnValue)
                },
            Parameters =
                new[]
                {
                    FunctionParameter.Create("MyFunctionInputParameter", PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32), ParameterMode.In)
                }
        };
    storeModel.AddItem(EdmFunction.Create(
        payload.StoreFunctionName,
        "MyFunctionsNamespace",
        DataSpace.SSpace,
        payload,
        payload.Parameters.Select(arg => MetadataProperty.Create(arg.Name, arg.TypeUsage, null)).ToArray()));
}

Mais toujours pas de chance:

  model.Compile();  // ERROR 

Est-ce possible ou non? Les étapes ne sont probablement pas bonnes? Le support sera probablement ajouté à EF 6.1. Toute information sera très utile.

Merci!

26
Alexey

Je n'ai pas encore essayé cela, mais Entity Framework 6.1 inclut API de mappage public . Moozzyk a implémenté Store Functions for EntityFramework CodeFirst en utilisant cette nouvelle fonctionnalité.

Voici à quoi ressemble le code:

public class MyContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new FunctionsConvention<MyContext>("dbo"));
    }

    [DbFunction("MyContext", "CustomersByZipCode")]
    public IQueryable<Customer> CustomersByZipCode(string zipCode)
    {
        var zipCodeParameter = zipCode != null ?
            new ObjectParameter("ZipCode", zipCode) :
            new ObjectParameter("ZipCode", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext
            .CreateQuery<Customer>(
                string.Format("[{0}].{1}", GetType().Name, 
                    "[CustomersByZipCode](@ZipCode)"), zipCodeParameter);
    }

    public ObjectResult<Customer> GetCustomersByName(string name)
    {
        var nameParameter = name != null ?
            new ObjectParameter("Name", name) :
            new ObjectParameter("Name", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext.
            ExecuteFunction("GetCustomersByName", nameParameter);
    }
}
15
Athari

Voici toutes les étapes nécessaires [Testé]:

Install-Package EntityFramework.CodeFirstStoreFunctions

Déclarez une classe pour le résultat de sortie:

public class MyCustomObject
{
   [Key]
   public int Id { get; set; }
   public int Rank { get; set; }
}

Créez une méthode dans votre classe DbContext

[DbFunction("MyContextType", "SearchSomething")]
public virtual IQueryable<MyCustomObject> SearchSomething(string keywords)
{
   var keywordsParam = new ObjectParameter("keywords", typeof(string)) 
                           { 
                              Value = keywords 
                            };
    return (this as IObjectContextAdapter).ObjectContext
    .CreateQuery<MyCustomObject>(
     "MyContextType.SearchSomething(@keywords)", keywordsParam);
}

Ajouter

public DbSet<MyCustomObject> SearchResults { get; set; }

à votre classe DbContext

Ajoutez la méthode OnModelCreating remplacée:

modelBuilder.Conventions
.Add(new CodeFirstStoreFunctions.FunctionsConvention<MyContextType>("dbo"));

Et maintenant, vous pouvez appeler/joindre avec une fonction de valeurs de table comme ceci:

CREATE FUNCTION SearchSomething
(   
    @keywords nvarchar(4000)
)
RETURNS TABLE 
AS
RETURN 
(SELECT KEY_TBL.RANK AS Rank, Id
FROM MyTable 
LEFT JOIN freetexttable(MyTable , ([MyColumn1],[MyColumn2]), @keywords) AS KEY_TBL      
ON MyTable.Id = KEY_TBL.[KEY]  
WHERE KEY_TBL.RANK > 0   
)
GO
3
Nina

Vous pouvez obtenir le type Store à partir du type primitif avec une méthode d'assistance:

    public static EdmType GetStorePrimitiveType(DbModel model, PrimitiveTypeKind typeKind)
    {
        return model.ProviderManifest.GetStoreType(TypeUsage.CreateDefaultTypeUsage(
            PrimitiveType.GetEdmPrimitiveType(typeKind))).EdmType;
    }

Dans votre exemple, vous devrez modifier le type du paramètre de retour:

var edmType = GetStorePrimitiveType(model, PrimitiveTypeKind.String);


J'ai trouvé l'aide dont j'avais besoin ici: http://entityframework.codeplex.com/discussions/466706

3
drew

maintenant Entity Framework n'est pas bêta, alors peut-être que vous avez résolu votre problème, mais celui-ci a résolu mon problème Comment utiliser la fonction à valeur scalaire avec linq pour l'entité?

1
DanielV