c# entity-framework ef-code-first entity-framework-6 sql-function

c# - Entity Framework 6 Code First function mapping



entity-framework ef-code-first (4)

Aquí están todos los pasos necesarios [Probado]:

Install-Package EntityFramework.CodeFirstStoreFunctions

Declara una clase para el resultado de salida:

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

Crea un método en tu clase 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); }

Añadir

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

a tu clase DbContext

Agregue el método OnModelCreating :

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

Y ahora puedes llamar / unirte con una función de valores de tabla como esta:

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

Quiero integrar Entity Framework 6 a nuestro sistema, pero tengo un problema.

  1. Quiero usar el código primero. No quiero usar el archivo Database First * .edmx por otras razones.
  2. Uso la asignación de atributos [Tabla], [Columna] y esto funciona bien
  3. La base de datos tiene muchas funciones definidas por el usuario y necesito usarlas en la consulta de Linq To Entities.

El problema es:

No puedo asignar la función a través de un atributo como [Tabla], [Columna]. Solo 1 atributo está disponible [DbFunction], que requiere el archivo * .edmx.

Estoy bien para tener funciones de asignación en el archivo * .edmx, pero significa que no puedo usar la asignación de atributos para las Entidades: [Tabla], [Columna]. El mapeo debe estar completo en * .edmx o en los atributos.

Intenté crear DbModel y agregar una función a través de este código:

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

Pero tienen excepción:

Se detectaron uno o más errores de validación durante la generación del modelo:

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

El problema está en la variable "edmType". No puedo crear correctamente ReturnType para la función. ¿Alguien puede sugerir cómo puedo agregar una función al modelo? La interfaz de la función de adición está expuesta, por lo que debería poder hacerlo, pero no hay información en la web para esta situación. Probablemente, alguien sepa cuándo el equipo de Entity Framework implementará la asignación de atributos para funciones como Line To Sql.

Versión EF: 6.0.0-beta1-20521

¡Gracias!

Sí, esto funciona para mí. Pero solo para funciones escalares. También necesito la función de mapa, que devuelve IQueryable:

IQueryable<T> MyFunction()

Donde T es EntityType o RowType o cualquier tipo. No puedo hacer esto en absoluto (la versión EF es 6.0.2-21211). Creo que esto debería funcionar de esta manera:

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

Pero todavía no hay suerte:

model.Compile(); // ERROR

¿Es posible o no? Probablemente los pasos no están bien? Probablemente se agregará soporte en EF 6.1. Cualquier información será muy útil.

¡Gracias!


No he intentado esto todavía, pero Entity Framework 6.1 incluye una API de mapeo pública . Moozzyk ha implementado Funciones de tienda para EntityFramework CodeFirst usando esta nueva funcionalidad.

Así es como se ve el código:

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


Puede obtener el tipo de Tienda del tipo primitivo con un método auxiliar:

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

En su ejemplo, tendría que cambiar el tipo de parámetro de retorno:

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


Encontré la ayuda que necesitaba aquí: http://entityframework.codeplex.com/discussions/466706