20

Je veux intégrer Entity Framework 6 à notre système, mais j'ai un problème.Entity Framework 6 Code Mappage de la première fonction

  1. Je veux utiliser le code d'abord. Je ne souhaite pas utiliser le fichier .edmx de Database First * pour d'autres raisons.
  2. J'utiliser le mappage d'attributs [Table], [colonne] et cela fonctionne très bien
  3. Database a de nombreuses fonctions définies par l'utilisateur et je dois les utiliser dans LINQ pour interroger des entités.

problème est:

Je ne peux pas la carte fonction via l'attribut comme [Table], [colonne]. Un seul attribut est disponible [DbFunction], ce qui nécessite le fichier * .edmx. Je peux utiliser des fonctions de mappage dans le fichier * .edmx, mais cela signifie que je ne peux pas utiliser le mappage des attributs pour les entités: [Table], [Column]. Le mappage doit être complet dans * .edmx ou dans les attributs.

J'ai essayé de créer DbModel et ajouter la 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 ont exception:

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

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

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 je peux ajouter la fonction dans le modèle? L'interface de la fonction d'ajout est exposée, donc elle devrait être capable de le faire, mais il n'y a pas d'information sur le web pour cette situation. Probablement, quelqu'un sait quand l'équipe d'Entity Framework va implémenter le mappage des 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 seulement. Je, ai aussi besoin carte fonction, qui retourne IQueryable:

IQueryable<T> MyFunction() 

Où T est EntityType ou RowType ou tout autre type. Je ne peux pas faire cela 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-il possible ou non? Probablement les étapes ne sont pas correctes? Probablement le support sera ajouté à EF 6.1. Toute information sera très utile.

Merci!

+0

Vous avez une base de données et souhaitez accéder via Code d'abord? Pas besoin de modèle alors. Voir http://msdn.microsoft.com/en-us/data/jj200620 –

Répondre

3

Vous pouvez obtenir le type de magasin 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 auriez à changer 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

+0

Comment mapper une fonction composable dont le résultat peut être interrogé de n'importe quel type? – Alexey

+0

Je ne suis pas sûr de ce que vous demandez. J'ai utilisé le code ci-dessus pour mapper une fonction de base de données scalaire à une méthode C# pour comparer un champ de concurrence (octet []) et utiliser cette méthode pour composer des requêtes. Peut-être que ma réponse originale aidera: http://stackoverflow.com/a/20225824/2808810 – drew

1

maintenant Entity Framework n'est pas bêta, alors peut-être que vous résolvez d votre problème, mais celui-ci a résolu mon problème How to use scalar-valued function with linq to entity?

+0

La question est d'utiliser la fonction avec l'approche code-first, vous avez lié à une solution avec le fichier .edmx, qui ne l'est pas. – Athari

+0

la chose est qu'il n'y a aucun moyen d'utiliser une fonction scalaire avec cadre d'entité en utilisant l'approche du code, j'essayais de le faire, et finalement utilisé ce qui est suggéré dans le lien – DanielV

+0

Je viens d'essayer l'outil pour framework d'entité 6 et m'a permis d'utiliser l'approche code-premier – DanielV

12

N'a pas essayé encore, mais Entity Framework 6.1 comprend public mapping API. Moozzyk a implémenté Store Functions for EntityFramework CodeFirst en utilisant cette nouvelle fonctionnalité.

Voici ce que le code ressemble à:

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); 
    } 
} 
+0

Puis-je retourner un type différent d'un type de table avec cela? IE: Je veux retourner ObjectResult ? –

+1

@SarahBourt Je n'ai aucune idée. Vous devriez soit poser une question sur SO correctement ou demander directement à l'auteur de la bibliothèque. – Athari

+3

Bonne idée! J'ai trouvé la solution, et l'ai affichée comme une question/réponse ici: http: // stackoverflow.com/questions/29198416/using-ef6-stocker-fonctions-pour-entityframework-codefirst-can-i-return-a-custom-t J'espère que cela aide quelqu'un d'autre –

2

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

Install-Package EntityFramework.CodeFirstStoreFunctions 

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

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

Créer 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

Ajouter dans la méthode OnModelCreating overriden:

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

Et maintenant, vous pouvez appeler/se joindre à une valeur de table fonctionnent 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 
Questions connexes