What is the easiest way to get the output of a Dapper ORM query into the data members of the class that provides the method for the query?
This is my code with a method A (ugly) and B (does not work):
public class MyLab_ClientRef
{
public int UserId { get; set; }
public string ClientId { get; set; }
// ... more fields ...
public bool GetUser(OracleConnection conn, int UserId, string ClientId)
{
bool Ok = false;
IEnumerable<MyLab_ClientRef> QueryResultRecords =
conn.Query<MyLab_ClientRef>(#"
SELECT *
FROM MyLab_ClientRef
WHERE UserId = :UserId
AND ClientId = :ClientId",
new { UserId = UserId, ClientId = ClientId });
if (QueryResultRecords.Count() == 1)
{
// Method A
MyLab_ClientRef Rec = QueryResultRecords.First(); // works
CopyRec(Rec, this); // ugly
// Method B
this = QueryResultRecords.First(); // avoids CopyRec, does not work
Ok = true;
}
return Ok;
}
private static void CopyRec(MyLab_ClientRef CR_From, MyLab_ClientRef CR_To)
{
CR_To.UserId = CR_From.UserId;
CR_To.ClientId = CR_From.ClientId;
}
}
I like to keep the record definition close to the query that gets the record, but don't like to implement a CopyRec method for every table class this way.
Is there no better way to implement this? I tried to write this = ... but that is not possible.
How to write a method B that is better than method A?
Following would not work:
this = QueryResultRecords.First();
Check following links for why:
Why can't I set "this" to a value in C#?
MSDN
As shown in the first link above, your best options remains that you return the MyLab_ClientRef from a given method, can make it static and use if for value or reference assignment, in this case either should yield same result
Check the following if this can be a cleaner implementation in your view:
public class MyLab_ClientRef
{
public int UserId { get; set; }
public string ClientId { get; set; }
// ... more fields ...
public static MyLab_ClientRef GetUser(OracleConnection conn, int UserId, string ClientId)
{
bool Ok = false;
var QueryResultRecords =
conn.Query<MyLab_ClientRef>(#"SELECT * FROM MyLab_ClientRef WHERE UserId = :UserId AND ClientId = :ClientId",new { UserId = UserId, ClientId = ClientId });
if(QueryResultRecords.Any())
return QueryResultRecords.First();
else
return null;
}
}
It can be called as:
var newClient = MyLab_ClientRef.GetUser(conn, UserId,ClientId);
It would be preferred though is the connection object is local to a method and is use in the using clock
Related
I have a database that has two tables as follows, Please ignore the data but the format looks as follows
Now I have a Model class that is constructed as follows
public class FamilyModel
{
public string Name { get; set; }
public List<FamilyModel> FamilyList { get; set; }
public FamilyModel()
{
FamilyList = new List<FamilyModel>();
}
}
Now all I want is to get data from the two tables and populate the list.
So I have a stored procedure that returns data as follows
So I have written some code to populate the above class. But it dosent work. I get a count of 5 when I debug. I want the count to be 2 and when expanded I want something like FamilyA ->{Nick, Tom, Pam}.. FamilyB->{Harry} and so on. Please help fixing this code.
public static FamilyModel familyData()
{
//FamilyModel fml = new FamilyModel();
//fml.FamilyList = new List<FamilyModel>();
using (SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\v11.0; AttachDbFilename=|DataDirectory|\Families.mdf; Integrated Security=True; Connect Timeout=30;"))
{
con.Open();
SqlCommand cmd = new SqlCommand("sp_GetFamilies", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read()) {
FamilyModel fm = new FamilyModel();
fm.Name = dr["FamilyName"].ToString();
foreach (var item in dr["ChildName"].ToString())
{
if (Convert.ToInt32(dr["id"]) == Convert.ToInt32(dr["FID"]))
{
fm.FamilyList.Add(new FamilyModel() { Name = dr["ChildName"].ToString() });
}
}
}
return fm;
}
}
Here is some source code that should get the right idea across. Below it, I've included some explanation for what's going on.
using Dapper;
public class FamilyModel
{
public int Id { get; set;}
public string FamilyName { get; set; }
public List<Person> Members { get; set; } = new List<Person>();//Initializer for Auto-Property, C#6<= only
}
public class Person
{
public int Id { get; set;}
public string Name { get; set; }
}
public class DatabasePOCO
{
public string FamilyName { get; set; }
public string ChildName { get; set; }
public int Fid { get; set; }
public int Id { get; set;}
}
void Main()
{
using (IDbConnection conn = new SqlConnection("..."))
{
conn.Open();
var raw = conn.Query<DatabasePOCO>("sp_GetFamilies",
commandType: CommandType.StoredProcedure);//Could be dynamic, not typed
var familyList = raw
.GroupBy(x => x.Fid)
.Select(x =>
{
var rawMembers = x.ToList();
var fId = x.First().Fid;
var fName = x.First().FamilyName;
var members = rawMembers.Select(y => new Person
{
Id = y.Id,
Name = y.ChildName
});
return new FamilyModel
{
Id = fId,
FamilyName = fName,
Members = members.ToList()
};
});
//Whatever else you want to do here
}
}
Consider using Dappper. It is a great ORM that makes accessing data from database really easy. It's designed to work with SQL Server, but I've had success using Oracle too, and most other RMDBS systems will probably work.
Consider using Slapper. If you have control over your stored procedure, this can reduce a lot of the boilerplate code below.
If you use Dapper (I hope you do), you can play around with C# dynamic objects, or you can create a POCO to help get some type enforcement on your code.
Understand if you care about reference equality. The code I provided below does not enforce reference equality of objects. Reference equality, in my experience, doesn't buy you much and is a pain to enforce.
You need to distinguish between a new row in the data set and a new FamilyModel. One way to do this is to declare a list of models, then look up the "right" one before you add the current child row:
var rootModel = new FamilyModel();
rootModel.Name = "root";
// ... Set up data reader ...
while (dr.Read())
{
//This requires support for the Id in your FamilyModel:
var id = (int)dr["Id"];
//You could also use ".Single(...)" here
var fm = rootModel.FamilyList.Where(x => x.Id == id).First();
if (fm == null)
{
fm = new FamilyModel();
fm.Name = dr["FamilyName"].ToString();
rootModel.FamilyList.Add(fm);
}
fm.FamilyList.Add(new FamilyModel() { Name = dr["ChildName"].ToString() });
}
For each row in your database query, you'll:
Try to look up that family in your list
If you don't find one, create a new one. Add it to your top-level list.
Add the child name as a sub-element of the "current" family.
Suppose I have following table:
public class User
{
public User()
{
}
public User(string name, string pass)
{
addUser(name, pass);
}
public void addUser(string name, string pass)
{
//todo cryptography
this.login = name;
this.password = pass;
}
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
[Unique, MaxLength(20)]
public string login { get; set; }
[MaxLength(20)]
private string password { get; set; }
public string group { get; set; }
}
I have unique keyword in login field. If I add another person with same login, exception will be thrown or this insert command will be skiped?
What is a best way to get user name from User table?
To get all users depending on some name condition I use this example function for my test purpose.
public async void GetRow(string name)
{
var query = dbConnection.Table<User>().Where(x => x.login.Contains(name));
var result = await query.ToListAsync();
foreach (var item in result)
{
User u = item as User;
MessageDialog msgbox = new MessageDialog(u.login);
await msgbox.ShowAsync();
}
}
Suppose I want to get only 1 record from Users table based on a given name, what would be best way to do that.
I tried something like this:
from u in dbConection.Table<User> select u.Login where u.Login = name;
How to return from GetRow function described in 2 question user password? I can recieve only list of items, I search on the web and I find FirstOrDefault function but is there any better way to do it?
Edit:
ad 1. Throws exception
ad 2.This works only if record exists in table, in other case throws exception
var query = (from s in dbConnection.Table<User>() where s.login == name && s.password == password select s).FirstAsync();
User qr = query.Result;
I find solution. This is a simple validation function. Thank You for Your help.
try
{
var query = (from s in dbConnection.Table<User>() where s.login == name && s.password == password select s).FirstAsync();
User x = await query;
if (x != null) return true;
else return false;
}
catch (Exception)
{
return false;
}
var some_strings = new List<string> {"Szcz","epan"};
string first_string = some_strings.FirstOrDefault();
//first_string = "szcz";
some_strings = new List<string>();
first_string = some_strings.FirstOrDefault();
//first_string = null;
if (first_string != null)
// do your stuff here.
if you were dealing with Int's, then the default would have been zero. IF it's a custom class, it'll be whatever your default for the class is.
I have the following test:
public class ListingEventTest
{
public ListingEventTest()
{
Artists = new List<ArtistTest>();
}
public List<ArtistTest> Artists { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public double Popularity { get; set; }
}
public class ArtistTest
{
public string Id { get; set; }
public Stat Stats { get; set; }
}
public class Stat
{
public double Popularity { get; set; }
}
public class ArtistsWithStats_ByName : AbstractIndexCreationTask<ListingEventTest>
{
public ArtistsWithStats_ByName()
{
Map = listingEvents => from listingEvent in listingEvents
let artists = LoadDocument<ArtistTest>(listingEvent.Artists.Select(x => x.Id))
select new
{
Popularity = artists.Average(x => x.Stats.Popularity),
listingEvent.Name
};
}
}
[TestFixture]
public class IndexCanDoSums
{
[Test]
public async void WhenListingEventArtistsHaveStatsIndexReturnsPopularity()
{
var store = new EmbeddableDocumentStore
{
UseEmbeddedHttpServer = true,
Configuration =
{
RunInUnreliableYetFastModeThatIsNotSuitableForProduction = true,
RunInMemory = true,
}
}.Initialize();
IndexCreation.CreateIndexes(typeof(ArtistsWithStats_ByName).Assembly, store);
using (var session = store.OpenAsyncSession())
{
for (int i = 0; i < 10; i++)
{
var le = new ListingEventTest
{
Name = "test-" + i
};
await session.StoreAsync(le);
for (int j = 0; j < 2; j++)
{
var artist = new ArtistTest
{
Stats = new Stat
{
Popularity = 0.89d
}
};
await session.StoreAsync(artist);
le.Artists.Add(artist);
}
await session.SaveChangesAsync();
}
}
Thread.Sleep(2000);
using (var session = store.OpenAsyncSession())
{
var query = session
.Advanced.AsyncLuceneQuery<ListingEventTest>("ArtistsWithStats/ByName");
var result = await query.ToListAsync();
result.First().Popularity.Should().NotBe(0);
}
}
}
When I query this index Popularity is always 0.
Any ideas?
Some funny things going on here.
First, you are storing ArtistTest under the ListingEventTest document, not as separate documents, so in your index there is no need to call LoadDocument, you could just do:
from listingEvent in listingEvents
from artist in listingEvent.Artists
select ...
Second, a Map-only index is a lot like a SQL index where you're just calling out the columns you want to be able to query on. Here, you're doing a calculation on a set of buried properties and you have a top-level property where you want to store that information, but how that ends up working is that your calculated property value goes into the Lucene index (so you could query by Popularity if you want) but the data that is returned is straight from the unaltered document. The map defines what goes into Lucene, which points to the document id, and then the document store returns the documents as the results.
This could be modified somewhat by calling Store(x => x.Popularity) in the index's constructor, which would store the value to be recalled later, but honestly offhand I'm not sure if your calculated value or the document's value (which is zero) would win.
Given that, it becomes pretty confusing to have a document property for the sole purpose of trying to fill it during indexing, which is why it's usually a better option to have a class that represents the mapped state, and then implementing AbstractIndexCreationTask<TDocument, TReduceResult> where the TReduceResult class would only contain the result of your mapping, namely the Name and Popularity columns.
Then when you query from, you can use .ProjectFromIndexFieldsInto<T>() to get your results from the stored index results, not from the document store.
I am new to C# ASP.NET and I am trying to get the items from a store (EPiServer).
Visual Studio says
Cannot resolve symbol Where, OnderzoekId and ToList
What am I doing wrong? I used this code example:
[EPiServerDataStore(AutomaticallyCreateStore = true, AutomaticallyRemapStore = true)]
public class OnderzoekColumn
{
private static int Counter = 0;
public Identity Id { get; set; }
public int ColumnId { get; set; }
public int OnderzoekId { get; set; }
public string ColumnName { get; set; }
public OnderzoekColumn()
{
Initialize();
}
public OnderzoekColumn(int onderzoekId, string columnName)
{
Initialize();
OnderzoekId = onderzoekId;
ColumnName = columnName;
}
protected void Initialize()
{
Id = Identity.NewIdentity(Guid.NewGuid());
ColumnId = System.Threading.Interlocked.Increment(ref Counter);
OnderzoekId = 0;
ColumnName = string.Empty;
}
public static List<OnderzoekColumn> GetOnderzoekColumns(int onderzoekId)
{
var store = typeof(OnderzoekColumn).GetStore();
var columns = from c in store
where c.OnderzoekId == onderzoekId
select c;
if (columns == null)
{
return new List<OnderzoekColumn>();
}
return columns.ToList<OnderzoekColumn>();
}
}
The linq statement
var columns = from c in store
where c.OnderzoekId == onderzoekId
select c;
is trying to enumerate over a collection, but the GetStore() method returns a single item. Try using the following code in place of your GetOnderzoekColumns method (its untested)
public static List<OnderzoekColumn> GetOnderzoekColumns(int onderzoekId)
{
var store = typeof(OnderzoekColumn).GetStore();
var columns = store.Items<OnderzoekColumn>().Where(c => c.OnderzoekId == onderzoekId);
return columns.ToList();
}
I'd add the following extension methods to your solution, then you can use a strongly typed Find method, which will be more efficient than the above, which returns all items, then filters in memory using the linq Where() method.
I used the following code to get it to work. I removed the AutomaticallyCreateStore and AutomaticallyRemapStore attribute also.
public static List<OnderzoekColumn> GetOnderzoekColumns(int onderzoekId)
{
var store = DynamicDataStoreFactory.Instance.GetStore(typeof(OnderzoekColumn));
var query = from item in store.Items<OnderzoekColumn>()
where item.OnderzoekId == onderzoekId
select item;
return query.ToList();
}
My scenario :
I have an object, lets call it object1 which looks like this :
object1{
string commaSeparatedListOfIds;
DateTime time;
int passes;
...
irrelvant properties
...
}
What I wish to do is split commaSeparatedListOfIds for each id value, and save each one in it's own object (object2), with the relevant other properties.
object2{
int id;
DateTime time;
int passes;
}
This duplicates information, but for the method I want to write any other solution will be horrifically messy.
What I have tried is :
List<object2> newObjects = new List<object2>(object1.commaSeparatedListOfIds.Split(',').Select(
new object2{
id int.Parse,
time = object1.time
passes = object1.passes
}).ToList<object2>());
but this will not build.
Can anyone help me do what I wish as elegantly as possible please ? I realise it would be possible with two loops and some horrible code, but I know there's a nice looking solution out there somewhere! :)
I believe you want something like:
List<object2> newObjects = object1.commaSeparatedListOfIds.Split(',')
.Select(str =>
new object2
{
id = int.Parse(str),
time = object1.time,
passes = object1.passes
})
.ToList();
In query syntax(which i prefer when it comes to SelectMany):
var newObjects = from ob1 in object1
from strId in ob1.commaSeparatedListOfIds.Split(',')
select new object2(){
id = int.Parse(strId),
time = ob1.time,
passes = ob1.passes
};
List<object2> result = newObjects.ToList();
(mistakenly assumed that object1 is an IEnumerable<object1>)
public class O1
{
public string Ids { get; set; }
public DateTime Time { get; set; }
public int Passes { get; set; }
}
public class O2
{
public int Id { get; set; }
public DateTime Time { get; set; }
public int Passes { get; set; }
}
static void Main(string[] args)
{
var o1 = new O1();
o1.Ids = "1,2,3,4,5";
o1.Time = DateTime.Now;
o1.Passes = 42;
var results = o1.Ids.Split(',').Select(r => new O2 { Id = int.Parse(r), Time = o1.Time, Passes = o1.Passes });
foreach (var item in results)
{
Console.WriteLine("{0}: {1} {2}", item.Id, item.Time, item.Passes);
}
}
Something like that. Beware of just doing int.Parse(...), though, as you might have invalid data in your Id string.