How to diagnose slow Entity Framework stored procedure call? - asp.net

Problem: I'm calling a stored procedure through EF Core. When I run the stored procedure directly (via 'debug procedure'), it runs quickly, but it runs VERY slowly when called by EF's FromSqlRaw. So the problem appears to be when converting the returned data-table to a list of objects.
Setup:
Simple application with a list of blog posts. The stored procedure gets a hierarchical list of posts and associated users from a TPH table of posts, plus a table of users.
// Code is simplified, actually 8 parameters
SqlParameter depth_p = new SqlParameter("#depth", depth);
SqlParameter authorizedUserID_p = new SqlParameter("#authorizedUserID", authorizedUser.ID);
IQueryable<PostUser> query = postContext.PostUsers
.FromSqlRaw("Post.USP_ReadDebate #depth, #authorizedUserID",
parameters: new[] { depth_p, authorizedUserID_p });
List<PostUser> postUsers = query.ToList(); // This hangs.
26 columns are returned and converted by EF into the PostUser class.
PostUser holds 26 "ordinary" properties. No navigation properties, custom classes or any getters or setters that do any work.
public class PostUser
{
// Post fields
public Int32? ID { get; set; } // Primary Key
public String Text { get; set; }
public Guid OwnerID { get; set; }
public int? ParentID { get; set; } // nullable
public bool IsDisabled { get; set; }
public DateTime TimeCreated { get; set; }
public bool User_IsBanned { get; set; } = false;
// some others...
public PostUser() { }
}
Note: the stored procedure is very complex. It calls another stored procedure which fills a #spid table, then inserts the contents of that #SPID table into a table variable and returns that.
But again when debugged directly it returns quickly, so I think the problem is when EF Core is converting the returned data to the PostUser object.
Bottom Line: is there any way to get visibility into what EF Core is doing on the conversion to PostUser to find the problem?
Thank you!

Related

SQLite.net database with Xamarin.Forms App

I have a problem with an SQLite database in my Xamarin.Forms PCL project.
I have followed this example from Microsoft Docs:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/databases
I've been using my own types to store data and it's worked Ok for simple custom types, but I've recently added List<int> and Attendance type to the custom object (Info).
Now when I try and create the object, i get the following errors:
Don't know about System.Collections.Generic.List`1[System.Int32]
Don't know about MyApp.Attendance
Here is the init code:
readonly SQLiteAsyncConnection database;
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<UserPrefrences>().Wait();
database.CreateTableAsync<Attendance>().Wait();
database.CreateTableAsync<Info>().Wait();
I'm using Xamarin.Forms with Xamarin.iOS.
You can not store them by default like that. However there is sqlite-net-extensions which you can use to accomplish that. You can take a look about sqlite-net-extensions here.
Using this extension you will be able to do that with TextBlob property, something like this:
public class Address
{
public string StreetName { get; set; }
public string Number { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
public class Person
{
public string Name { get; set; }
[TextBlob("PhonesBlobbed")]
public List<string> PhoneNumbers { get; set; }
[TextBlob("AddressesBlobbed")]
public List<Address> Addresses { get; set; }
public string PhonesBlobbed { get; set; } // serialized phone numbers
public string AddressesBlobbed { get; set; } // serialized addresses
}
More explanation about TextBlob from url.
Text blobbed properties Text-blobbed properties are serialized into a text property when saved and deserialized when loaded. This allows
storing simple objects in the same table in a single column.
Text-blobbed properties have a small overhead of serializing and
deserializing the objects and some limitations, but are the best way
to store simple objects like List or Dictionary of basic types or
simple relationships.
Text-blobbed properties require a declared string property where the
serialized object is stored.
I just saw that there is also similar/same questions about this topic on StackOverflow already, so you can take a look at them also.
How can you store lists of objects in SQLite.net?
Can I use a List of String in a class intended for SQLite?

EF Core custom results from stored procedure

I'm using EF Core which I believe is also known as EF 7? Anyways, I have a stored procedure that returns custom results that will not identify with any specific table. How am I supposed to access those results and how should I call the sql command?
Normally we have to use .FromSql but that is only available on entities, eg. _context.User.FromSql(). I don't have an entity for it.
So I tried building a dbset/entity for the results, but again, there is no associated table, and there is also no "Key". How am I supposed to parse the data then of the custom results?
You can create a fake entity for the result of your stored procedure. You can set any property as the Key, even if in the results of the stored procedure the key values are not unique.
For example if you have a table like the following :
CREATE TABLE [dbo].[banana_hoard]
(
[id] INT NOT NULL PRIMARY KEY IDENTITY (1,1),
[owner] NVARCHAR(64) NOT NULL,
[bananas] BIGINT NOT NULL
)
You can have a query that does not return the row id like this :
public class Program
{
public static void Main(string[] args)
{
using (var db = new MonkeyDbContext())
{
var sp_results = db.search.FromSql(#"execute <YOUR_STORED_PROC>");
str_result = String.Join("\n", sp_results.Select(a => JsonConvert.SerializeObject(a) ));
Console.WriteLine("stored proc result :\n" + str_result);
}
}
}
public class MonkeyDbContext : DbContext
{
public DbSet<StoredProcRow> search { get; set; }
protected override void OnConfiguring (DbContextOptionsBuilder builder)
{
builder.UseSqlServer(#"Server=(localdb)\monkey_db;Database=monkey_db;Trusted_Connection=True;");
}
}
public class StoredProcRow
{
[Key]
public string Owner { get; set; }
public long Bananas { get; set; }
}

The member with identity does not exist in the metadata collection. Parameter name: identity

We are using EF Code First 4.3.1.
We are developing an ASP.NET Web Role referring to multiple class libraries.
There are two class libraries each containing classes and an individual DBcontext.
Lets say the Library1 has classes A and B.
DBcon1: DbSet and DbSet
Lets say the Library2 has classes C and D.
Class C{
[Key]
public int CId{ get; set;}
[Required]
public virtual A referencedA {get; set;}
}
DBcon2: DbSet<C> and DbSet<D>
When I try to use the DBcon2 as such:
using (var con = new DBcon2())
{
C vr = new C();
vr.CId= 1;
vr.referencedA = DBCon1.As.First();
con.Cs.Add(vr);
con.SaveChanges();
}
I get an exception as:
"The member with identity does not exist in the metadata collection.
Parameter name: identity"
Both DBCon1 and DBcon2 are using the sane SQL Server Database "SampleDB".
Please point me in the right direction.
I got this error and fixed it by not trying to set the navigation property in the related table, just set the foreign key id instead
eg
public class Student()
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
}
public class Course()
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Main code:
var myCourse = new Course();
var myCourseId = 1;
var student = new Student() {
CourseId = myCourseId
// Course = myCourse <-- this would cause the error
}
Might not be your issue but maybe it will point you in the right direction and hopefully will help someone else.
The exception is a bit cryptic, but pretty clear if you realise that a context needs information about entities (metadata) to be able to write sql statements. Thus, your DBcon2 context has no clue where to find the primary key of an A, because it has no metadata about A.
You could however set an integer property A_Id (or the like), but then you'll have to write custom code to resolve it to an A.
Another option is to merge (parts of) the contexts, if possible.

Many to one configuration using EF 4.1 code first

I have the following classes:
public class CartItem
{
public long Id { get; set; }
public int Quantity { get; set; }
public Product Product { get; set; }
}
public class Product {
public long Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
}
I currently have the following configuration:
modelBuilder.Entity<CartItem>().HasRequired(x => x.Product).WithMany().Map(x => x.MapKey("ProductId"));
I am trying to ensure that whenever I retrieve a cartitem from the database there will be a join on the product table so I can access the product properties but not the other way around.
I basically want to be able to do:
string title = cartItem.Product.Title
using the configuration I have gives me an Object reference not set to an instance of an object exception.
Short answer: to solve your problem, make the Product property virtual.
In-depth:
First, you don't need a join to do this. EF works fine with lazy loading (you need the virtual modifier)
Second, you can fetch the Product eagerly, using the Include extension method. Example:
var cartItem = context.CartItems.Include(x => x.Product)
.Where(/*some condition*/).ToList();
...but you can't configure this to be the default behavior (nor is it a good idea usually)
Third, this is a many-to-one relationship, not one-to-one (a Product has many related CartItems)

Deserializing a PHP json encoded (json_encode) string with ASP.NET webservices

I am really struggling deserializing a PHP json encoded string in ASP.NET.
I am using nusoap and CakePHP 1.3 on the PHP side and mvc.net 4.0 on the web service side and everything was working well. However, I couldn’t figure out how to pass a complex array as one parameter of the webservice, so I had the idea of serializing it as json and passing a simple string. So far so good...
But I cannot for the life of me de-serialize the json_encoded string in ASP.NET [well, I’ve been trying for the last two hours at least ;)]
Here is what I have so far:
The PHP sends an array of products (product id as a GUID - sent as a string then converted on the web service side) and the number of products:
$args['products'] = json_encode($booking['Booking']['prs_products']);
This is received ok by the webservice as the following json string (products):
[{"BookingProducts":{"id":"2884f556-67ed-4eb8-98ca-a99dc27a2665","quantity":2}},{"BookingProducts":{"id":"f57854ba-0a9b-400b-bea0-bafdcb179b01","quantity":2}},{"BookingProducts":{"id":"7ff81128-c33c-4e6c-a33c-3ca40ccfb5d0","quantity":2}}]
I then try and populate a BookingProducts List<>. The BookingProducts class is as follows:
public class BookingProducts
{
public String id { get; set; }
public int quantity { get; set; }
public BookingProducts()
{
}
public BookingProducts(string id, int quantity)
{
this.id = id;
this.quantity = quantity;
}
}
I have tried both the [System.Web.Script.Serialization][2] and Newtonsoft.Json libraries as follows, but without success:
List<BookingProducts> productsList = new List<BookingProducts>();
try
{
productsList = JsonConvert.DeserializeObject<List<BookingProducts>>((products));
}
catch (Newtonsoft.Json.JsonSerializationException)
{
productsList = new JavaScriptSerializer().Deserialize<List<BookingProducts>>(products);
}
In both cases I get a list of empty products (or a serialization exception).
Hopefully someone has done this before, or can spot an obvious mistake!
What you really have here is a list of objects containing BookingProducts object. In order to deserialize it, you need to have something like this for your entity:
public class BookingProductsWrapper
{
public class BookingProductsInner
{
public string id { get; set; }
public int quantity { get; set; }
}
public BookingProductsInner BookingProducts { get; set; }
}
Now you can deserialize it using JavaScriptSerializer (for example):
System.Web.Script.Serialization.JavaScriptSerializer jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
List<BookingProductsWrapper> productsList = jsSerializer.Deserialize<List<BookingProductsWrapper>>(products);
That will do the trick.

Resources