So I'm trying to store a record in the databse using dapper. I'm passing an object to the method where I have my query to store the recorde. Let me be more clear. Below is my model :
public class Foo
{
public long FooId { get; set; }
public Guid Foo2ID { get; set; }
public string Status { get; set; }
public Person Person { get; set; } = new Person();
}
public class Person
{
public string Type { get; set; }
public string Character { get; set; }
public DateTime Test { get; set; }
}
And this is my query :
public async Task<ActionResult> Create(Foo f)
{
using (var connection = _dbAccess.CreateConnection())
{
var sqlStatement = #"
INSERT INTO ReportRequests
(FooId
,Foo2Id
,Person
,Status)
VALUES
(#FooId
#,Foo2Id
#,Person
#,Status)";
await connection.ExecuteAsync(sqlStatement, f);
};
return Ok();
}
I'm trying to save a json in the Person column in the database. But I get this error :
The member x of type x cannot be used as a parameter value
Can anyone please give me an idea on how I can approach to this problem. It would be very helpful.
Thank you a lot :)
enter code hereFirst of all, you should consider whether you can use LINQ-like queries with dapper. It makes it both more readable and avoids having issues like that.
Back to your problem, from the code you posted it looks like you've misplaced the comas after the # symbol #,Foo2Id :
(#FooId
#,Foo2Id
#,Person
#,Status)
It should be:
(#FooId
#Foo2Id,
#Person,
#Status)
Related
TLDR - The error is:
The query has been configured to use 'QuerySplittingBehavior.SplitQuery' and contains a collection in the 'Select' call, which could not be split into separate query. Please remove 'AsSplitQuery' if applied or add 'AsSingleQuery' to the query.
I am developing a backend with EntityFrameworkCore in C#.
My table classes are like this:
public class MainTable : BasicAggregateRoot<int>
{
public MainTable()
{
this.Operations = new HashSet<OperationTable>();
}
public long? RecId { get; set; }
public int FormStatus { get; set; }
public virtual ICollection<OperationTable> Operations { get; set; }
}
public class OperationTable : BasicAggregateRoot<int>
{
public OperationTable()
{
this.Works = new HashSet<Work>(); //Not important things
this.Materials = new HashSet<Material>(); //Not important things
}
public string ServiceType { get; set; }
}
And my DTOs are like this:
public class MainDto : EntityDto<int>
{
public long? RecId { get; set; }
public int FormStatus { get; set; }
public List<OperationDto> Operations { get; set; }
}
public class OperationDto
{
public string ServiceType { get; set; }
}
I created maps this way:
CreateMap<MainTable, MainDto>().ReverseMap();
CreateMap<OperationTable, OperationDto>().ReverseMap();
When I commit the mapping by:
class Service{
IRepository<MainTable, int> _mainTableRepository;
Service(IRepository<MainTable, int> mainTableRepository){
_mainTableRepository = mainTableRepository;
}
List<MainDto> All()
{
var result = mainTableRepository.Include(p => p.Operations)
.ProjectTo<MainDto>(ObjectMapper.GetMapper().ConfigurationProvider) //Here is the problem.
.ToList();
return result;
}
}
I get the error on the top.
When I get rid of the List from mainDto, error does not occur, but I don't have the result that I want either.
What might be the problem? I couldn't find an answer.
In that source you can find the differences between single and split query: https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries
The problem is (I guess) IRepository.Include uses split query by default. But (again I guess) AutoMapper is not configured to use split query, it works with single queries.
We need to change the query type before mapping like this:
var result = mainTableRepository.Include(p => p.Operations)
.AsSingleQuery() //This solved the problem
.ProjectTo<MainDto>(ObjectMapper.GetMapper().ConfigurationProvider)
.ToList();
I have an application that has two similar but different objects and I want to store those objects in the same collection. What is the best way to do this? And how can I query this collection?
Today my collections is represented by:
public IMongoCollection<Post> Posts
{
get
{
return _database.GetCollection<Post>("posts");
}
}
And I have this class:
public class Post
{
public string Id { get; set; }
public string Message { get; set; }
}
public class NewTypePost
{
public string Id { get; set; }
public string Image { get; set; }
}
So, today I just can save and query using Post class. Now I want to store and retrive the both classes, Post and NewTypePost.
I tried to change the class type from Post to dynamic. But when I did this, I could not query the collections.
MongoDB .NET driver offers few possibilites in such cases:
Polymorphism
You can build a hierarchy of classes and MongoDB driver will be able to determine a type of an object it gets retrieved from the database:
[BsonKnownTypes(typeof(Post), typeof(NewTypePost))]
public abstract class PostBase
{
[BsonId]
public string Id { get; set; }
}
public class Post: PostBase
{
public string Message { get; set; }
}
public class NewTypePost: PostBase
{
public string Image { get; set; }
}
MongoDB driver will create additional field _t in every document which will represent corresponding class.
Single Class
You can still have Post class and use BsonIgnoreIfNull attribute to avoid serialization exception. MongoDB .NET driver will set those properties to null if they don't exist in your database.
public class Post
{
[BsonId]
public string Id { get; set; }
[BsonIgnoreIfNull]
public string Message { get; set; }
[BsonIgnoreIfNull]
public string Image { get; set; }
}
BsonDocument
You can also drop strongly-typed approach and use BsonDocument class which is dynamic dictionary-like structure that represents your Mongo documents
var collection = db.GetCollection<BsonDocument>("posts");
More details here
dynamic
Specifying dynamic as generic parameter of ICollection you should get a list of ExpandoObject that will hold all the values you have in your database.
var collection = db.GetCollection<dynamic>("posts");
var data = collection.Find(Builders<dynamic>.Filter.Empty).ToList();
var firstMessage = data[0].Message; // dynamically typed code
Suppose I have the next conn to a test database:
var mongoClient = new MongoClient(new MongoClientSettings
{
Server = new MongoServerAddress("localhost"),
});
var database = mongoClient.GetDatabase("TestDb");
Then I can do something like:
var col = database.GetCollection<Post>("posts");
var col2 = database.GetCollection<NewTypePost>("posts");
To get two different instances of IMongoCollection but pointing to the same collection in the database. Further I am able to save to each collection in the usual way:
col.InsertOne(new Post { Message = "m1" });
col2.InsertOne(new NewTypePost { Image = "im1" });
Then, I'm also able to query from those collection base on the specific fields:
var p1= col.Find(Builders<Post>.Filter.Eq(x=>x.Message, "m1")).FirstOrDefault();
var p2 =col2.Find(Builders<NewTypePost>.Filter.Eq(x=>x.Image, "im1")).FirstOrDefault();
Console.WriteLine(p1?.Message); // m1
Console.WriteLine(p2?.Image); // im1
I don't know if that's what you want but it uses the same collection. BTW, change the Id properties to be decorated with [BsonId, BsonRepresentation(BsonType.ObjectId)]. Hope it helps.
Use the BsonDocument data type. It can do all of that. BsonDocument and dynamic back and forth is very convenient.
public class CustomObject{
public long Id{get;set;}
public string Name{get;set;}
public List<(string,object)> CollectionDynamic{get;set;}
}
// inserted in mongo
//public class CustomObject_in_Db{
// public long Id {get;set;}
// public string Name {get;set;}
// public string field2 {get;set;}
// public string field3 {get;set;}
// public string field4 {get;set;}
// public string field5 {get;set;}
// }
// something code... mapper(config)
Automapper.Mapper.CreateMap<BsonDocument,CustomObject>()
.ForMember(dest=>dest.Id, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Id)).AsInt64)
.ForMember(dest=>dest.Name, a=>a.MapFrom(s=>s.Id.GetValue(nameof(CustomObject.Name)).AsString)
.ForMember(dest=>dest.CollectionDynamic, a=>a.MapFrom(s=>_getList(s));
// .......
private List<(string, object)> _getList(BsonDocument source){
return source.Elements.Where(e=>!typeof(CustomObject).GetProperties().Select(s=>s.Name).Any(a=>a ==e.Name)).Select(e=>e.Name, BsonTryMapper.MapToDotNetValue(e.Value)));
}
I'm creating a synchronize function between a device and server for a large database. I have a lot of listing tables (the items in a dropdown/picker).
I don't want to write a lot of code and I'm looking for an elegant solution :)
On a device in SQLite I defined listing table like
public class NameTable : IBusinessEntity {
public int Id { get; set; } = 0;
public string Description { get; set; }
}
When I save in database a new record (item) I call this function
public int SaveItem<T>(T item) where T : IBusinessEntity {
lock (locker) {
if (item.Id != 0) {
database.Update(item);
return item.Id;
}
else {
return database.Insert(item);
}
}
}
Now when the device receives a new record from the server the structure is like
public class SyncResult {
public int DeviceId { get; set; }
public int ServerId { get; set; }
public string TableName { get; set; }
public string Description { get; set; }
}
Then I want to save (insert a new record if DeviceId == 0 or update an existing item).
My question is: how can I call SaveItem where T is the TableName from SyncResult?
Thank you in advance for any help (I can offer a beer for that!)
SaveItem is a member of MyDatabase class. Basically my problem is how to pass to SaveItem<T> the T as string.
I don't know if I explained clearly my issue.
You could map a TableName to a Type for example Dictionary<string,Type> Use the Activator class to construct the type. Use reflection to fill the data.
I have a similar structure to the one below
Base class
public class BaseClass
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public Guid Guid { get; set; }
public string Hometown { get; set; }
}
Derived Class
public class DerivedClass : BaseClass
{
public List<DerivedClassDataItem> Data { get; set; }
}
Data class
public class DerivedClassDataItem
{
public string Datum1 { get; set; }
public string Datum2 { get; set; }
public string Datum3 { get; set; }
public string Datum4 { get; set; }
public int Datum5 { get; set; }
public DateTime Datum6 { get; set; }
}
What is the best practice to return specific set of info from the DerivedClass?
a potential set could be:
Name, Address, Guid and then a Data list that only contains Datum1 and Datum4
I could see anonymousTypes, Tuples or another set of class(es), all to be valid approaches.
My concern about creating new set of classs for the set returned is that the class(s) structure will be similar to the structure of the three mentioned above except it will have fewer selected members, which to me, does not sound ideal. (duplicate code and structure)
Using anonymousTypes was my initial solution to tackle this, something like
List<DerivedClass> list = new List<DerivedClass>();
var mySet = list.Select(d => new
{
Name = d.Name,
Address = d.Address,
.
.
.
.
.
Data = d.Data.Select(item => new
{
Datum1 = item.Datum1,
Datum4 = item.Datum4
})
});
but again, that was a headache for us to track through httpResponse and through out API calls.
Should I go with Tuple?
Any insights as to what is the best practice for doing this?
Edit
I am using this set of data to be a response returned by a API/GET call. I will send the set back using HttpRespose and then the framework will transform that into json
this is an actual method we have now
private void populateReturnFile()
{
var returnFileAnonymous = new
{
Vendor = this.Vendor,
OrganizationName = this.OrganizationName,
User = this.User,
Platform = this.Platform,
DictionaryType = this.DictionaryType,
UseCaseId = this.UseCaseId,
Data = this.Data.Select(d => new
{
MigrationTermId = d.MigrationTermId,
ImoLexicalCode = d.ImoLexicalCode
})
};
this.returnFile = returnFileAnonymous;
}
Then my GET will return the retunFile (this is a very simple method, i have remove irrelevant code)
[HttpGet]
public HttpResponseMessage Get(Guid migrationFileId)
{
ProblemList problemList = ProblemList.GetProblemList(migrationFileId);
return Request.CreateResponse(HttpStatusCode.OK, problemList.ReturnFile, new JsonMediaTypeFormatter());
}
If API calls is where you are using these classes, then I personally like to keep it simple and avoid complex inheritance hierarchy. Remember, simple code is good code.
I would make a separate class for each api request/response call. For very simple api calls (ajax requests for example) I like to use anonymous types, but for controllers that only handle API calls I like to create separate classes, organized in a nice folder structure.
Everyone has their "style" but as long as you strive for simplicity your code will be maintainable.
I'm trying to perform a very standard multi mapping query using Dapper, and I'm getting the following error. I also get another error occasionally when this seems to work, but I'm unable to reproduce it at the moment. I'll append it to this post if/when the first problem is solved.
Here is the query code:
const string storedProc = "dbo.GetStopsForRouteID";
var stops = conn.Query<RouteStop, MapLocation, RouteStop>(
storedProc, (stop, loc) =>
{
stop.Location = loc;
return stop;
}, new { RouteID = routeId }, commandType: CommandType.StoredProcedure);
In Dapper.cs on line 498:
var deserializer2 = (Func<IDataReader, TSecond>)info.OtherDeserializers[0];
info.OtherDeserializers is null which causes a NullReferenceException.
This is the guts of the stored procedure:
SELECT
RouteStops.StopID,
RouteStops.Name,
RouteStops.Description,
RouteStops.IsInbound,
RouteStops.Location.Lat as Latitude,
RouteStops.Location.Long as Longitude
FROM dbo.Routes
INNER JOIN dbo.StopsOnRoute ON
Routes.RouteID = StopsOnRoute.RouteID
INNER JOIN dbo.RouteStops ON
StopsOnRoute.StopID = RouteStops.StopID
WHERE Routes.RouteID = #RouteID
ORDER BY StopsOnRoute.SequenceNumber
I've had an extensive look at the dapper code but I can't find anything that seems out of place other than that TFirst's deserialiser isn't null, but TSecond's is. Could there be a problem when it creates TSecond's deserializer that leaves it as null?
Here are the types:
public class MapLocation
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public class RouteStop {
public int StopID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsInbound { get; set; }
public MapLocation Location { get; set; }
}
Probably the main problem here is that you haven't told it how to "split"; try adding the parameter:
splitOn: "Latitude"
without that, as far as dapper can see there is no second result portion (it splits on Id by default).