Retrofit: Image Upload in POST #body - image

After hours goolging and trying different solutions I gave up and I'm asking for some direction, if possible some example. Here is the problem: I have a class that have a picture atribute. I'm using Retrofit and I want to send the Image as part of the Body of HTTP POST and I'm receiving a error. Bellow the code and the error.
Thank you in advance for your help.
The POJO Class:
public class Class1 {
#SerializedName("Picture")
private Bitmap mPicture;
#SerializedName("Giver")
public Integer mGiver;
public String getPicture() {
return mPicture;
}
public void setPicture (Bitmap picture) {
this.mPicture = picture;
}
public String getLaboratory() {
return mLaboratory;
}
public void setLaboratory(String laboratory) {
this.mLaboratory = laboratory;
}
The Activity:
mClass1.setPicture(mImageBitmap);
mClass1DAO.insertClass1(mClass1, new Callback<Integer>() {
#Override
public void success(Integer uid, Response response) {
mClass1.setUID(uid);
progressDialog.dismiss();
Toast.makeText(getApplicationContext(), R.string.msgThankYou, Toast.LENGTH_LONG).show();
dispatchNavigationDrawerActivity();
}
#Override
public void failure(RetrofitError error) {
progressDialog.dismiss();
showErrorDialog(error.getLocalizedMessage());
}
});
The API
#POST("/Service.svc/Insert")
void insert(#Body Class1 class1, Callback<Integer> cb);
The WebService in c#
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, UriTemplate = "InsertMedicineToDonate")]
Int32 insertMedicineToDonate(MedicineToDonate medicineToDonate);
//insertMedicineToDonate
public Int32 insertMedicineToDonate(MedicineToDonate medicineToDonate)
{
UserService mUserService = new UserService();
if (mUserService.isUserAuthorized())
{
return this.insertMedicineToDonateAuth(medicineToDonate);
}
else
{
errorDetail = new CustomHttpError(003);
throw new WebFaultException<CustomHttpError>(errorDetail, HttpStatusCode.Forbidden);
}
}
Web Service POJO Class
namespace DoarMed
{
[DataContract]
public class MedicineToDonate
{
[DataMember]
public Int32 UID { get; set; }
[DataMember]
public Bitmap Picture { get; set; }
THE ERROR
In the debug when I open the class and look at the attributes all of them are correct but the Picture is wrong.
See the Picture information bellow:
- Picture {System.Drawing.Bitmap} System.Drawing.Bitmap
+ Flags '((System.Drawing.Image)(medicineToDonate.Picture)).Flags' threw an exception of type 'System.ArgumentException' int {System.ArgumentException}
+ FrameDimensionsList '((System.Drawing.Image)(medicineToDonate.Picture)).FrameDimensionsList' threw an exception of type 'System.ArgumentException' System.Guid[] {System.ArgumentException}
+ Height '((System.Drawing.Image)(medicineToDonate.Picture)).Height' threw an exception of type 'System.ArgumentException' int {System.ArgumentException}
and so on
When I try to save the Picture to the DataBase the code throw System.ArgumentException
What am I missing here?
Thank you in advance

This is the solution, after 3 days trying. I hope it can save your time. if it does save your time, please give me +1.
Client Android
Class
public class MedicineToDonate {
#SerializedName("Picture")
private String mPicture;
#SerializedName("DateTimeInsert")
public Long mDateTimeInsert;
public Bitmap getPicture() {
return Global.convertStringToBitmap(mPicture);
}
public void setPicture(Bitmap picture) {
this.mPicture = Global.convertBitmapToString(picture);
}
Retrofit
mMedicineToDonateDAO.getGiverAllMedicineToDonate(mGlobal.getUserUID(), new Callback<List<MedicineToDonate>>() {
#Override
public void success(List<MedicineToDonate> mMedicineToDonateList, Response response) {
if (mMedicineToDonateList != null) {
for (int i = 1; i <= mMedicineToDonateList.size(); i++) {
mMedicineToDonate = mMedicineToDonateList.get(i - 1);
mAdapter.add(mMedicineToDonate);
}
}
progressDialog.dismiss();
Fragment mFragment = mFragmentManager.findFragmentByTag(Global.OPTION_DONATE);
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.detach(mFragment)
.attach(mFragment)
.commit();
mFragmentManager.executePendingTransactions();
}
#Override
public void failure(RetrofitError error) {
progressDialog.dismiss();
showErrorDialog(error.getLocalizedMessage());
}
});
Global
public static String convertBitmapToString(Bitmap imageBitmap){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
if(imageBitmap != null) {
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
return Base64.encodeToString(byteArray, Base64.URL_SAFE);
}else{
return null;
}
}
public static Bitmap convertStringToBitmap (String encodedString) {
try {
byte[] data = Base64.decode(encodedString, Base64.URL_SAFE);
return BitmapFactory.decodeByteArray(data, 0, data.length);
} catch(Exception e) {
e.getMessage();
return null;
}
}
Server Side (C#/IIS/Postgresql)
Class
[DataContract]
public class MedicineToDonate
{
[DataMember]
public string Picture { get; set; }
[DataMember]
public Int64 DateTimeInsert { get; set; }
[DataMember]
INSERT:
NpgsqlParameter addPictureParameter = new NpgsqlParameter("#" + Global.MEDICINETODONATE_COL_picture, NpgsqlDbType.Bytea);
byte[] byteArrayPicture = Global.convertStringToByteArray(medicineToDonate.Picture);
addPictureParameter.Value = byteArrayPicture;
SELECT:
byte [] pictureByteArray = (byte[]) reader[12];
mMedicineToDonate.Picture = Global.convertByteArrayToString(pictureByteArray);
Global
public static string convertByteArrayToString(byte[] bytes)
{
char[] chars = new char[bytes.Length / sizeof(char)];
System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
return new string(chars);
}

Related

Xamarin Forms consuming Web Service

I'm new in mobile apps and now developing an app with xamarin forms. There is a website which i developed with django (sqlite3 db), and now i'am trying to consume data from it and display in my mobile app in listvew. Any thoughts how to achieve it. I've tried this but it doesn't work. Should i use rest api?
public class LandingViewModel : INotifyPropertyChanged
{
private List<Dishes> _menuList { set; get; }
public List<Dishes> MenuList
{
get
{
return _menuList;
}
set
{
if(value != _menuList)
{
_menuList = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public LandingViewModel()
{
GetDataAsync();
}
private async void GetDataAsync()
{
HttpClient client = new HttpClient();
var response = await client.GetAsync("https://mysite.ru/project/");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var menu = JsonConvert.DeserializeObject<List<Dishes>>(content);
MenuList = new List<Dishes>(menu);
}
}
models:
public class Dishes
{
public int id { get; set; }
public string description { get; set; }
public string image { get; set; }
public DateTime published { get; set; }
}
my database in django:
operations = [
migrations.CreateModel(
name='Post',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.TextField(blank=True)),
('image', models.ImageField(blank=True, upload_to='pictures/')),
('published', models.DateTimeField(verbose_name='publishing date')),
],
),
]
i solved the problem
in postgresql database allowed remote connection: in postgresql.conf replaced line listen_addresses = 'localhost' with listen_addresses = '*'
allowed tcp\ip connection on port 5432
in my web api established connection width db
NpgsqlConnection connection;
public string _name;
public string _description;
public string _image;
private readonly MenuContext _context;
public MenuController(MenuContext context)
{
_context = context;
string connectionString = "Server=server; Port=5432; User Id=user;
Password=password; Database=database";
try
{
connection = new NpgsqlConnection(connectionString);
connection.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
NpgsqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM table";
try
{
NpgsqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
_name = reader[1].ToString();
_image = reader[2].ToString();
_description = reader[3].ToString();
_context.Menus.Add(new Menu { name = _name, description =
_description, image = "https://mysite.ru/media/" + _image });
_context.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
// GET: api/Menu
[HttpGet]
public async Task<ActionResult<IEnumerable<Menu>>> GetMenus()
{
return await _context.Menus.ToListAsync();
}
code in my app:
private async void GetDataAsync()
{
HttpClient httpClient = new HttpClient();
var content = await httpClient.GetStringAsync("https://locahost/api/Menu");
var menu = JsonConvert.DeserializeObject<List<Dishes>>(content);
MenuList = new ObservableCollection<Dishes>(menu);
}
and then displayed it in listview

Database handler globally as Singleton pattern in xamarin forms

I am developing an application which have a local database for offline support. So I am using Sqlite.net.pcl plugin and its working fine for all Create, Insert, Update and Delete table for every class model.
But instead of creating a separate database activities like insert, get, update for each Model class, I tried to worked on singeton pattern of common database handler(DatabasHandler.cs).
This is my code which I tried to workout singleton pattern,
public void CreateTable<T>() where T : new()
{
var myClass = new T();
myDatabase.CreateTableAsync<T>().Wait();
}
I called this function from my EmployeeViewModel class like this;
App.Database.CreateTable<EmployeeModel>();
here EmployeeModel is a model class and its worked fine, also the above function is successfully created a Employee Table. Doing the same way I created rest of the Tables from each ViewModel like this;
App.Database.CreateTable<SalaryModel>(); // call from SalaryViewModel Page
App.Database.CreateTable<EmployeeAttendanceModel>(); // call from AttendanceViewModel Page
Next: So how can I insert and get all list items into DatabaseHandler.cs using same (Create Table)singleton pattern. My question is;
How should I create a method for Insert/Get/Update a list in DatabaseHandler.cs(Singleton class)?
How should I call those method(Insert/Get/Update) from its viewmodel?
Please help me,
Now I had a similar thing in my Old XF app this is how I implemented the Singleton this will also answer your first question:
How should I create a method for Insert/Get/Update a list in DatabaseHandler.cs(Singleton class)?
public class DatabaseHandler: IDisposable
{
private SQLiteConnection conn;
//public static string sqlpath;
private bool disposed = false;
private static readonly Lazy<DatabaseHandler> database = new Lazy<DatabaseHandler>(() => new DatabaseHandler());
private DatabaseHandler() { }
public static DatabaseHandler Database
{
get
{
return database.Value;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
Dispose();
}
disposed = true;
}
public bool InitDatabase()
{
var ifExist = true;
try
{
this.CreateDatabase();
ifExist = TableExists(nameof(LocationModel), conn);
if (!ifExist)
this.CreateTable<LocationModel>();
return true;
}
catch (Exception ex)
{
return false;
}
}
public static bool TableExists(String tableName, SQLiteConnection connection)
{
var cmd = connection.CreateCommand("SELECT name FROM sqlite_master WHERE type = 'table' AND name = #name", new object[] { tableName });
//cmd.CommandText = "SELECT * FROM sqlite_master WHERE type = 'table' AND name = #name";
//cmd.Parameters.Add("#name", DbType.String).Value = tableName;
string tabledata = cmd.ExecuteScalar<string>();
return (cmd.ExecuteScalar<string>() != null);
}
public SQLiteConnection GetConnection()
{
var sqliteFilename = "xamdblocal.db3";
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
var path = Path.Combine(documentsPath, sqliteFilename);
Console.WriteLine(path);
if (!File.Exists(path)) File.Create(path);
//var plat = new SQLite.Net.Platform.XamarinAndroid.SQLitePlatformAndroid();
var conn = new SQLiteConnection(path);
// Return the database connection
return conn;
}
private bool CreateDatabase()
{
conn = GetConnection();
string str = conn.DatabasePath;
return true;
}
public bool CreateTable<T>()
where T : new()
{
conn.DropTable<T>();
conn.CreateTable<T>();
return true;
}
public bool InsertIntoTable<T>(T LoginData)
where T : new()
{
conn.Insert(LoginData);
return true;
}
public bool InsertBulkIntoTable<T>(IList<T> LoginData)
where T : class //new()
{
conn.InsertAll(LoginData);
return true;
}
public List<T> SelectDataFromTable<T>()
where T : new()
{
try
{
return conn.Table<T>().ToList();
}
catch (Exception ex)
{
return null;
}
}
public List<T> SelectTableDatafromQuery<T>(string query)
where T : new()
{
return conn.Query<T>(query, new object[] { })
.ToList();
}
public bool UpdateTableData<T>(string query)
where T : new()
{
conn.Query<T>(query);
return true;
}
public void UpdateTableData<T>(IEnumerable<T> query)
where T : new()
{
conn.UpdateAll(query);
}
public void UpdateTableData<T>(T query)
where T : new()
{
conn.Update(query);
}
public bool DeleteTableData<T>(T LoginData)
{
conn.Delete(LoginData);
return true;
}
public bool DeleteTableDataFromPrimaryKey<T>(object primaryKey)
{
conn.Delete(primaryKey);
return true;
}
public bool DeleteTableDataFromQuery<T>(string query)
where T : new()
{
conn.Query<T>(query);
return true;
}
}
How should I call those method(Insert/Get/Update) from its viewmodel? Please help me,
Now for Eg: you want to insert location's Lat Long in your local database where your local model looks something like this:
public class LocationModel
{
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Address { get; set; }
}
So first what you will do is create an instance of LocationModel something like this:
var locationModel = new LocationModel
{
Latitude = location.Latitude,
Longitude = location.Longitude
};
Then insert it something like this:
DatabaseHandler.Database.InsertIntoTable<LocationModel>(locationModel);
Also, do not forget to add the SQLiteNetExtensions in your project for Linq support.
Goodluck feel free to revert in case of queries

Trying to call code in my controller but getting Null Reference error

Don't want to over-complicate the issue, but I think I need to post all the code that's hooked into this error.
Using MvcMailer and introduced a separate Send mechanism (for use with Orchard CMS' own EMail).
The MvcMailer Code:
1) AskUsMailer.cs:
public class AskUsMailer : MailerBase, IAskUsMailer
{
public AskUsMailer()
: base()
{
//MasterName = "_Layout";
}
public virtual MvcMailMessage EMailAskUs(AskUsViewModel model)
{
var mailMessage = new MvcMailMessage { Subject = "Ask Us" };
ViewData.Model = model;
this.PopulateBody(mailMessage, viewName: "EMailAskUs");
return mailMessage;
}
}
2) IAskUsMailer.cs:
public interface IAskUsMailer : IDependency
{
MvcMailMessage EMailAskUs(AskUsViewModel model);
}
3) AskUsController.cs: (GETTING NULL REFERENCE ERROR BELOW)
[Themed]
public ActionResult Submitted()
{
//This is the new call (see new code below):
//Note: Debugging steps through eMailMessagingService,
//then shows the null reference error when continuing to
//SendAskUs
eMailMessagingService.SendAskUs(askUsData);
//Below is normal MvcMailer call:
//AskUsMailer.EMailAskUs(askUsData).Send();
return View(askUsData);
}
Note: askUsData is defined in a separate block in the controller:
private AskUsViewModel askUsData;
protected override void OnActionExecuting(ActionExecutingContext
filterContext)
{
var serialized = Request.Form["askUsData"];
if (serialized != null) //Form was posted containing serialized data
{
askUsData = (AskUsViewModel)new MvcSerializer().
Deserialize(serialized, SerializationMode.Signed);
TryUpdateModel(askUsData);
}
else
askUsData = (AskUsViewModel)TempData["askUsData"] ??
new AskUsViewModel();
TempData.Keep();
}
protected override void OnResultExecuted(ResultExecutedContext
filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["askUsData"] = askUsData;
}
I did not know how to get my EMailMessagingService.cs (see below) call into the controller, so in a separate block in the controller I did this:
private IEMailMessagingService eMailMessagingService;
public AskUsController(IEMailMessagingService eMailMessagingService)
{
this.eMailMessagingService = eMailMessagingService;
}
I think this is part of my problem.
Now, the new code trying to hook into Orchard's EMail:
1) EMailMessagingServices.cs:
public class EMailMessagingService : IMessageManager
{
private IAskUsMailer askUsMailer;
private IOrchardServices orchardServices;
public EMailMessagingService(IAskUsMailer askUsMailer,
IOrchardServices orchardServices)
{
this.orchardServices = orchardServices;
this.askUsMailer = askUsMailer;
this.Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void SendAskUs(AskUsViewModel model)
{
var messageAskUs = this.askUsMailer.EMailAskUs(model);
messageAskUs.To.Add("email#email.com");
//Don't need the following (setting up e-mails to send a copy anyway)
//messageAskUs.Bcc.Add(AdminEmail);
//messageAskUs.Subject = "blabla";
Send(messageAskUs);
}
....
}
The EMailMessagingService.cs also contains the Send method:
private void Send(MailMessage messageAskUs)
{
var smtpSettings = orchardServices.WorkContext.
CurrentSite.As<SmtpSettingsPart>();
// can't process emails if the Smtp settings have not yet been set
if (smtpSettings == null || !smtpSettings.IsValid())
{
Logger.Error("The SMTP Settings have not been set up.");
return;
}
using (var smtpClient = new SmtpClient(smtpSettings.Host,
smtpSettings.Port))
{
smtpClient.UseDefaultCredentials =
!smtpSettings.RequireCredentials;
if (!smtpClient.UseDefaultCredentials &&
!String.IsNullOrWhiteSpace(smtpSettings.UserName))
{
smtpClient.Credentials = new NetworkCredential
(smtpSettings.UserName, smtpSettings.Password);
}
if (messageAskUs.To.Count == 0)
{
Logger.Error("Recipient is missing an email address");
return;
}
smtpClient.EnableSsl = smtpSettings.EnableSsl;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
messageAskUs.From = new MailAddress(smtpSettings.Address);
messageAskUs.IsBodyHtml = messageAskUs.Body != null &&
messageAskUs.Body.Contains("<") &&
messageAskUs.Body.Contains(">");
try
{
smtpClient.Send(messageAskUs);
Logger.Debug("Message sent to {0} with subject: {1}",
messageAskUs.To[0].Address, messageAskUs.Subject);
}
catch (Exception e)
{
Logger.Error(e, "An unexpected error while sending
a message to {0} with subject: {1}",
messageAskUs.To[0].Address, messageAskUs.Subject);
}
}
}
Now, in EMailMessagingService.cs I was getting an error that things weren't being implemented, so I auto-generated the following (don't know if this is part of my error):
public void Send(Orchard.ContentManagement.Records.ContentItemRecord recipient, string type, string service, System.Collections.Generic.Dictionary<string, string> properties = null)
{
throw new NotImplementedException();
}
public void Send(System.Collections.Generic.IEnumerable<Orchard.ContentManagement.Records.ContentItemRecord> recipients, string type, string service, System.Collections.Generic.Dictionary<string, string> properties = null)
{
throw new NotImplementedException();
}
public void Send(System.Collections.Generic.IEnumerable<string> recipientAddresses, string type, string service, System.Collections.Generic.Dictionary<string, string> properties = null)
{
throw new NotImplementedException();
}
public bool HasChannels()
{
throw new NotImplementedException();
}
public System.Collections.Generic.IEnumerable<string> GetAvailableChannelServices()
{
throw new NotImplementedException();
}
2) IEMailMessagingServices.cs
public interface IEMailMessagingService
{
MailMessage SendAskUs(AskUsViewModel model);
}
MvcMailer works fine without this addition (outside of Orchard), but I am trying to get everything working within Orchard.
I just cannot figure out what I am doing wrong. Any thoughts?
Sorry for excessive code.
IEmailMessaginService does not implement IDependency, so it can't be found by Orchard as a dependency. That's why it's null.

GWT retrieve list from datastore via serviceimpl

Hi I'm trying to retrieve a linkedhashset from the Google datastore but nothing seems to happen. I want to display the results in a Grid using GWT on a page. I have put system.out.println() in all the classes to see where I go wrong but it only shows one and I don't recieve any errors. I use 6 classes 2 in the server package(ContactDAOJdo/ContactServiceImpl) and 4 in the client package(ContactService/ContactServiceAsync/ContactListDelegate/ContactListGui). I hope someone can explain why this isn't worken and point me in the right direction.
public class ContactDAOJdo implements ContactDAO {
#SuppressWarnings("unchecked")
#Override
public LinkedHashSet<Contact> listContacts() {
PersistenceManager pm = PmfSingleton.get().getPersistenceManager();
String query = "select from " + Contact.class.getName();
System.out.print("ContactDAOJdo: ");
return (LinkedHashSet<Contact>) pm.newQuery(query).execute();
}
}
public class ContactServiceImpl extends RemoteServiceServlet implements ContactService{
private static final long serialVersionUID = 1L;
private ContactDAO contactDAO = new ContactDAOJdo() {
#Override
public LinkedHashSet<Contact> listContacts() {
LinkedHashSet<Contact> contacts = contactDAO.listContacts();
System.out.println("service imp "+contacts);
return contacts;
}
}
#RemoteServiceRelativePath("contact")
public interface ContactService extends RemoteService {
LinkedHashSet<Contact> listContacts();
}
public interface ContactServiceAsync {
void listContacts(AsyncCallback<LinkedHashSet <Contact>> callback);
}
public class ListContactDelegate {
private ContactServiceAsync contactService = GWT.create(ContactService.class);
ListContactGUI gui;
void listContacts(){
contactService.listContacts(new AsyncCallback<LinkedHashSet<Contact>> () {
public void onFailure(Throwable caught) {
gui.service_eventListContactenFailed(caught);
System.out.println("delegate "+caught);
}
public void onSuccess(LinkedHashSet<Contact> result) {
gui.service_eventListRetrievedFromService(result);
System.out.println("delegate "+result);
}
});
}
}
public class ListContactGUI {
protected Grid contactlijst;
protected ListContactDelegate listContactService;
private Label status;
public void init() {
status = new Label();
contactlijst = new Grid();
contactlijst.setVisible(false);
status.setText("Contact list is being retrieved");
placeWidgets();
}
public void service_eventListRetrievedFromService(LinkedHashSet<Contact> result){
System.out.println("1 service eventListRetreivedFromService "+result);
status.setText("Retrieved contactlist list");
contactlijst.setVisible(true);
this.contactlijst.clear();
this.contactlijst.resizeRows(1 + result.size());
int row = 1;
this.contactlijst.setWidget(0, 0, new Label ("Voornaam"));
this.contactlijst.setWidget(0, 1, new Label ("Achternaam"));
for(Contact contact: result) {
this.contactlijst.setWidget(row, 0, new Label (contact.getVoornaam()));
this.contactlijst.setWidget(row, 1, new Label (contact.getVoornaam()));
row++;
System.out.println("voornaam: "+contact.getVoornaam());
}
System.out.println("2 service eventListRetreivedFromService "+result);
}
public void placeWidgets() {
System.out.println("placewidget inside listcontactgui" + contactlijst);
RootPanel.get("status").add(status);
RootPanel.get("contactlijst").add(contactlijst);
}
public void service_eventListContactenFailed(Throwable caught) {
status.setText("Unable to retrieve contact list from database.");
}
}
It could be the query returns a lazy list. Which means not all values are in the list at the moment the list is send to the client. I used a trick to just call size() on the list (not sure how I got to that solution, but seems to work):
public LinkedHashSet<Contact> listContacts() {
final PersistenceManager pm = PmfSingleton.get().getPersistenceManager();
try {
final LinkedHashSet<Contact> contacts =
(LinkedHashSet<Contact>) pm.newQuery(Contact.class).execute();
contacts.size(); // this triggers to get all values.
return contacts;
} finally {
pm.close();
}
}
But I'm not sure if this is the best practice...

Fluent NHibernate Mapping test takes forever

I've recently started to learn Fluent NH, and I'm having some trouble with this test method. It takes forever to run (it's been running for over ten minutes now, and no sign of progress...).
[TestMethod]
public void Entry_IsCorrectlyMapped()
{
Action<PersistenceSpecification<Entry>> testAction = pspec => pspec
.CheckProperty(e => e.Id, "1")
.VerifyTheMappings();
TestMapping<Entry>(testAction);
}
with this helper method (slightly simplified - i have a couple of try/catch blocks too, to provide nicer error messages):
public void TestMapping<T>(Action<PersistenceSpecification<T>> testAction) where T : IEntity
{
using (var session = DependencyFactory.CreateSessionFactory(true).OpenSession())
{
testAction(new PersistenceSpecification<T>(session));
}
}
The DependencyFactory.CreateSessionFactory() method looks like this:
public static ISessionFactory CreateSessionFactory(bool buildSchema)
{
var cfg = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(Entry).Assembly));
if (buildSchema)
{
cfg = cfg.ExposeConfiguration(config => new SchemaExport(config).Create(false, true));
}
return cfg.BuildSessionFactory();
}
I've tried debugging, but I can't figure out where the bottleneck is. Why is this taking so long?
I would think it has to do with the way your trying to use the session together with the persistence spec. Make a base test class like the one below that provides you a session; if whole test takes longer than about 3 - 4 seconds max something is wrong.
Cheers,
Berryl
[TestFixture]
public class UserAutoMappingTests : InMemoryDbTestFixture
{
private const string _nickName = "berryl";
private readonly Name _name = new Name("Berryl", "Hesh");
private const string _email = "bhesh#cox.net";
protected override PersistenceModel _GetPersistenceModel() { return new UserDomainAutoMapModel().Generate(); }
[Test]
public void Persistence_CanSaveAndLoad_User()
{
new PersistenceSpecification<User>(_Session)
.CheckProperty(x => x.NickName, _nickName)
.CheckProperty(x => x.Email, _email)
.CheckProperty(x => x.Name, _name)
.VerifyTheMappings();
}
}
public abstract class InMemoryDbTestFixture
{
protected ISession _Session { get; set; }
protected SessionSource _SessionSource { get; set; }
protected Configuration _Cfg { get; set; }
protected abstract PersistenceModel _GetPersistenceModel();
protected PersistenceModel _persistenceModel;
[TestFixtureSetUp]
public void SetUpPersistenceModel()
{
_persistenceModel = _GetPersistenceModel();
}
[SetUp]
public void SetUpSession()
{
NHibInMemoryDbSession.Init(_persistenceModel); // your own session factory
_Session = NHibInMemoryDbSession.Session;
_SessionSource = NHibInMemoryDbSession.SessionSource;
_Cfg = NHibInMemoryDbSession.Cfg;
}
[TearDown]
public void TearDownSession()
{
NHibInMemoryDbSession.TerminateInMemoryDbSession();
_Session = null;
_SessionSource = null;
_Cfg = null;
}
}
public static class NHibInMemoryDbSession
{
public static ISession Session { get; private set; }
public static Configuration Cfg { get; private set; }
public static SessionSource SessionSource { get; set; }
public static void Init(PersistenceModel persistenceModel)
{
Check.RequireNotNull<PersistenceModel>(persistenceModel);
var SQLiteCfg = SQLiteConfiguration.Standard.InMemory().ShowSql();
SQLiteCfg.ProxyFactoryFactory(typeof(ProxyFactoryFactory).AssemblyQualifiedName);
var fluentCfg = Fluently.Configure().Database(SQLiteCfg).ExposeConfiguration(cfg => { Cfg = cfg; });
SessionSource = new SessionSource(fluentCfg.BuildConfiguration().Properties, persistenceModel);
Session = SessionSource.CreateSession();
SessionSource.BuildSchema(Session, true);
}
public static void TerminateInMemoryDbSession()
{
Session.Close();
Session.Dispose();
Session = null;
SessionSource = null;
Cfg = null;
Check.Ensure(Session == null);
Check.Ensure(SessionSource == null);
Check.Ensure(Cfg == null);
}
}

Resources