Laden...

sqlite-net und Klassendesign der Entitäten

Erstellt von BlackMatrix vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.327 Views
B
BlackMatrix Themenstarter:in
218 Beiträge seit 2012
vor 9 Jahren
sqlite-net und Klassendesign der Entitäten

Hallo,

ich stehe eigentlich vor einem ganz simplen Problem, dennoch scheint es mir irgendwie als unlösbar.

Für meine Persistenz nutze ich die Bilbliothek sqlite-net. Mein CLR-Objekt ist ein IPEndPoint mit zusätzlichen Eigenschaften ala "IsAvailable", "WasAvailableAt" etc. Dieses Objekt leidet von IPEndPoint ab und delegiert die parametrisierten Konstruktoren einfach nur an die Basisklasse weiter. Einen parameterlosen Konstruktor gibt es nicht.

Nun sollen die meisten dieser Eigenschaften serialisiert werden. IPEndPoint lässt sich nicht direkt serialisieren, da die Eigenschaft Address vom IPAddress ist und diese sich über sqlite-net nicht serialisieren lässt.

Also habe ich mir eine IPEndPointEntity Klasse erstellt, die das interface IPEndPointEntity implementiert, welche die Entität und das CLR-Objekt implementieren. Und nun mache ich für meine Begriffe unschöne Dinge, aber seht selbst:

	public class IPEndPointDBContext
	{
		private readonly SQLiteConnection _connection;

		public IPEndPointDBContext(string dataBasePath)
		{
			_connection = new SQLiteConnection(dataBasePath);
		}

		public IEnumerable<IPEndPoint> GetIPEndPoints()
		{
			return _connection.Table<IPEndPointEntity>().AsEnumerable().Select(AsIPEndPoint);
		}

		#region Type conversion

		private IPEndPoint AsIPEndPoint(IPEndPointEntity endPointEntity)
		{
			var endPoint = new IPEndPoint(IPAddress.Parse(endPointEntity.Address), endPointEntity.Port);
			AssignProperties(endPointEntity, endPoint );
			return endPoint ;
		}

		private IPEndPoint AsIPEndPointEntity(IPEndPoint endPoint)
		{
			var endPointEntity= new IPEndPointEntity
			{
				Address = endPoint.Address.ToString(),
				Port = endPoint.Port,
			};

			AssignProperties(endpoint, endPointEntity);

			return endPointEntity;
		}

		private void AssignProperties(IIPEndPoint from, IIPEndPoint to)
		{
			to.IsAvailable = from.IsAvailable;
			to.WasAvailableAt = from.WasAvailableAt;
			// ...
		}
		#endregion
	}

Nun, das ganze funktioniert zwar, aber als ich dann GetIPEndPoints auf async/await umstellen wollte und den nativen Support von sqlite-net ausnutzen will war das ganze "Design" dahin. Eigentlich will ich doch nur, dass die IPAddress als string in der Datenbank abgespeichert werden 🤔

16.834 Beiträge seit 2008
vor 9 Jahren

Speicher die IP ab und dazu den Typ (IPv4, IPv6) - das haste in Deiner DB Schicht, mehr nicht.
In Deinem BL Layer haste dann das Mapping von IP auf IPAddress und kannst da dann auch wieder mit den Informationen arbeiten.
Aber in Deiner Datenbankschicht hat IPAddress oder IPEndpoint nichts zu suchen.

Wenn die Schicht sauber ist, dann kannst Du auch alles mit wenigen Code-Zeilen asynchron umsetzen - ohne, dass das Design geändert werden muss.

PS: Hattest Du nicht das gleiche Problem vor ein paar Tagen? 🤔

B
BlackMatrix Themenstarter:in
218 Beiträge seit 2012
vor 9 Jahren

Das ist schon richtig, aber ob nun die #region Type conversion in einer eigenen Klasse/Layer unterbringe oder nicht ist erst einmal irrelevant.

Ich muss in jedem Fall IPEndPointEntity zu IPEndPoint wandeln und IPEndPoint zu IPEndPointEntity. Dafür muss ich die ganzen Properties des einen Objekts den Properties des andere zuweisen (AssignProperties). Oder wie soll sonst das "Mapping" funktionieren?

interface IIPEndPoint
	{
		DateTime LastAccess{ get; set; }
		bool IsOnline { get; set; }
		EndPointType Type { get; set; }
	}
namespace Data.DataAccess
{
	public class IPEndPointEntity:IIPEndPoint
	{
		[PrimaryKey, AutoIncrement]
		public int Id { get; set; }
		public string Address { get; set; }
		public int Port { get; set; }
		public DateTime LastAccess{ get; set; }
		public public bool IsOnline { get; set; }
		public EndPointType Type { get; set; }
	}
}

public class ExtendedIPEndPoint: IPEndPoint,IIPEndPoint
	{
		public ExtendedIPEndPoint(long address, int port) : base(address, port)
		{
		}

		public ExtendedIPEndPoint(IPAddress address, int port) : base(address, port)
		{
		}
		public DateTime LastAccess{ get; set; }
		public public bool IsOnline { get; set; }
		public EndPointType Type { get; set; }
	}

Mir scheint das irgendwie nicht richtig. Umständlich und hässlich. Hinzu kommt, dass wenn ich auf Async umsteige nicht wüsste, wie ich beispielsweise aus einem AsyncTableQuery<IPEndPointEntity> ein Task<IEnumerable<IPEndPoint>> oder vergleichbares bekomme ohne im Speicher eine Kopie beispielsweise über AsyncTableQuery.ToListAsync() zu erstellen.

Der theoretische Teil ist mir schon einigermaßen klar, aber das zu coden scheint mir nicht trivial, obwohl man meinen sollte, dass man sowas dutzende Mal im Netz finden müsste.

16.834 Beiträge seit 2008
vor 9 Jahren

Ich weiß nicht, obs an Deinen sechs Edits liegt, aber Coffeebean und ich verstehen beide nicht, was Du geschrieben hast 😉

Ist Deine Frage, wie Du aus Deiner Entität ein Address-Objekt bekommst und umgekehrt, oder was möchtest Du wissen? 🤔
Mir ist auch nicht ganz klar, wo pltzöich das Interface her kommt, und was Du damit machen möchtest.

DAL:

public class IPEndPointRepository : MyRepBase<IPEndPoint>, IIPEndPointRepository
{
}

BL

public class IPEndPointService : MyServiceBase
{
	private IIPEndPointRepository _ipEndPointRepository;
   
	public IPEndPointService( IIPEndPointRepository ipEndPointRepository )
	{
		_ipEndPointRepository = ipEndPointRepository;
	}
	
	public Task<IIPEndPoint> GetByIdAsync<T>( int id ) where T : IIPEndPoint 
	{
		return _ipEndPointRepository.GetByIdAsync<T>( id );
	}
}

(Hier im Editor runter getippt).

Ich fühl mich ein wenig wie bei Jeopardy, aber ist das die Antwort auf Deine Frage? 😉

B
BlackMatrix Themenstarter:in
218 Beiträge seit 2012
vor 9 Jahren

Genau, es geht um die Konvertierung von IPEndPointEntity zum CLR-IPEndPoint und umgekehrt. IIPEndPoint habe ich nur aus diesem Grund eingeführt, weil ExtendedIPEndPoint schon von IPEndPoint ableitet.

Und das geht doch meiner Meinung nach nur, indem ich die einzelnen Properties zuweise oder wie bekomme ich sonst das Mapping hin?


IPEndPointEntity iPEndPointEntity = // ...

var endPoint = new ExtendedIPEndPoint(iPEndPointEntity.Address,iPEndPointEntity.Port);
endPoint .LastChecked=extendedIPEndPoint.LastChecked;
// ...

2.207 Beiträge seit 2011
vor 9 Jahren

Hallo BlackMatrix,

Properties zuweisen (Factory, Konstruktor, Methode je nach Fall und Komplexität). Oder du benutzt Automapper: https://automapper.codeplex.com/

Gruss

Coffeebean

B
BlackMatrix Themenstarter:in
218 Beiträge seit 2012
vor 9 Jahren

Ich glaube, das bringt mir relativ wenig, da der Code wie oben bereits erwähnt funktioniert und der Automapper mir das Ganze wahrscheinlich nur vereinfacht. Wenn ich aber das Ganze auf async/await umstelle, dann funktioniert, wie angedeutet, überhaupt nichts mehr, weil die komplette async-Funktionalität auf den primitiven Typen aufgebaut ist.

Es muss doch eine ganz einfache Möglichkeit geben, meinen ExtendedIPEndPoint als ExtendedIPEndPointEntity abzubilden. Die sind doch eigentlich das Gleiche. Ich mache diesen ganzen Hickhack doch nur, weil die IPAddress des IPEndPoint sich nicht serialisieren lässt und IPEndPoint keinen parameterlosen Konstruktor implementiert.

Mein Verständnis:
Die IPAddress in der BL soll beim Speichern eines ExtendedIPEndPoint im DAL als IPAddress.ToString() gemappt werden. Beim Laden muss aus IPAddressAsString und Port ein neues ExtendedIPEndPoint-Objekt erzeugt werden und die Properties übernommen werden.