SharePoint Listen via WebService auslesen

Vor kurzen Stand ich vor dem Problem, das ich aus dem SharePoint Daten auslesen musste. Relativ schnell habe ich gefunden, das der SharePoint Webservices anbietet. Da es sich um den SharePoint 2007 handelt, ist die Webservice schnittstelle etwas eingeschränkt und lässt sich nur via XML/SOAP ansprechen und nicht via WCF (nach meinen erkenntnissen).

Mein erster Gedanke war eben, im VS2008 Rechtsklick, neuen Dienstverweis. Aber mit Dienstverweis habe ich leider nicht zum laufen bekommen. Deswegen habe ich dann den Webverweis genommen.

Lists _ClientLists = new Lists();
Guid _ListGuid = new Guid("Meine GUID von der Liste");

//URL zum Webservice
_ClientLists.Url = ConfigurationSettings.AppSettings["WSListURL"];// "http://server/webseite/_vti_bin/Lists.asmx";
//WindowsLogin nutzen
_ClientLists.UseDefaultCredentials = true;

XmlDocument xmlDoc = new System.Xml.XmlDocument();

XmlNode listQuery = xmlDoc.CreateNode(XmlNodeType.Element, "Query", "");
XmlNode listViewFields = xmlDoc.CreateNode(XmlNodeType.Element, "ViewFields", "");
XmlNode listQueryOptions = xmlDoc.CreateNode(XmlNodeType.Element, "QueryOptions", "");

//Optionen stehen im MSDN-Link
listQueryOptions.InnerXml = "<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>" +
	"<DateInUtc>TRUE</DateInUtc>";

//Spalten/Felder die aus der Liste geladen werden sollen
listViewFields.InnerXml = @"<FieldRef Name='ID' />
							<FieldRef Name='Name' />
							<FieldRef Name='Nutzen' />
							<FieldRef Name='Internet' />";

//Query - um die Abfrage etwas einzuschränken, zB nur freigegeben Daten auslesen, ....
listQuery.InnerXml = @"
<Query>
 <Where>
  <And>
   <Eq>
    <FieldRef Name='Internet' />
    <Value Type='Boolean'>TRUE</Value>
   </Eq>
   <Eq>
    <FieldRef Name='Nutzen' />
    <Value Type='Choice'>Ja</Value>
   </Eq>
  </And>
 </Where>
</Query>";

//Result von Webservice
XmlNode items;
try
{
	//GetListItems ist Methode, um alle Items/Zeilen aus der Liste zuholen
	//_ListGuid - ist der Listenname
	//null - ViewName
	//listQuery - von oben
	//listViewFields - von oben
	//10000 - Anzahl der Zeilen
	//listQueryOptions - von oben
	//null - WebId - ging auch ohne!
	items = _ClientLists.GetListItems(_ListGuid.ToString(), null, listQuery, listViewFields, "10000", listQueryOptions, null);

	//Da die eigentlichen Zeilen erst im ersten ChildNote sind
	var all = items.ChildNodes.Item(1);

	//XMLDocument in Linq-to-XML parsen
	XDocument linqDoc = XDocument.Parse(all.OuterXml);

	//da die Query von oben nicht nicht klappt, hier noch mal.
	var all2 = linqDoc.Root.Elements().Where(e => e.Attribute("Nutzen").Value == "Ja" && e.Attribute("ows_Internet").Value == "1");

	//Zeilen durch gehen und verarbeiten
	var enu = all2.GetEnumerator();
	while (enu.MoveNext())
	{
		//Daten verarbeiten
		//Daten auslesen
		var id = enu.Current.Attribute("ows_ID").Value;
	}
}
catch (System.Web.Services.Protocols.SoapException ex)
{
}

Jetzt kann man in der While-Schleife beliebt die Daten verarbeiten, in eine DB schreiben, XML-Files oder einfach nur anzeigen.

Viel Spaß damit, wenn noch jemand Tipps hat, dann bitte als Comment Posten – Vielen Dank

Links:

Linq-To-SQL generisches hinzufügen/bearbeiten/löschen von Daten

Endlich schaffe ich es mal wieder zubloggen.

Ich möchte euch zeigen, wie ich seit kurzen meine Daten mit der Datenbank über Linq-To-SQL abgleiche. Dabei gehe ich nur auf das hinzufügen von neuen Daten/Objekten, welche mit der Methode “InsertOnSubmit()” durch geführt wird.

Die Idee kam, da ich in meinem Code/Repository immer wieder DataContext.Users.InsertOnSummit(newUser) verwende. Jedoch muss/sollte man immer mit einem try-catch-Block die SQLException abfangen, dieser Code fand sich dann an vielen Stellen in meinem Repository. Dann habe ich gefunden, dass man sich die Tabelle aus dem DataContext auch über GetTable(object.GetType()) holen kann und dort dann ein InsertOnSubmit(object) nutzen kann.

//_DC = DataContext
//_Log = Log4Net
//ErrorMail ist eine Methode von mir, die mir bei Exceptions das per Mail mitteilt.

internal Boolean AddEntity(object e)
{
 Boolean back = false;
 if (e != null)
  {
   _DC.GetTable(e.GetType()).InsertOnSubmit(e);
   try
   {
    _DC.SubmitChanges();
    back = true;
   }
   catch (SqlException ex)
  {
    _DC.GetTable(e.GetType()).DeleteOnSubmit(e);
    ErrorMail(ex);
   _Log.Warn("AddEntity-Exception");
   _Log.Debug(ex.Message);
  }
 }
 return back;
}

Diese Methode nutze ich nun in allen Repository-Klasse, wo ein Insert/Add erfolgt “AddEntity(newUser)” und da muss ich mich nun nur noch in der einen Methode um das Fehlerfangen, Loggen und ErrorMails kümmern.

Unbedingt sollte man “_DC.GetTable(e.GetType()).DeleteOnSubmit(e);” im catch-Block ausführen! Denn wenn der Submit fehlt schlägt, befindet sich das Objekt noch in der “Warteschlage” zum ausführen, deswegen wird es mit “DeleteOnSubmit(object)” wieder aus der “Warteschlage” genommen und somit nicht zu weiteren Fehlern führt.

Wenn man diese Methode für das Update verwendet, dann sollte man drauf achten, das man einen Refrech der alten Daten durchführt, damit man keine inkonsistenz Daten hat.

ASP.NET MVC 2 – Unit Tests für / mit Data Annotations

Erst einmal schreibe ich, wo ich alles Tipps/Ideen bekommen habe
http://blog.thomasbandt.de/39/2318/de/blog/aspnet-mvc-2-unit-tests-fuer-data-annotations.html
http://der-albert.com/archive/2010/05/14/input-validierung-im-unit-test-mit-asp-net-mvc-2.aspx

Beim Thomas Brandt, habe ich das meinste her.
Mein Problem war, das ich Validierung von ASP.NET MVC 2 genutzt habe und dann meine Tests nicht mehr geklappt haben. Ich musste feststellen, das die die Validierung im Model nicht funktioniert bei UnitTests

//Model
public class HomeKontakt
{
[RegularExpression(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "MailInvalid")]
[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "MailRequired")]
public String Mail { get; set; }

[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "NameRequired")]
public String Name { get; set; }

[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "SubjectRequired")]
public String Betreff { get; set; }

[Required(ErrorMessageResourceType = typeof(ModelValidation), ErrorMessageResourceName = "TextRequired")]
public String Text { get; set; }
}

Als ich es mitbekommen habe, was mit DataAnnotations alles so tolles geht, war ich sehr schnell begeistert und hatte dann auch sehr schnell das in meinem Projekt umgesetzt.

//Controller
public ActionResult Kontakt(HomeKontakt model)
{
if (ModelState.IsValid)
{
//Mail verschicken
}
return View("Kontakt", model);
}

Die Erweiterungs-Methode “ContainsField” stammt von Thomas Brandt (siehe oben)

//Test
[TestMethod]
public void KontaktSendFehlerMailAdresse()
{
// Arrange
HomeController controller = new HomeController(_Factory, _FS);

// Act
HomeKontakt model = new HomeKontakt()
{
Mail = "test@bla@test.home.de",
// Text = "Ich werde nur getestet",
Betreff = "KontaktSendTest",
Name = "KontaktSendTest"
};

// Assert
var validationResults = new ValidationResults();
Validator.TryValidateObject(model, new ValidationContext(model, null, null), validationResults, true);

Assert.IsTrue(validationResults.ContainsField<HomeKontakt>(m => m.Mail));
Assert.IsTrue(validationResults.ContainsField<HomeKontakt>(m => m.Text));
Assert.AreEqual(validationResults[0].ErrorMessage, Web.Languages.ModelValidation.MailInvalid);
Assert.AreEqual(validationResults[1].ErrorMessage, Web.Languages.ModelValidation.TextRequired);
}

Zeile 21 / 22 (Assert.IsTrue(validationResults.ContainsField(m => m.Mail));) prüft ob in validationResults das Feld “Mail” vorhanden ist, wenn ja, dann ist die Validierung bei dem Feld fehlgeschlagen.
Mit “Assert.AreEqual(validationResults[0].ErrorMessage, Web.Languages.ModelValidation.MailInvalid);” prüfe ich noch zusätzlich ob der Fehldertext, auch der ist, den ich ausgeben möchte. Ich habe die ganzen Texte in einer Resource-Datei ausgelagert.

Leider funktioniert meine “alte Test-Methodik” nicht mehr.

//Alter Test
public void KontaktSendFehlerMailAdresse()
{
// Arrange
HomeController controller = new HomeController(_Factory, _FS);

// Act
HomeKontakt model = new HomeKontakt()
{
Mail = "baum@bla@test.local.de",
// Text = "Ich werde nur getestet",
Betreff = "KontaktSendTest",
Name = "KontaktSendTest"
};
//ERROR
ViewResult result = controller.Kontakt(model) as ViewResult;

// Assert
Assert.IsNotNull(result);

Assert.AreEqual("Kontakt", result.ViewName);

ViewDataDictionary viewData = result.ViewData;
Assert.AreEqual(ModelValidation.MailInvalid, result.ViewData.ModelState["mail"].Errors[0].ErrorMessage, "Fehler in Mail-Adresse");
Assert.IsFalse(viewData.ModelState.IsValid, "Fehler in Mail-Adresse");
}

Bei dem Aufruf von controller.Kontakt(model) bekomme ich dann immer eine FormatException, weil die Mail-Adresse ja nicht dem Format einer E-Mail-Adresse entspricht. In diesen Tests konnte ich wenigstens nich zusätzlich testen, ob ich den richtigen View zurück bekomme. Aber ich werde da jetzt wahrscheinlich nur Tests mit “gültigen Eingaben machen können” und bei “ungültigen Eingaben” muss ich extra Tests machen, die mir nur meine ModelValidation Testen.

Oder habe ich da einen Denkfehler?

Word Dokument mit Daten füllen aus .NET – Kopfzeile

Ich hatte schon geschrieben wie ich mit MergeField WordDokumente aus .NET mit Daten gefüllt habe. Ich musste nur leider selbst feststellen, das diese Methode nicht für die Kopf bzw. Fußzeile funktioniert

]
//HeaderFelder holen
 var header = doc.Sections[1].Headers[Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range.Fields.GetEnumerator();
while (header.MoveNext())
 {
  Word.Field current = (Word.Field)header.Current;
  String feldname = current.Code.Text;
  feldname = feldname.Substring(13, feldname.Length - 30);

  current.Select();

  if (feldname.Equals("Datum"))
  {
   _WordApp.Selection.TypeText(DateTime.Today.ToShortDateString());
  }
 }

Innerhalb der Kopfzeile müssen genauso MergeFields sein!

Link:

http://social.msdn.microsoft.com/forums/en-US/vsto/thread/1bf05ac8-6fce-4c9d-bc23-9f887fd9d5a7/

Word Dokumente mit Datenfüllen aus .NET

Ich weiß das es wie so oft, viele Wege gibt, aber leider konnte ich derzeit nur einen richtig verstehen und deswegen anwenden.

Mein Ziel war es, ein Word-Dokument/Vorlage mit Daten aus der Datenbank zufüllen.

Durch das suchen nach “Platzhaltern” für Word und dann das er setzen der Felder mit richtigen Daten, bin ich dann auf “MergeFields” gestoßen. Diese werden in Word über “Menü—>Einfügen—>Felder” eingefügt und mit einem Namen versehen.

im .NET ist es dann relativ einfach die Felder zu ersetzen.

Object _Doc = @"H:\MeineVorlage.doc";

Word.ApplicationClass _WordApp = new Word.ApplicationClass();

object _False = false;
object _True = true;
object _NULL = null;
object _Missing = Type.Missing;

Word.Document Doc = _WordApp.Documents.Open(ref _SpecEN, ref
_Missing, ref _Missing, ref _Missing, ref _Missing, ref _Missing,
ref _Missing, ref _Missing, ref _Missing, ref _Missing, ref
_Missing, ref _Missing, ref _Missing, ref _Missing, ref _Missing,
ref _Missing);

var felder = Doc.Fields.GetEnumerator();

while (felder.MoveNext())
{
Word.Field current = (Word.Field)felder.Current;
String feldname = current.Code.Text;
feldname = feldname.Substring(13, feldname.Length - 30);
//ist jetzt nur noch der Name, welcher in Word eingeben wurde für das MergeField

//damit man den text verändern kann
current.Select();

//Prüfen ob das Feld "Land" entspricht, wenn ja,
//dann Text verändern und durch das verändern des textes, wird auch das Feld gelöscht/ersetzt
if (feldname.Equals("Land"))
{
_WordApp.Selection.TypeText("Deutschland");
}

_WordApp.Visible = true;
Doc.Activate();
}

Links:

C-Sharpcorner – WordAutomation

VS Referenzen auf anderen Computer fehlen

Wenn man in einem VisualStudio Projekt externe Libs benutzt und diese einbindet. MUSS der Ordner mit den Libs auch in der Projektmappe von den VisualStudio Projekt mit drin sein und erst danach die Libs zum Projekt hinzufügen.

Wenn das Projekt den Lib-Ordner nicht kennt, macht VS absolute Pfade und somit ist es nicht mehr SVN/TFS tauglich.

Nach dem aber der Lib-Ordner auch dem VS-Projekt bekannt ist, werden relative Pfade zu den Libs und schon funktioniert es wieder im Team.

VS_Libs1

Der Screen zeigt, zwar auch den absoluten Pfad an, jedoch in der projektdatei an sich, ist er als relativer angegeben.

In der Projektdatei (XML-Ansicht) steht dann folgendes und wie gut zuerkennen ist, ist Convert.dll als relative Lib eingebunden und die Name.dll wurde absolut eingebunden

<ItemGroup>
  <Reference Include="Convert">
    <HintPath>Libs\Convert.dll</HintPath>
  </Reference>
  <Reference Include="Name">
    <HintPath>c:\Test\bin\Debug\Name.dll</HintPath>
  </Reference>
</ItemGroup>