I ran into an issue where I have used ServiceStack.OrmLite.Sqlite.Windows to build a join query between a table and a view, and the sql emitted contains a select statement with columns like this:
SELECT "Country"."Id", "Country"."Code", "Country"."Title", ...
As far as I can tell, this is perfectly valid SQL, but when I call IDbConnection.Select<Country>(expr), Ormlite doesn't map the fields correctly to the POCO. (I get the right number of results, but all of the fields in each object are null)
Here's the code I'm working with, unfortunately I couldn't get a reduced example to display the same issue:
Repository.cs
public List<Country> FilterValidCountries(ProgrammingMapFilter filter) {
using (_db.BeginTransaction()) {
var q = _db.From<Country>()
.Join<Mid, ProgrammingMapView>((mid, map) => mid.Id == map.Mid)
.Join<Country, Mid>((country, mid) => country.Code == mid.CountryCode)
.OrderBy(x => x.Title);
if (filter.ProductId.HasValue)
q = q.Where<ProgrammingMapView>(x => x.ProductId == filter.ProductId);
if (filter.ProtocolId.HasValue)
q = q.Where<ProgrammingMapView>(x => x.ProtocolId == filter.ProtocolId);
return _db.Select(q);
}
}
Models
public class Country {
public int Id { get; set; }
public string Code { get; set; }
public string Title { get; set; }
public string ShortTitle { get; set; }
public DateTime? ModifiedOn { get; set; }
public int? ModifiedBy { get; set; }
}
public class Mid {
public int Id { get; set; }
public string CountryCode { get; set; }
public DateTime? ModifiedOn { get; set; }
public int? ModifiedBy { get; set; }
}
public class ProgrammingMapView {
public int ProductId { get; set; }
public int ProtocolId { get; set; }
public int Mid { get; set; }
}
Schema
create table Country (
Id integer primary key,
Code text,
Title text,
ShortTitle text,
ModifiedOn text,
ModifiedBy integer
);
create table Mid (
Id integer primary key,
CountryCode text,
ModifiedOn text,
ModifiedBy integer
);
create view ProgrammingMapView as
select
p.Id ProductId
, pt.Id ProtocolId
, m.Id Mid
from Mid m
join MidProduct mprod on (mprod.RegisteredMid = m.Id)
join Product p on (p.Id = mprod.ProductId)
join MidProtocol mprot on (mprot.RegisteredMid = m.Id)
join ProtocolType pt on (pt.Id = mprot.ProtocolId)
join ProductProtocol pp on (pp.ProductId = p.Id and pp.ProtocolTypeId = pt.Id)
;
Sql emitted
SELECT "Country"."Id", "Country"."Code", "Country"."Title", "Country"."ShortTitle", "Country"."ModifiedOn", "Country"."ModifiedBy"
FROM "Country"
INNER JOIN "ProgrammingMapView" ON ("Mid"."Id" = "ProgrammingMapView"."Mid")
INNER JOIN "Mid" ON ("Country"."Code" = "Mid"."CountryCode")
WHERE ("ProgrammingMapView"."ProductId" = 87)
ORDER BY "Country"."Title" ASC
Debugging
I tracked the issue down to ServiceStack.OrmLite.OrmLiteWriteExtensions.cs:323 (v4.0.30). In TryGuessColumnIndex(), when Ormlite calls dataReader.GetName(i), the GetName method always returns "Country". (Not "Country.Id" or "Country.Title")
private static int TryGuessColumnIndex(string fieldName, IDataReader dataReader)
{
if (OrmLiteConfig.DisableColumnGuessFallback)
return NotFound;
var fieldCount = dataReader.FieldCount;
for (var i = 0; i < fieldCount; i++)
{
var dbFieldName = dataReader.GetName(i);
....
That leads me to believe that the System.Data.Sqlite.Core library that I have installed doesn't parse quoted, table qualified identifiers like that correctly. System.Data.Sqlite.Core (v1.0.93) displays the same behavior without using servicestack.ormlite:
using (var cn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", dbPath2)))
{
cn.Open();
var query = #"
SELECT ""Country"".""Id"", ""Country"".""Code"", ""Country"".""Title"", ""Country"".""ShortTitle"", ""Country"".""ModifiedOn"", ""Country"".""ModifiedBy""
FROM ""Country"" INNER JOIN ""ProgrammingMapView"" ON (""Mid"".""Id"" = ""ProgrammingMapView"".""Mid"") INNER JOIN ""Mid"" ON (""Country"".""Code"" = ""Mid"".""CountryCode"")
WHERE (""ProgrammingMapView"".""ProductId"" = 87)
ORDER BY ""Country"".""Title"" ASC
";
using (var cmd = new SQLiteCommand(query, cn))
{
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
for (var i = 0; i < reader.FieldCount; i++)
{
Console.Write(reader.GetName(i) + ", ");
}
Console.WriteLine();
}
}
}
}
Output
Country, Country, Country, Country, Country, Country,
Country, Country, Country, Country, Country, Country,
What do I do?
Is there something I've missed in the configuration of ServiceStack.Ormlite or System.Data.Sqlite that will allow IDataReader.GetName(i) to return the correct column name (and not just the Table name)?
Related
I have a List<Semesters> Object and Semesters class has Semester as String[] type. Below is my code which has error where Semester[1]= item.Semester. What is the correct syntax?
public class CourseList
{
public int? Prog_id { get; set; }
public List<Semesters> Series{ get; set; }
}
public class Semesters
{
public string[] Semester { get; set; }
public string Subject { get; set; }
public int Credit {get; set;}
}
[HttpGet]
[AllowAnonymous]
public CourseList GetCourseList(int Progid)
{
using (var context = new DataContext())
{
CourseList Obj = new CourseList();
List<Semesters> CObj = new List<Semesters>();
var qry = (from a in context.Courses
where a.Prog_id == Progid
orderby a.Semester
select a).ToList();
foreach (var item in qry)
{
Obj.Prog_id = item.Prog_id;
if (item.Semester.ToString() == "1st Semester")
{
CObj.Add(new Semesters {Semester[1]=item.Semester, Subject = item.Subject.ToString(), Coursetitle= item.Coursetitle });
}
if (item.Semester.ToString() == "2nd Semester")
{
CObj.Add(new Semesters { Semester[2] = item.Semester, Subject = item.Subject.ToString(), Coursetitle = item.Coursetitle });
}
Obj.Series = CObj;
}
return Obj;
}
You say Semester is an array of strings, so in this statement
Semester[1] = item.Semester
Semester is an array and item.Semester is an array.
Semester[1] should be assigned a string, but you are assigning it an array. You'll need to change it to (possibly...)
// assign a value from the item.Semester array.
Semester[1] = item.Semester[n]
That's one possibility w/o more info.
I want to do an update for a specific field on a table based on the results from a query that includes a join. Using OrmLite with ServiceStack.
My Classes are as follows:
public class Document
{
public int Id { get; set; }
public string BCL_Code { get; set; }
public bool IsActive { get; set; }
public int DocumentTypeId { get; set; }
}
public class DocumentType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
Trying to do the Update on the Join with the following code:
var q = db.From<Document>()
.Join<Document, DocumentType>(
(doc, type) => doc.DocumentTypeId == type.Id)
.Where(d => d.BCL_Code == "FR49")
.And<DocumentType>(t => t.TypeName == "Enrollment Process");
db.UpdateOnly(new Document { IsActive = false }, onlyFields: q);
I know I can update specific fields, and I know how to do joins, but when I try to include a join in the query, and then do an UpdateOnly, I get an error message on the db.UpdateOnly line:
The multi-part identifier "DocumentType.TypeName" could not be bound.
Is it possible to do an Update on a Join Query?
If so, what is the proper way to do it?
There's no Typed APIs for Update From Table in OrmLite yet, you can add a feature request for it.
In the meantime you can use Custom SQL, e.g:
db.ExecuteSql(#"UPDATE Document SET IsActive = #isActive
FROM Document d
INNER JOIN DocumentType t ON (d.DocumentTypeId = t.Id)
WHERE d.BCL_Code = 'FR49'
AND t.TypeName = 'Enrollment Process'",
new { isActive = false });
i'm learning, EntityFramework.
using (YonetimDBEntities YonetimDB = new YonetimDBEntities())
{
var Sorgu = from c in YonetimDB.iletisim
select new {
c.id,
c.FullName,
c.Email,
c.Subject,
c.Date,
c.Status
};
// Status Value 1 or 0
myRepeaterName.DataSource = Sorgu.ToList();
myRepeaterName.DataBind();
}
My c.Status value 1 or 0 , i want control my status value and write in my Repeater Line,
Example
if (c.Status == 1) { c.StatusString = "Active"; }else{ c.StatusString = "Deactive"; }
Can i read and how to write my Repeater Line.
Thanks.
Since you are creating an anonymous type (new {}) you can basically add whatever you want into it.
Try this:
var Sorgu = from c in YonetimDB.iletisim
select new {
c.id,
c.FullName,
c.Email,
c.Subject,
c.Date,
c.Status,
StatusString = c.Status == 1 ? "Active" : "Deactive"
};
A better solution is actually this:
public class sorguModel {
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string Subject { get; set; }
public DateTime Date { get; set; }
public bool Status { get; set; }
public string StatusText {
get{
return this.Status == 1 ? "Active" : "Deactive";
}
}
}
var sorguList = YonetimDB.iletisim
.select( i => new sorguModel {
Id = c.id,
FullName = c.FullName,
Email = c.Email,
Subject = c.Subject,
Date = c.Date, // ASSUMING THE DATE IS A PROPER DATE FORMAT
Status = c.Status})
.ToList();
I have a very simple Situation with 2 tables
public class Movie
{
[Key]
public Guid ID { get; set; }
public string Name { get; set; }
public byte[] Hash { get; set; }
public int GenreID{ get; set; }
[ForeignKey("GenreID")]
public virtual Genre genre{ get; set; }
}
and
public class Genre
{
public int ID { get; set; }
public string Name { get; set; }
}
Now, in an import sequence I want to create new movies and link the Genre with the existing entries in the Genre table or create new Genre entries if they don't exist.
Movie m = new Movie();
m.ID = Guid.NewGuid();
IndexerContext db = new IndexerContext();
var genre = db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
if(genre!= null)
{
m.GenreID= genre.GenreID;
}
else
{
genre= new Genre();
genre.Name = genreValue;
db.Genres.Add(genre);
var genreCreated= db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
m.GenreID= genreCreated.GenreID;
}
Now the problem is, it doesn't work. The last line fails because genreCreated is null.
Plus I think I must doing it wrong - it can't be that difficult in Entity Framework.
can anyone help me?
db.Genres.Add(genre);
This does not send insert statement to database - this instructs entity framework that new record should be inserted when saving changes. Genre will be saved (and created id available) after you call db.SaveChanges(); As for now, you do not have save call, so genreCreated is null.
In your situation - fix is simple, you do not need to select genreCreated from db. Just setting m.Genre to new value should do the job
Movie m = new Movie();
m.ID = Guid.NewGuid();
IndexerContext db = new IndexerContext();
var genre = db.Genre.Where(g => g.Name== genreValue).FirstOrDefault();
if(genre! = null)
{
m.GenreID = genre.GenreID;
}
else
{
genre = new Genre();
genre.Name = genreValue;
m.Genre = genre;
}
db.SaveChanges(); //m.GenreID will automatically be set to newly inserted genre
After the add statement you need to save it:
Try
genre= new Genre();
genre.Name = genreValue;
db.Genres.Add(genre);
db.SaveChanges();
How should I Map an object with a nullable field? I guess I must turn the nullable field into a non-nullable version, and it's that step that I stumble upon.
What is the proper way to map nullable properties?
public class Visit {
public string Id { get; set; }
public int? MediaSourceId { get; set; }
}
public class MapReduceResult
{
public string VisitId { get; set; }
public int MediaSourceId { get; set; }
public string Version { get; set; }
public int Count { get; set; }
}
AddMap<Visit>(
visits =>
from visit in visits
select new
{
VisitId = visit.Id,
MediaSourceId =
(visit.MediaSourceId.HasValue)
? visit.MediaSourceId
: UNUSED_MEDIASOURCE_ID,
Version = (string) null,
Count = 1
});
This doesn't work! In fact; this Map is completely ignored, while the other Maps work fine, and they are in the end Reduced as expected.
Thanks for helping me!
Below is a newly added test case that fails with a "Cannot assign <null> to anonymous type property". How am I supposed to get this flying with the least amount of pain?
[TestFixture]
public class MyIndexTest
{
private IDocumentStore _documentStore;
[SetUp]
public void SetUp()
{
_documentStore = new EmbeddableDocumentStore {RunInMemory = true}.Initialize();
_documentStore.DatabaseCommands.DisableAllCaching();
IndexCreation.CreateIndexes(typeof (MyIndex).Assembly, _documentStore);
}
[TearDown]
public void TearDown()
{
_documentStore.Dispose();
}
[Test]
public void ShouldWork()
{
InitData();
IList<MyIndex.MapReduceResult> mapReduceResults = null;
using (var session = _documentStore.OpenSession())
{
mapReduceResults =
session.Query<MyIndex.MapReduceResult>(
MyIndex.INDEX_NAME)
.Customize(x => x.WaitForNonStaleResults()).ToArray();
}
Assert.That(mapReduceResults.Count, Is.EqualTo(1));
}
private void InitData()
{
var visitOne = new Visit
{
Id = "visits/64",
MetaData = new MetaData {CreatedDate = new DateTime(1975, 8, 6, 0, 14, 0)},
MediaSourceId = 1,
};
var visitPageVersionOne = new VisitPageVersion
{
Id = "VisitPageVersions/123",
MetaData = new MetaData {CreatedDate = new DateTime(1975, 8, 6, 0, 14, 0)},
VisitId = "visits/64",
Version = "1"
};
using (var session = _documentStore.OpenSession())
{
session.Store(visitOne);
session.Store(visitPageVersionOne);
session.SaveChanges();
}
}
public class MyIndex :
AbstractMultiMapIndexCreationTask
<MyIndex.MapReduceResult>
{
public const string INDEX_NAME = "MyIndex";
public override string IndexName
{
get { return INDEX_NAME; }
}
public class MapReduceResult
{
public string VisitId { get; set; }
public int? MediaSourceId { get; set; }
public string Version { get; set; }
public int Count { get; set; }
}
public MyIndex()
{
AddMap<Visit>(
visits =>
from visit in visits
select new
{
VisitId = visit.Id,
MediaSourceId = (int?) visit.MediaSourceId,
Version = (string) null,
Count = 1
});
AddMap<VisitPageVersion>(
visitPageVersions =>
from visitPageVersion in visitPageVersions
select new
{
VisitId = visitPageVersion.VisitId,
MediaSourceId = (int?) null,
Version = visitPageVersion.Version,
Count = 0
});
Reduce =
results =>
from result in results
group result by result.VisitId
into g
select
new
{
VisitId = g.Key,
MediaSourceId = (int?) g.Select(x => x.MediaSourceId).FirstOrDefault(),
Version = g.Select(x => x.Version).FirstOrDefault(),
Count = g.Sum(x => x.Count)
};
}
}
}
You don't need to do anything to give nullables special treatment.
RavenDB will already take care of that.