How to execute RawSql against the context - c#

I currently have a project that I'm working on, which has a database connected to it. In said database I need to query some tables that don't have a relationship. I need to get a specific set of data in order to display it on my user interface. However I need to be able to reference the returned data put it into a list and convert it into json. I have a stored procedure that needs to just be executed against the context because it's retrieving data from many different tables.
I've tried using ExecuteSqlCommand but that doesn't work, because it returns -1 and can't put it into a list.
I've tried using linq to select the columns I want however it's really messy and I cannot retrieve the data as easily.
I've tried using FromSql, however that needs a model to execute against the context which is exactly what I don't want.
public string GetUserSessions(Guid memberId)
{
string sql = $"EXECUTE dbo.GetUserTrackByMemberID #p0";
var session = _context.Database.ExecuteSqlCommand(sql, memberId);
var json = JsonConvert.SerializeObject(session);
return json;
}
This is the ExecuteSqlCommand example, this returns -1 and cannot be put into a list as there will be more than one session.
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.Where(ms => ms.MemberId == memberId).Select(s => new Session() { SessionId =
s.SessionId, EventId = s.Session.EventId, CarCategory = s.Session.CarCategory, AirTemp = s.Session.AirTemp,
TrackTemp = s.Session.TrackTemp, Weather = s.Session.Weather, NumberOfLaps = s.Session.NumberOfLaps, SessionLength = s.Session.SessionLength,
Event = new Event() { EventId = s.Session.Event.EventId, TrackId = s.Session.Event.TrackId, Name = s.Session.Event.Name, NumberOfSessions =
s.Session.Event.NumberOfSessions, DateStart = s.Session.Event.DateStart, DateFinish = s.Session.Event.DateFinish, TyreSet = s.Session.Event.TyreSet,
Track = new Track() { TrackId = s.Session.Event.Track.TrackId, Name = s.Session.Event.Track.Name, Location = s.Session.Event.Track.Location, TrackLength
= s.Session.Event.Track.TrackLength, NumberOfCorners = s.Session.Event.Track.NumberOfCorners} } });
var json = JsonConvert.SerializeObject(session);
return json;
}
This is using Linq, however it's really messy and I feel there's probably a better way to do this, and then when retrieving the data from json it's a lot bigger pain.
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.FromSql($"EXECUTE dbo.GetUserSessionByMemberID {memberId}").ToList();
var json = JsonConvert.SerializeObject(session);
return json;
}
This is the ideal way I would like to do it, however since I'm using the MemberSession model it will only retrieve that data from the stored procedure which is in the MemberSession table, however I want data that is in other tables as well....
public string GetUserSessions(Guid memberId)
{
var session = _context.MemberSession.Where(ms => ms.MemberId == memberId).Include("Session").Include("Event").ToList();
var json = JsonConvert.SerializeObject(session);
return json;
}
I tried this way but because the Event table has no reference / relationship to MemberSession it returns an error.
As I've previously stated in the RawSql example I'm only getting the table data that is in the MemberSession table, no other tables.
There are no error messages.

using (var context = new DBEntities())
{
string query = $"Exec [dbo].[YOUR_SP]";
List<ResponseList> obj = context.Database.SqlQuery<ResponseList>(query).ToList();
string JSONString = JsonConvert.SerializeObject(obj);
}

Related

C#: Cannot implicitly convert type

I am trying to access a stored procedure that retrieves a page using an id.
But I am getting an error:
Error 1 Cannot implicitly convert type
'System.Data.Entity.Core.Objects.ObjectResult<StorePageCMS.Models.mn_StorePage_Select_One_Result>' to 'StorePageCMS.Models.StorePage'
I am not sure how to fix this. The stored precedure that comes from dbEntities from SQL Server, does take an int parameter.
Any help is much appreciated.
public StorePage Get(int StorePageID)
{
using (dbEntities db = new dbEntities())
{
StorePage storepage = db.mn_StorePage_Select_One(StorePageID);
if (storepage == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return storepage;
}
}
UPDATE
I rewrote the method this way:
public List<StorePage> Get(int StorePageID)
{
List<StorePage> storepagelist = new List<StorePage>();
using (dbEntities db = new dbEntities())
{
var results = db.mn_StorePage_Select_One(StorePageID).ToList();
foreach (var result in results)
{
var storepage = new StorePage()
{
StorePageID = result.StorePageID,
SPPreambleID = result.SPPreambleID,
Title = result.Title,
SEOTitle = result.SEOTitle,
ParentStorePageID = result.ParentStorePageID ?? -1,
Meta = result.Meta,
Image = result.Image,
ImageLink = result.ImageLink,
Blurb = result.Blurb,
RegionID = result.RegionID,
Footer = result.Footer
};
storepagelist.Add(storepage);
}
return storepagelist;
}
}
Does this looks more correct?
2 UPDATE
Does this looks correct?
If you're not using the Code First model of Entity Framework
Since StorePageCMS.Models.mn_StorePage_Select_One_Result has no conversion to StorePage, I'm assuming it's a stored procedure result. If that's a stored procedure result (of mn_StorePage_Select_One), you need to map it's result to the StorePage model instead in the EDMX designer.
Here, you'd need to say it returns a collection of StorePageCMS.Models.StorePage Entities.

Insert rows using Linq

Can anyone tell where I make a mistake ? :( I want to insert a row using this. It's just not working. I also tried to use "context.SaveChanges();" but nothing changed. No insert at all, and no exception.
public List<string> Add_Address(string address, int selected_item)
{
List<string> list = new List<string>();
using(var context = new RSS_Reader_Database())
{
Address Address = new Address();
Category_Address Category_Address = new Category_Address();
Address.URL = address.ToString();
int max_id = Convert.ToInt32(context.Addresses.OrderByDescending(t => t.ID_Address).FirstOrDefault());
Category_Address.ID_Address = max_id;
Category_Address.ID_Category = selected_item+1;
var select_query = from t in context.Addresses select t.URL;
foreach (var element in select_query)
{
list.Add(element);
}
}
return list;
}
Edit: Following all Your advices, I made something that works. Looking at this code above, I have no idea what I was trying to do yesterday. Thanks a lot.
public List<string> Add_Address(string address, int selected_item)
{
List<string> list = new List<string>();
using(var context = new RSS_Reader_Database())
{
Address Address = new Address() { URL = address };
context.Addresses.Add(Address);
context.SaveChanges();
int max_id = context.Addresses.Max(u => u.ID_Address);
Category_Address Category_Address = new Category_Address() { ID_Address = max_id, ID_Category = selected_item + 1 };
context.Categories_Addresses.Add(Category_Address);
context.SaveChanges();
var query = from t in context.Addresses
select t.URL;
var data = query.ToList();
foreach (var element in data)
{
list.Add(element);
}
}
return list;
}
Saving with Entity Framework generally works like this. Using your above code as a starting point.
using(var context = new RSS_Reader_Database())
{
Address address = new Address();
// Set address properties
context.Addresses.Add(address);
context.SaveChanges();
}
You need to add the object to the DbSet<T> where T is the type of the entity that is defined on the DbContext. You then need to call SaveChanges() on the context.
I would suggest reading this. It is an easy to follow introduction to Entity Framework.
Not sure exactly what you are trying to do.
But if you are expecting to insert the data by the list.Add(element); command it won't work.
If you are planning to insert data into the same DB, you need to use one property from the context to represent the List collection add a new element on this property.
Something like:
context.Lists.Add(element);
if you want retrieve data, you should not call SaveChanges() !,
try get all values from one query like this:
List<string> select_query = (from t in context.Addresses select t.URL).ToList();

How can I edit or add to a particular field without pull the all object

How I can do just this ( a.myFavorits.Add()) without pulling the all object to var a , because a has a lot of data, and I don't want to pull all a object, but I can't find a way do do it.
I want to do the lambada and the linq without return something but linq is always return something
public static void addFavorits(long f,long idUser)
{
using (var db = dataBase())
{
// here i pull object user from users table
var a = db.users.Where(c => c.id == idUser).SingleOrDefault();
// here i adding to the object field myFavorits new value
//myFavorits is also a table of entitys that connected to user object
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.SaveChanges();
}
}
I thought to do something like this but i dont know how to set the field users_TableId that is the key that connect the 2 tables
public static void addFavorits(long favoritId,long idUser)
{
using (var db = dataBase())
{
db.favoritsUsersLong.Add(new BE.FavoritsUsersLong {myLong = favoritId}
/*,users_TableId =idUser*/);
db.SaveChanges();
}
}
Here's a concrete example that does what you want. In this example, only the Name of a Company is modified and saved. Or an item is added to one of its collections.
var cmp = new Company{ CmpId = 1, Name = "Cmp1" }; // CmpId is the primary key
db.Companies.Attach(cmp);
db.Entry(cmp).Property(c => c.Name).IsModified = true;
// Or add an entity to a collection:
cmp.Users = new[] {new User { Name = "a1", PassWord = "a1" } };
try
{
db.Configuration.ValidateOnSaveEnabled = false;
db.SaveChanges();
}
finally
{
db.Configuration.ValidateOnSaveEnabled = true;
}
Result in SQL:
DECLARE #0 VarChar(30) = 'Cmp1'
DECLARE #1 Int = 1
UPDATE [dbo].[Company]
SET [Name] = #0
WHERE ([CmpId] = #1)
There are a few things to note here:
Obviously you need to know the Id of the entity you want to modify.
The object you create is called a stub entity, which is an incomplete entity. When you try to save such an entity, EF is very likely to complain about null values in required properties. That's why almost certain you'd have to disable validation (temporarily, or, better, dispose the context immediately).
If you want to add an item to a collection, you should leave validation enabled, because you'd want to know for sure that the new entity is valid. So you shouldn't mix these two ways to use a stub entity.
If you often need roughly the same small part of your entity you may consider table splitting.
I'm guessing this is what you want? I don't see you 'editting' I only see you adding.
using (var db = dataBase())
{
var a = new user();
....
//set properties etc..
...
a.myFavorits.Add(new BE.FavoritsUsersLong { myLong = f });
db.users.Add(a);
db.SaveChanges();
}

Query MongoDB Using 'ObjectId'

I have inserted documents into MongoDB without an id. And I want to retrieve them by searching through their MongoDB ObjectId, that has been assigned in default.
Here is my attempt-
var query_id = Query.EQ("_id", "50ed4e7d5baffd13a44d0153");
var entity = dbCollection.FindOne(query_id);
return entity.ToString();
And I get following error-
A first chance exception of type 'System.NullReferenceException' occurred
What is the problem?
You need to create an instance of ObjectId and then query using that instance, otherwise your query compares ObjectIds to string and fails to find matching documents.
This should work:
var query_id = Query.EQ("_id", ObjectId.Parse("50ed4e7d5baffd13a44d0153"));
var entity = dbCollection.FindOne(query_id);
return entity.ToString();
In C# for latest official MongoDB.Driver write this-
var filter_id = Builders<MODEL_NAME>.Filter.Eq("id", ObjectId.Parse("50ed4e7d5baffd13a44d0153"));
var entity = dbCollection.Find(filter).FirstOrDefault();
return entity.ToString();
We can accomplish the same result without converting id from string to ObjectId. But then, we will have to add [BsonRepresentation(BsonType.ObjectId)] before id attribute in the model class.
The code can even be further simplified using lambda expression-
var entity = dbCollection.Find(document => document.id == "50ed4e7d5baffd13a44d0153").FirstOrDefault();
return entity.ToString();
If you're here in 2018 and want copy/paste code that still works or pure string syntax;
[Fact]
public async Task QueryUsingObjectId()
{
var filter = Builders<CosmosParkingFactory>.Filter.Eq("_id", new ObjectId("5b57516fd16cb04bfc35fcc6"));
var entity = stocksCollection.Find(filter);
var stock = await entity.SingleOrDefaultAsync();
Assert.NotNull(stock);
var idString = "5b57516fd16cb04bfc35fcc6";
var stringFilter = "{ _id: ObjectId('" + idString + "') }";
var entityStringFiltered = stocksCollection.Find(stringFilter);
var stockStringFiltered = await entityStringFiltered.SingleOrDefaultAsync();
Assert.NotNull(stockStringFiltered);
}
The selected answer is correct. For anyone confused by the Query.EQ, here is another way to write a basic update (updates the entire mongodb document):
string mongoDocumentID = "123455666767778";
var query = new QueryDocument("_id", ObjectId.Parse(mongoDocumentID));
var update = new UpdateDocument { { "$set", documentToSave } };
mongoCollection.Update(query, update, UpdateFlags.Multi);
The ObjectId object is needed when you want to actually search by object ID, otherwise it is comparing string to objectid type, and it won't match. Mongo is very type-strict in this way, regardless if the field name is correct.
You can also do it this way, its
public static ObjectId GetInternalId(string id)
{
if (!ObjectId.TryParse(id, out ObjectId internalId))
internalId = ObjectId.Empty;
return internalId;
}
then in your method you can do something like this
ObjectId internalId = GetMongoId.GetInternalId(id);
return await YourContext.YourTable.Find(c => c.InternalId == internalId).FirstOrDefaultAsync();
Note: id param in GetInternalId its a parameter on that method. In case you need as this:
public async Task<YourTable> Find(string id)
{
ObjectId internalId = GetMongoId.GetInternalId(id);
return await YourContext.YourTable.Find(c => c.InternalId == internalId).FirstOrDefaultAsync();
}
Hope it helps also.

How to work around NotMapped properties in queries?

I have method that looks like this:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = db.Organizations.Select(org => new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
SiteCount = org.Sites.Count(),
DbSecureFileCount = 0,
DbFileCount = 0
});
return results;
}
This is returns results pretty promptly.
However, you'll notice the OrganizationViewModel has to properties which are getting set with "0". There are properties in the Organization model which I added via a partial class and decorated with [NotMapped]: UnsecureFileCount and SecureFileCount.
If I change those 0s to something useful...
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount
... I get the "Only initializers, entity members, and entity navigation properties are supported" exception. I find this a little confusing because I don't feel I'm asking the database about them, I'm only setting properties of the view model.
However, since EF isn't listening to my argument I tried a different approach:
private static IEnumerable<OrganizationViewModel> GetOrganizations()
{
var db = new GroveDbContext();
var results = new List<OrganizationViewModel>();
foreach (var org in db.Organizations)
{
results.Add(new OrganizationViewModel
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
});
}
return results;
}
Technically this gives me the correct results without an exception but it takes forever. (By "forever" I mean more than 60 seconds whereas the first version delivers results in under a second.)
Is there a way to optimize the second approach? Or is there a way to get the first approach to work?
Another option would be to load the values back as an anonymous type and the loop through those to load your viewmodel (n+1 is most likely the reason for the slowness).
For example:
var results = db.Organizations.Select(org => new
{
Id = org.OrgID,
Name = org.OrgName,
DbSecureFileCount = org.SecureFileCount,
DbFileCount = org.UnsecureFileCount,
SiteCount = org.Sites.Count()
}).ToList();
var viewmodels = results.Select( x=> new OrganizationViewModel
{
Id = x.Id,
Name = x.Name,
DbSecureFileCount = x.DbSecureFileCount,
DbFileCount = x.DbFileCount,
SiteCount = x.SiteCount
});
Sorry about the formatting; I'm typing on a phone.
You are basically lazy loading each object at each iteration of the loop, causing n+1 queries.
What you should do is bring in the entire collection into memory, and use it from there.
Sample code:
var organizationList = db.Organizations.Load();
foreach (var org in organizationList.Local)
{
//Here you are free to do whatever you want
}

Categories

Resources