Simple contract for use with FromSql() - entity-framework-core

With its recent improvements, I'm looking to move from Dapper back to EF (Core).
The majority of our code currently uses the standard patterns of mapping entities to tables, however we'd also like to be able to make simple ad-hoc queries that map to a simple POCO.
For example, say I have a SQL statement which returns a result set of strings. I created a class as follows...
public class SimpleStringDTO
{
public string Result { get; set; }
}
.. and called it as such.
public DbSet<SimpleStringDTO> SingleStringResults { get; set; }
public IQueryable<SimpleStringDTO> Names()
{
var sql = $"select name [result] from names";
var result = this.SingleStringResults.FromSql(sql);
return result;
}
My thoughts are that I could use the same DBSet and POCO for other simple queries to other tables.
When I execute it, EF throws an error "The entity type 'SimpleStringDTO' requires a primary key to be defined.".
Do I really need to define another field as a PK? There'll be cases where there isn't a PK defined. I just want something simple and flexible. Ideally, I'd rather not define a DBSet or POCO at all, just return the results straight to an IEnumerable<string>.
Can someone please point me towards best practises here?

While I wait for EF Core 2.1 I've ended up adding a fake key to my model
[Key]
public Guid Id { get; set; }
and then returning a fake Guid from SQL.
var sql = $"select newid(), name [result] from names";

Related

Using REST to try and get Field Service Detail - InventoryID = SQL error: Multi-part identifier not found

I am getting SQL errors when trying to use REST to get to FSAppointmentDet.InventoryID, either as a Field Service service item or as an Inventory Item.
The InventoryID field exists in the table, however, it looks like the DACs have been inherited, for example as FSAppointmentDetService.
Other fields work, it just seems that the fields with an ID are causing the SQL error.
In this case, the SQL error is a multi-step identifier not found. Running a SQL Profiler trace and looking at the SQL, it looks like the table has been aliased in one part of the query and not in another. Obviously this is occurring at a level much lower than we can get to, so looking for a workaround or ideas on how to get the InventoryID for Field Service detail records.
I've seen this happen when one DAC herits (herits as in class inheritance not extend as in DAC extension) from another DAC without redeclaring it's key fields. The way to fix that is to add the parent keys abstract class fields in the children.
FSAppointmentDetService seems to be missing AppointmentID key declaration. When the ORM builds the SQL query it generates Alias for the herited DAC but it gets confused becaused the key fields of the parent were not all re-declared in the child.
In FSAppointmentDet you have 2 key fields:
#region AppointmentID
public abstract class appointmentID : PX.Data.IBqlField
{
}
[PXDBInt(IsKey = true)]
[PXParent(typeof(Select<FSAppointment, Where<FSAppointment.appointmentID, Equal<Current<FSAppointmentDet.appointmentID>>>>))]
[PXDBLiteDefault(typeof(FSAppointment.appointmentID))]
[PXUIField(DisplayName = "Appointment Nbr.")]
public virtual int? AppointmentID { get; set; }
#endregion
#region AppDetID
public abstract class appDetID : PX.Data.IBqlField
{
}
[PXDBIdentity(IsKey = true)]
public virtual int? AppDetID { get; set; }
#endregion
But in FSAppointmentDetService only one of them is redeclared. Notice how it's using 'override' to redeclare compared to FSAppointmentDet which do not override:
#region AppDetID
public new abstract class appDetID : PX.Data.IBqlField
{
}
[PXDBIdentity(IsKey = true)]
public override int? AppDetID { get; set; }
#endregion
In this case we can't add field to that DAC though because it's part of the base product. I think it would be possible to create a new DAC that herits from FSAppointmentDetService, add the missing key in there and use that new herited DAC instead of FSAppointmentDetService.
However I don't know if that would be possible when working with Web Services. If not the change will have to be made in Acumatica base product. You could fill a bug report with Acumatica support to have that done in future versions.

create linked entities in EF based on automatically generated id

Challenge in EF6:
how to check Id of resulting row in the database after running this (esentially adding an entity record):
repository.Add(myEntity1);
...and use that id to add the second entity which has property X = to the id of the first entity?
use that id to add the second entity which has property X = to the id of the first entity?
repository.Add(myEntity2);
Right now there is no linkage between entity 1 and entity 2 because i don;t know how to save the id (automatically generated by ef) after first add
... and preserve it for adding it as a fk in the second entity?
Thanks a lot
You could try this following after your call to SaveChanges:
myEntity2.X = myEntity1.Id;
Then call SaveChanges again. This doesn't really utilise the power of Entity Framework, however, which is in managing relationships between entities. If your class was defined something like this:
public class MyEntity
{
[Key]
public int Id { get; set; }
[ForeignKey(nameof(RelatedEntity))]
public int RelatedEntityId { get; set; }
public MyEntity RelatedEntity { get; set; }
}
You could add your entities something like the following, and the Id/foreign key matching would be handled for you after calling SaveChanges:
myEntity1.RelatedEntity = myEntity2;
This is a fairly general solution, so if you'd like something more specific then you will need to include more details in your question.
You can read more about configuring Entity Framework relationships here.

Code First creates int instead of enum

I want to create a enum type column named 'type' but when I reverse engineer my Code First generated DB it assigns it as an 'int'.
Here is my Enum class:
[Flags]
public enum TypeNames
{
Een = 0,
Twee = 1,
Drie = 2
}
Here is my Grounds class to create the table with the 'TypeNames'-enum. The Properties class is another table (Grounds - Properties have a TPT inheritance).
[Table("gronden")]
public partial class Grounds : Properties
{
[Column("opp")]
public double? Surface { get; set; }
[EnumDataType(typeof(TypeNames)), Column("type")]
public TypeNames Types { get; set; }
}
Any ideas of what I am missing here to get an enum-type into my DB?
According to the following answer, it appears that EnumDataTypeAttribute is only implemented for ASP.NET UI components, and not for EF usage.
Should the EnumDataTypeAttribute work correctly in .NET 4.0 using Entity Framework?
EF Core 2.1 implements a new feature that allows Enums to be stored as strings in the database. This allows data to be self-describing, which can be really helpful for long-term maintainability. For your specific case, you could simply do:
[Table("gronden")]
public partial class Grounds : Properties
{
[Column("opp")]
public double? Surface { get; set; }
[Column("type", TypeName = "nvarchar(24)")]
public TypeNames Types { get; set; }
}
You can find more detail on this page:
https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions
Also, I noticed that you have the FlagsAttribute set on your enum. Are you hoping to be able to apply multiple enum values to a single entity? This should work fine when values are persisted as an int, but will not work if storing as a MySQL ENUM or string datatype. MySQL does support a SET datatype, but it seems unlikely that EF would add support for this feature, since most other databases don't have a similar concept.
https://dev.mysql.com/doc/refman/5.6/en/constraint-enum.html
If you do indeed want to allow multiple enum values to be applied to each entity (similar to the way tags are used on Stack Overflow), you might consider creating a many-to-many relationship instead. Basically, this would mean converting the TypeNames enum into a Types table in the database, and allowing EF to generate a GroundTypes table to link them together. Here is a tutorial:
http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx

Support for Table Valued Functions in EF6 Code First?

Is it possible to call a TVF in EF6 Code First?
I started a new project using EF6 Database first and EF was able to import a TVF into the model and call it just fine.
But updating the model became very time consuming and problematic with the large read-only db with no RI that I'm stuck dealing with.
So I tried to convert to EF6 code first using the Power Tools Reverse Engineering tool to generate a context and model classes.
Unfortunately the Reverse Engineering tool didn't import the TVFs.
Next I tried to copy the DBFunctions from my old Database First DbContext to the new Code First DbContext, but that gave me an error that my TVF:
"cannot be resolved into a valid type or function".
Is it possible to create a code first Fluent mapping for TVFs?
If not, is there a work-around?
I guess I could use SPs instead of TVFs, but was hoping I could use mostly TVFs to deal with the problematic DB I'm stuck with.
Thanks for any work-around ideas
This is now possible. I created a custom model convention which allows using store functions in CodeFirst in EF6.1. The convention is available on NuGet http://www.nuget.org/packages/EntityFramework.CodeFirstStoreFunctions. Here is the link to the blogpost containing all the details: http://blog.3d-logic.com/2014/04/09/support-for-store-functions-tvfs-and-stored-procs-in-entity-framework-6-1/
[Tested]
using:
Install-Package EntityFramework.CodeFirstStoreFunctions
Declare a class for output result:
public class MyCustomObject
{
[Key]
public int Id { get; set; }
public int Rank { get; set; }
}
Create a method in your DbContext class
[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);
}
Add
public DbSet<MyCustomObject> SearchResults { get; set; }
to your DbContext class
Add in the overriden OnModelCreating method:
modelBuilder.Conventions.Add(new FunctionsConvention<MyContextType>("dbo"));
And now you can call/join with
a table values function like this:
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
I was able to access TVF with the code below. This works in EF6. The model property names have to match the database column names.
List<MyModel> data =
db.Database.SqlQuery<MyModel>(
"select * from dbo.my_function(#p1, #p2, #p3)",
new SqlParameter("#p1", new System.DateTime(2015,1,1)),
new SqlParameter("#p2", new System.DateTime(2015, 8, 1)),
new SqlParameter("#p3", 12))
.ToList();
I actually started looking into it in EF6.1 and have something that is working on nightly builds. Check this and this out.
I have developed a library for this functionality. You can review my article on
UserTableFunctionCodeFirst.
You can use your function without writing SQL query.
Update
First of all you have to add reference to the above mentioned library and then you have to create parameter class for your function. This class can contain any number and type of parameter
public class TestFunctionParams
{
[CodeFunctionAttributes.FunctionOrder(1)]
[CodeFunctionAttributes.Name("id")]
[CodeFunctionAttributes.ParameterType(System.Data.SqlDbType.Int)]
public int Id { get; set; }
}
Now you have to add following property in your DbContext to call function and map to the property.
[CodeFunctionAttributes.Schema("dbo")] // This is optional as it is set as dbo as default if not provided.
[CodeFunctionAttributes.Name("ufn_MyFunction")] // Name of function in database.
[CodeFunctionAttributes.ReturnTypes(typeof(Customer))]
public TableValueFunction<TestFunctionParams> CustomerFunction { get; set; }
Then you can call your function as below.
using (var db = new DataContext())
{
var funcParams = new TestFunctionParams() { Id = 1 };
var entity = db.CustomerFunction.ExecuteFunction(funcParams).ToList<Customer>();
}
This will call your user defined function and map to the entity.

Entity Framework 5 table-per-type update, change sub type but keep same base type

I have a simple hierarchy
public abstract class CommunicationSupport
{
public SupportTypeEnum Type { get; set; }
public Country Origin { get; set; } // National or Foreign support
}
public class TelecomSupport : CommunicationSupport
{
public string Number { get; set; }
}
public class PostalSupport : CommunicationSupport
{
public Address Address { get; set; }
}
I plan to use the Table-per-type hierarchy for my DB. So 3 tables will be created, one base and two child using the same PK as the base.
My problem is that I want to be able to update a CommunicationSupport by changing it's type.
Let's say that I create a TelecomSupport, save it and then change it's type to a PostalSupport and save it again (update). The result I expect is for EF to keep the same base record (CommunicationSupport Id) but delete the record in the TelecomSupport table and create a new one in the PostalSupport.
So TelecomSupport and PostalSupport are exclusive and cannot share the same base CommunicationSupport.
How can I do that using EntityFramework 5?
Thanks for your help!
I don't have a good answer, but I can think of four "solutions" that are really workarounds:
Don't use DBMS-computed values for your primary keys (if you already use natural keys, it's fine).
Use DBMS-computed surrogate keys.
Follow something like the state pattern.
Do some evil voodoo with the object state manager.
Update: There seems to be a popular consensus that trying isn't even worth it; most people thus simply use stored procedures instead to work around the problem.
Changing Inherited Types in Entity Framework
Entity Framework: Inheritance, change object type
Changing the type of an (Entity Framework) entity that is part of an inheritance hierarchy
Changing the type of an entity that is part of an inheritance hierarchy
Using natural keys
First, remember that the objects tracked by the EF are part of your DAL, not your domain model (regardless of whether you use POCOs or not). Some people don't need a domain model, but keep it in mind, as we can now think of these objects as representations of table records we manipulate in ways we wouldn't with domain objects.
Here, we use IDbSet.Remove to delete the records of the entity, then add new ones with the same primary key using IDbSet.Add, all in a single transaction. See the ChangeType method in the sample code below.
In theory, integrity is OK, and in theory, EF could detect what you're trying to do and optimize things. In practice, it currently doesn't (I profiled the SQL interface to verify this). The result is that it looks ugly (DELETE+INSERT instead of UPDATE), so if system beauty and performance are issues, it's probably a no-go. If you can take it, it's relatively straightforward.
Here is some sample code I used to test this (if you want to experiment, simply create a new console application, add a reference to the EntityFramework assembly, and paste the code).
A is the base class, X and Y are subclasses. We consider Id to be a natural key, so we can copy it in the subclasses copy constructors (here only implemented for Y). The code creates a database and seeds it with a record of type X. Then, it runs and changes its type to Y, obviously losing X-specific data in the process. The copy constructor is where you would transform data, or archive it if data loss is not part of the business process. The only piece of "interesting" code is the ChangeType method, the rest is boilerplate.
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
namespace EntitySubTypeChange {
abstract class A {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Foo { get; set; }
public override string ToString() {
return string.Format("Type:\t{0}{3}Id:\t{1}{3}Foo:\t{2}{3}",
this.GetType(), Id, Foo, Environment.NewLine);
}
}
[Table("X")]
class X : A {
public string Bar { get; set; }
public override string ToString() {
return string.Format("{0}Bar:\t{1}{2}", base.ToString(), Bar, Environment.NewLine);
}
}
[Table("Y")]
class Y : A {
public Y() {}
public Y(A a) {
this.Id = a.Id;
this.Foo = a.Foo;
}
public string Baz { get; set; }
public override string ToString() {
return string.Format("{0}Baz:\t{1}{2}", base.ToString(), Baz, Environment.NewLine);
}
}
class Program {
static void Main(string[] args) {
Display();
ChangeType();
Display();
}
static void Display() {
using (var context = new Container())
Console.WriteLine(context.A.First());
Console.ReadKey();
}
static void ChangeType()
{
using (var context = new Container()) {
context.A.Add(new Y(context.A.Remove(context.X.First())));
context.SaveChanges();
}
}
class Container : DbContext {
public IDbSet<A> A { get; set; }
public IDbSet<X> X { get; set; }
public IDbSet<Y> Y { get; set; }
}
static Program() {
Database.SetInitializer<Container>(new ContainerInitializer());
}
class ContainerInitializer : DropCreateDatabaseAlways<Container> {
protected override void Seed(Container context) {
context.A.Add(new X { Foo = "Base Value", Bar = "SubType X Value" });
context.SaveChanges();
}
}
}
}
Output:
Type: EntitySubTypeChange.X
Id: 0
Foo: Base Value
Bar: SubType X Value
Type: EntitySubTypeChange.Y
Id: 0
Foo: Base Value
Baz:
Note: If you want an auto-generated natural key, you can't let EF ask the DBMS to compute it, or EF will prevent you from manipulating it the way you want (see below). In effect, EF treats all keys with computed values as surrogate keys, even though it still happily leaks them (the bad of both worlds).
Note: I annotate the subclasses with Table because you mentioned a TPT setup, but the problem is not actually related to TPT.
Using surrogate keys
If you consider a surrogate key to be truly internal, then it doesn't matter if it changes under your nose as long as you can still access your data the same way (using a secondary index for example).
Note: In practice, many people leak surrogate keys all around (domain model, service interface, ...). Don't do it.
If you take the previous sample, simply remove the DatabaseGenerated attribute and the assignment of the Id in the copy constructor of the subtypes.
Note: With its value generated by the DBMS, the Id property is completely ignored by EF and doesn't serve any real purpose other than being analyzed by the model builder to generate the Id column in the SQL schema. That and being leaked by bad programmers.
Output:
Type: EntitySubTypeChange.X
Id: 1
Foo: Base Value
Bar: SubType X Value
Type: EntitySubTypeChange.Y
Id: 2
Foo: Base Value
Baz:
Using the state pattern (or similar)
This solution is probably what most people would consider the "proper solution", since you can't change the intrinsic type of an object in most object-oriented languages. This is the case for CTS-compliant languages, which includes C#.
The problem is that this pattern is properly used in a domain model, not in a DAL like one implemented with EF. I'm not saying it's impossible, you may be able to hack things up with complex types or TPH constructs to avoid the creation of an intermediary table, but most likely you'll be swimming up the river until you give up. Hopefully someone can prove me wrong though.
Note: You can decide that you want your relational model to look different, in which case you may bypass this problem altogether. It wouldn't be an answer to your question though.
Using internal EF voodoo
I've rather quickly looked around the reference documentation for DbContext, ObjectContext and ObjectStateManager, and I can't immediately find any way to change the type of an entity. If you have better luck than me, you may be able to use DTOs and DbPropertyValues to do your conversion.
Important note
With the first two workarounds, you'll likely hit a bunch of problems with navigational properties and foreign keys (because of the DELETE+INSERT operation). This would be a separate question.
Conclusion
EF is not that flexible when you do anything non-trivial, but it keeps improving. Hopefully this answer won't be relevant in the future. It's also possible that I'm not aware of an existing killer-feature that would make what you want possible, so don't make any decisions based on this answer.